Back to skill

Security audit

Youtube Clipper Ko

Security checks for vulnerabilities and agentic risk

Overview

This is a coherent video-clipping skill, but it needs review because its privacy and install disclosures understate cloud processing and mutable dependency risk.

Review before installing. Use this only for media you are comfortable sending to OpenAI for transcription and Anthropic for transcript analysis, and prefer an isolated environment with pinned dependency versions. Be aware that the current artifacts do not implement the advertised silence/breath removal and may leave source media, audio, transcripts, metadata, and generated clips on disk.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

other

Warning
Location
README.md:31
Finding
Misleading Local-Processing Claim Conceals Cloud Disclosure<![CDATA[ ## Vulnerability Details **File Location**: `README.md:31`; `scripts/clip.py:181-188`; `scripts/clip.py:243-249`; `scripts/clip.py:282-286`; `scripts/clip.py:315-327` **Vulnerability Type**: Misleading Privacy Disclosure **Risk Level**: Medium The README states: ```markdown - **로컬 실행** — 영상 외부 서버에 올라가지 않음 ``` However, the implementation uploads extracted audio to OpenAI Whisper: ```python with open(audio_path, "rb") as f: response = client.audio.transcriptions.create( model="whisper-1", file=f, language=lang, response_format="verbose_json", timestamp_granularities=["segment", "word"], ) ``` Chunked files are also uploaded: ```python with open(chunk_path, "rb") as f: resp = client.audio.transcriptions.create( model="whisper-1", file=f, language=lang, response_format="verbose_json", timestamp_granularities=["segment"], ) ``` The complete transcript is assembled for use in prompts: ```python segments_text = "\n".join( f"[{fmt_time(s['start'])} → {fmt_time(s['end'])}] {s['text']}" for s in transcript["segments"] ) total_duration = transcript.get("duration", 0) ``` That prompt content is then sent to Anthropic: ```python response = client.messages.create( model="claude-haiku-4-5", max_tokens=max_tokens, messages=messages, ) ``` ### Technical Analysis The claim that the video does not go to an external server is technically narrow and materially misleading. Although the video container itself is not uploaded, its extracted audio contains substantially the same spoken information and is transmitted to OpenAI. The resulting full transcript is subsequently transmitted to Anthropic through multiple model requests. Users may interpret the documented “local execution” claim as meaning that private media content remains on their machine. The implementation does not provide a warning or explicit consent prompt before transmitting that cont ...[truncated 1602 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace the local-processing claim with a precise data-flow disclosure stating that: - Video processing and clipping occur locally. - Extracted audio is uploaded to OpenAI Whisper. - The resulting transcript is sent to Anthropic Claude. 2. Display an explicit confirmation prompt before the first cloud request, particularly for local files. 3. Add a noninteractive consent flag, such as `--allow-cloud-processing`, and fail closed when it is absent. 4. Document the external providers, transmitted fields, expected retention behavior, applicable privacy terms, and account-level data controls. 5. Offer local alternatives, such as a locally hosted Whisper implementation and a local language model. 6. Delete `audio.mp3` after successful transcription unless the user explicitly requests retention. 7. Add a cleanup path using `try/finally` so temporary audio and chunk files are removed after failures. 8. Provide a configurable data-retention option for transcripts and generated metadata. ]]>

T08 · Insecure Dependencies

Warning
Location
README.md:38
Finding
Unpinned Third-Party Installation Commands Create Supply-Chain Exposure<![CDATA[ ## Vulnerability Details **File Location**: `README.md:38-45`; `SKILL.md:50-52` **Vulnerability Type**: Unpinned and Automatically Confirmed Dependency Installation **Risk Level**: Medium The README recommends globally invoking an unpinned npm-distributed tool with automatic confirmation: ```bash npx skills add thedalbee/youtube-clipper-ko -g -y ``` It also installs mutable latest versions of Python dependencies: ```bash git clone https://github.com/thedalbee/youtube-clipper-ko pip install yt-dlp openai anthropic ``` The Skill instructions repeat the unpinned installation guidance: ```bash pip install yt-dlp ``` ```bash pip install openai anthropic ``` ### Technical Analysis These commands do not pin versions, verify package hashes, use a lockfile, or constrain the package index. As a result, identical installation commands can resolve to different code over time. The `npx` command may download and execute a mutable version of the `skills` package. The `-y` option suppresses interactive confirmation, while `-g` requests a global installation scope. The Python packages are likewise resolved from the currently configured package index without integrity pinning. No malicious dependency was identified in the audited project. The vulnerability is the unsafe dependency acquisition pattern, which creates an exploitable supply-chain path if a package registry, publisher account, release, dependency, or configured package source is compromised. ### Attack Path 1. An attacker compromises an upstream publisher account, package release, transitive dependency, or package source used by npm or pip. 2. The attacker publishes a malicious version that satisfies the unpinned package name. 3. A user follows the documented `npx` or `pip install` command. 4. The package manager resolves the attacker-controlled release because no exact version or verified hash is required. 5. Installation-time or subsequently imported code executes with the privileges of the in ...[truncated 969 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin exact versions for all direct dependencies. 2. Provide a requirements lockfile generated with hashes, and install with hash enforcement, for example: ```bash pip install --require-hashes -r requirements.lock ``` 3. Pin the `npx` package to a reviewed version rather than allowing resolution to the latest release. 4. Remove automatic confirmation flags from security-sensitive installation instructions. 5. Prefer project-local installation over global installation. 6. Use an isolated Python virtual environment and document its creation. 7. Record and review transitive dependencies through a reproducible lockfile. 8. Configure trusted package indexes explicitly and reject unexpected alternate indexes. 9. Add automated dependency vulnerability, provenance, and integrity checks to the release process. 10. Publish checksums or signed release artifacts so users can verify the Skill package before installation. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (22)

Tp2

High
Category
MCP Tool Poisoning
Confidence
85% confidence
Finding
Mixing characters from multiple Unicode scripts in a single identifier is a common technique to create visually ambiguous tool names.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
전반적인 주요 목적은 선언과 거의 일치한다. 실제 코드도 한국어 YouTube/로컬 영상을 처리하고, Whisper 기반 자막 추출, Claude 기반 바이럴 구간 선정, ffmpeg 클립 생성, 3색 자막, 점수/이유 메타데이터 저장을 수행한다. 그러나 선언에서 명시한 '무음/숨소리 구간 제거'는 코드에 없다. extract_clip의 주석에는 '무음 압축'이 언급되지만 실제 ffmpeg 호출은 원본 오디오를 그대로 AAC로 재인코딩할 뿐 무음 탐지, 음성 활동 검출, 숨소리 제거, 자동 컷 편집 같은 처리가 전혀 없다. 이는 사용자 기대에 영향을 주는 실질적 기능 불일치다. 반면 YouTube URL/로컬 파일 지원, Clawitzer 연결 가능성(경로 추가 및 3색 규칙 재사용 성격), 자막 스타일링 등은 대체로 설명과 부합한다. 따라서 부분적이지만 중요한 설명-행동 불일치가 있다고 판단한다.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
parser.add_argument("--n-clips", type=int, default=10, help="추출할 클립 수 [기본: 10]")
    parser.add_argument("--lang", default="ko", help="자막 언어 [기본: ko]")
    parser.add_argument("--dry", action="store_true", help="자막 추출만 (클립 미생성)")
    parser.add_argument("--no-check", action="store_true", help="환경 검사 스킵")
    args = parser.parse_args()

    if not args.url and not args.file:
Confidence
85% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
The description explicitly positions the skill as '한국어 YouTube 영상' and the README repeatedly frames outputs such as Korean subtitles as a fixed behavior. There is no user opt-in, language choice, or explanation that the locale restriction is required for a region-specific compliance or similar justified constraint.

Intent-Code Divergence

Medium
Confidence
84% confidence
Finding
L030 explicitly says '원본 음성 유지 ... 무음 압축 없음', indicating silence is not compressed or removed. This conflicts with the stated skill description that includes '무음/숨소리 구간 제거'. The documentation presents contradictory intent about whether the tool alters audio continuity.

Description-Behavior Mismatch

Medium
Confidence
97% confidence
Finding
The README claims the tool runs locally and that videos are not uploaded to external servers, but the documented use of OpenAI Whisper and Anthropic Claude APIs means audio and/or transcript data is sent to third parties. This is dangerous because users may process sensitive or copyrighted media under a false privacy assumption, leading to unintended data disclosure and consent/compliance issues.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill explicitly instructs the agent to use shell commands, inspect environment state, install packages, download media, and write outputs, but it does not declare any tool scope or permission boundaries. In practice this increases the blast radius of prompt-triggered execution because the agent may invoke powerful capabilities without a least-privilege contract or user-visible guardrails.

Vague Triggers

Medium
Confidence
94% confidence
Finding
Broad trigger phrases increase the chance that the skill activates in loosely related contexts and starts workflows involving downloads, shell execution, file writes, and API transmission without sufficiently specific user intent. Because this skill handles external URLs/files and invokes powerful tooling, ambiguous activation is more dangerous than for a read-only or local-only skill.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill downloads media and sends audio/transcript content to external providers, but the description does not clearly warn users that their content will be transmitted off-platform and stored in output artifacts. This creates privacy and compliance risk, especially for local files or non-public videos that may contain sensitive or copyrighted material.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
errors = []

    # yt-dlp
    r = subprocess.run(["yt-dlp", "--version"], capture_output=True)
    if r.returncode != 0:
        errors.append("yt-dlp 없음: pip install yt-dlp")
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
errors.append("yt-dlp 없음: pip install yt-dlp")

    # ffmpeg
    r = subprocess.run(["ffmpeg", "-version"], capture_output=True)
    if r.returncode != 0:
        errors.append("ffmpeg 없음: apt install ffmpeg")
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
errors.append("ffmpeg 없음: apt install ffmpeg")

    # libass (자막 burn-in에 필요)
    r = subprocess.run(["ffmpeg", "-filters"], capture_output=True, text=True)
    if "subtitles" not in r.stderr:
        print("  ⚠ ffmpeg libass 없음 — 자막 burn-in 불가 (SRT로 대체)")
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
if os.path.exists(p):
            return p
    # 시스템 폰트 검색
    r = subprocess.run(["fc-list", ":lang=ko"], capture_output=True, text=True)
    for line in r.stdout.splitlines():
        if ".ttf" in line or ".ttc" in line:
            return line.split(":")[0].strip()
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
e = end or "99:59:59"
        cmd += ["--download-sections", f"*{s}-{e}"]

    r = subprocess.run(cmd, capture_output=True, text=True)
    if r.returncode != 0:
        print("  ✗ 다운로드 실패:\n", r.stderr[-500:])
        raise RuntimeError("yt-dlp 다운로드 실패")
Confidence
74% confidence
Finding
The script passes a user-supplied URL directly to `yt-dlp`, which then performs network retrieval against attacker-controlled destinations. While this is not shell injection, it can enable SSRF-style access to internal resources or unexpected local/protocol handling depending on `yt-dlp` support and runtime network reachability.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def get_video_duration(path: str) -> float:
    cmd = ["ffprobe", "-v", "quiet", "-print_format", "json", "-show_format", path]
    r = subprocess.run(cmd, capture_output=True, text=True)
    data = json.loads(r.stdout)
    return float(data["format"]["duration"])
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# 오디오 추출 (MP3로 변환, Whisper 25MB 제한 대응)
    audio_path = str(out_dir / "audio.mp3")
    subprocess.run([
        "ffmpeg", "-y", "-i", video_path,
        "-q:a", "0", "-map", "a",
        "-b:a", "64k",  # 낮은 비트레이트로 용량 절약
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
while offset < duration:
        chunk_path = str(out_dir / f"audio_chunk_{int(offset)}.mp3")
        subprocess.run([
            "ffmpeg", "-y", "-i", audio_path,
            "-ss", str(offset), "-t", str(chunk_sec),
            "-b:a", "64k", chunk_path,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
The prompts explicitly instruct the model to act as a Korean short-form content editor, score clips by fit for Korean viewers, and generate titles in Korean. This imposes a specific language/locale policy in the skill's behavior rather than offering the user a choice, which matches the policy-violation criterion for forced language/locale.

Description-Behavior Mismatch

Medium
Confidence
97% confidence
Finding
The manifest explicitly claims '무음/숨소리 구간 제거', but the actual extraction path only trims the selected time range, rescales video, burns subtitles, and re-encodes audio. The inline comment at L635 even states the original audio is kept and that no silence compression is applied, so this advertised behavior is not implemented.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
)

    # 영상 + 오디오 한번에 (원본 음성 유지, 무음 압축 없음)
    subprocess.run([
        "ffmpeg", "-y",
        "-ss", str(start_sec), "-t", str(duration),
        "-i", source_video,
Confidence
83% confidence
Finding
The ffmpeg filter string embeds `ass_path` directly into `-vf` as `ass={ass_path}` without escaping for ffmpeg filter syntax. Because `ass_path` is filesystem-derived and temporary paths can legally contain special characters such as `:` or `'`, this can break parsing and, in less controlled environments, create filter-argument injection behavior or unreliable execution.

Intent-Code Divergence

Low
Confidence
94% confidence
Finding
The section header/comment at L628 describes the ffmpeg step as including '무음 압축', but the immediately following comment at L635 says the original audio is preserved with no silence compression, and the ffmpeg arguments contain no silence-removal or compression filters. This is active documentation inconsistency about what the code does.

Static analysis

No suspicious patterns detected.