Back to skill

Security audit

Clips Machine

Security checks for vulnerabilities and agentic risk

Overview

This video-clipping skill is purpose-aligned, but it needs Review because its output folder argument can write files outside the documented output area and its docs understate some runtime authority.

Review this before installing if you process private media or run agent-provided commands. Use only videos you are authorized to process, keep outputs in a trusted directory, avoid untrusted --output values, and install ffmpeg, yt-dlp, and whisper-cpp from trusted pinned sources where possible.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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
Findings (2)

T09 · Insecure Skill Coding Practices

Warning
Location
clips_machine.py:396
Finding
User-Controlled Output Path Can Escape the Intended Output Directory<![CDATA[ ## Vulnerability Details **File Location**: `clips_machine.py:396-398` **Vulnerability Type**: Arbitrary directory creation and file overwrite through path traversal **Risk Level**: Medium ### Vulnerable Code ```python output_dir = OUTPUT_DIR / output_name output_dir.mkdir(parents=True, exist_ok=True) results = {"directory": str(output_dir), "clips": []} ``` The value reaches this operation directly from the command-line argument: ```python parser.add_argument("--output", help="Custom output folder name") ``` ```python process_video( source=args.source, num_clips=args.clips, style=args.style, no_captions=args.no_captions, start_time=parse_timestamp(args.start) if args.start else None, end_time=parse_timestamp(args.end) if args.end else None, min_score=args.min_score, output_name=args.output, ) ``` The resulting directory is subsequently used for fixed-name output files, including: ```python transcript_path = output_dir / "transcript.json" with open(transcript_path, "w") as f: json.dump(transcript, f, indent=2) ``` ```python moments_path = output_dir / "viral_moments.json" with open(moments_path, "w") as f: json.dump(moments, f, indent=2) ``` ```python with open(output_dir / "summary.md", "w") as f: f.write(summary) ``` ### Technical Analysis The `--output` value is described as a folder name, but it is not restricted to a single path component. Python's `pathlib` permits both traversal components and absolute paths: - `OUTPUT_DIR / "../../target"` resolves outside `OUTPUT_DIR`. - If `output_name` is absolute, it replaces the preceding `OUTPUT_DIR` component entirely. The program creates the selected directory recursively and writes multiple predictable filenames into it. Video-processing commands also use FFmpeg's `-y` option, which authorizes replacement of existing output files. No canonicalization, containment check, or refusal to overwrite existing destinations is performed. This is not co ...[truncated 1435 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Treat `--output` strictly as a directory name rather than an arbitrary path: 1. Reject absolute paths. 2. Reject `.` and `..` components and all path separators. 3. Resolve the candidate destination and verify that it remains beneath the resolved `OUTPUT_DIR`. 4. Consider refusing existing output directories to prevent accidental replacement of prior artifacts. 5. Avoid unconditional overwrite behavior where it is unnecessary. Example hardening: ```python def safe_output_directory(output_name: str) -> Path: base = OUTPUT_DIR.resolve() candidate_name = Path(output_name) if ( candidate_name.is_absolute() or len(candidate_name.parts) != 1 or output_name in ("", ".", "..") ): raise ValueError("Output must be a single directory name") candidate = (base / candidate_name).resolve() if not candidate.is_relative_to(base): raise ValueError("Output directory escapes the configured output root") candidate.mkdir(parents=False, exist_ok=False) return candidate ``` Replace the vulnerable construction with: ```python output_dir = safe_output_directory(output_name) ``` If compatibility with older Python versions is required, perform containment validation using `os.path.commonpath()` instead of `Path.is_relative_to()`. ]]>

T08 · Insecure Dependencies

Note
Location
SKILL.md:118
Finding
Unpinned Third-Party Package Installation Instruction<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:118` **Vulnerability Type**: Mutable and unverified dependency installation **Risk Level**: Low ### Vulnerable Code ```bash pip install yt-dlp ``` ### Technical Analysis The setup documentation instructs users to install `yt-dlp` without specifying a reviewed version or cryptographic hash. Consequently, the installed artifact depends on mutable package-index state at installation time rather than the code reviewed in this project. The package name is consistent with the tool declared by the Skill, and the audit found no evidence of typosquatting or a deliberately malicious dependency. The risk arises from the absence of version and integrity controls: a compromised package release, registry, dependency chain, or package-index configuration could cause users to install code that was not part of this audit. ### Attack Path 1. A user follows the Linux setup instructions in `SKILL.md`. 2. `pip` resolves the latest available `yt-dlp` release and its dependencies from the user's configured package index. 3. If the selected release, transitive dependency, registry response, or configured mirror has been compromised, unreviewed code is installed. 4. The installed `yt-dlp` executable is later invoked by `clips_machine.py` when processing a remote video. 5. Compromised dependency code executes with the privileges of the user running the installation or the Skill. ### Impact Assessment The potential impact depends entirely on a supply-chain compromise. A malicious installed package could execute code with the installing or invoking user's privileges, access files available to that account, alter downloaded content, or communicate over the network. No compromise is present in the audited project itself, and exploitation requires control of an upstream package source, release, dependency, or package-index configuration. This makes the likelihood lower than a directly exploitable flaw in the bundled c ...[truncated 8 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Use a reviewed and reproducible dependency installation process: 1. Pin `yt-dlp` to a specific reviewed version. 2. Record package hashes in a requirements or lock file. 3. Install with hash verification enabled. 4. Document the expected official package index or trusted distribution source. 5. Upgrade versions through a deliberate review process rather than resolving the latest release automatically. For example, maintain a hash-pinned requirements file: ```text yt-dlp==<reviewed-version> \ --hash=sha256:<verified-distribution-hash> ``` Install it with: ```bash python -m pip install --require-hashes -r requirements.txt ``` The version and hash must be populated from a verified release artifact and updated only after review. ]]>
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (19)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The metadata and description claim the skill is self-contained and uses no external modules, yet the file explicitly requires external binaries and supports downloading from third-party URLs. That mismatch can mislead users and security controls about the real attack surface, especially because remote fetching and shelling out to external executables materially increase risk compared with a purely local, self-contained transformation skill.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill advertises capabilities that imply network access, local file creation, and shell execution through ffmpeg, yt-dlp, and whisper-cpp, but it does not declare any explicit tool scope such as permissions or allowed-tools. This creates a transparency and policy gap: users and the host agent may approve the skill without understanding that it can download remote content and write multiple derivative artifacts to disk.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
brew install ffmpeg yt-dlp whisper-cpp

# Or on Linux
sudo apt install ffmpeg
pip install yt-dlp
# Build whisper.cpp from source
```
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Description-Behavior Mismatch

Medium
Confidence
97% confidence
Finding
The manifest and module docstring present the skill as 'self-contained, no external modules,' yet the implementation shells out to ffmpeg, ffprobe, yt-dlp, whisper-cpp, and which, and also downloads videos from remote platforms. That is a meaningful behavior/scope mismatch because these are substantial external runtime dependencies rather than incidental implementation details.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
This is an active contradiction between the top-level documentation and the implementation. Core operations rely on subprocess calls to ffmpeg/ffprobe, yt-dlp, whisper-cpp, and which, so the code is not self-contained in the ordinary sense described by the docstring.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def get_video_duration(video_path: str) -> float:
    """Get video duration in seconds"""
    result = subprocess.run([
        "ffprobe", "-v", "quiet",
        "-show_entries", "format=duration",
        "-of", "default=noprint_wrappers=1:nokey=1",
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
"-o", output_path,
        url
    ]
    subprocess.run(cmd, capture_output=True, text=True, timeout=600)
    return output_path
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
def transcribe_with_timestamps(video_path: str) -> List[Dict]:
    """Extract audio and transcribe with Whisper.cpp"""
    audio_path = secure_tempfile(suffix=".wav")
    subprocess.run([
        "ffmpeg", "-y", "-i", video_path,
        "-ar", "16000", "-ac", "1", "-c:a", "pcm_s16le",
        audio_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 code sets the transcription model to `ggml-base.en.bin`, which enforces English transcription behavior. Because the skill does not provide any user opt-in, language selection, or documented justification for this locale constraint, it conflicts with the policy against forcing a specific language without user choice.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
whisper_cmd = "whisper-cpp"
    whisper_model = "/usr/local/share/whisper-cpp/models/ggml-base.en.bin"

    if subprocess.run(["which", whisper_cmd], capture_output=True).returncode != 0:
        whisper_cpp_dir = Path.home() / ".whisper-cpp"
        whisper_cmd = str(whisper_cpp_dir / "main")
        whisper_model = str(whisper_cpp_dir / "models" / "ggml-base.en.bin")
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
whisper_cmd = str(whisper_cpp_dir / "main")
        whisper_model = str(whisper_cpp_dir / "models" / "ggml-base.en.bin")

    subprocess.run([
        whisper_cmd, "-m", whisper_model,
        "-f", audio_path, "-oj", "-of", output_base
    ], capture_output=True)
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
def cut_video(input_path: str, output_path: str, start: float, end: float) -> str:
    """Cut a segment from a video"""
    subprocess.run([
        "ffmpeg", "-y",
        "-ss", str(start),
        "-i", input_path,
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
def crop_to_vertical(input_path: str, output_path: str) -> str:
    """Crop horizontal video to vertical 9:16 (center crop)"""
    subprocess.run([
        "ffmpeg", "-y",
        "-i", input_path,
        "-vf", "crop=ih*9/16:ih",
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
with open(ass_file, "w") as f:
        f.write(header + events)

    subprocess.run([
        "ffmpeg", "-y", "-i", video_path,
        "-vf", f"ass={ass_file}",
        "-c:v", "libx264", "-preset", "fast", "-c:a", "aac",
Confidence
89% confidence
Finding
The ffmpeg ass filter argument embeds a temporary file path directly into a filter string. Because ffmpeg filter syntax treats certain characters specially, an unusual temp path or hostile environment/file-system conditions could break parsing or, in edge cases, enable filter-argument injection rather than being treated purely as a filename.

Tainted flow: 'transcript_path' from os.getenv (line 422, credential/environment) → open (file write)

Medium
Category
Data Flow
Content
print("\nStep 2/5: Transcribing audio...")
    transcript = transcribe_with_timestamps(video_path)
    transcript_path = output_dir / "transcript.json"
    with open(transcript_path, "w") as f:
        json.dump(transcript, f, indent=2)
    results["transcript"] = str(transcript_path)
    print(f"   Done - Transcribed {len(transcript)} segments")
Confidence
87% confidence
Finding
The output path ultimately derives from OUTPUT_DIR, which is sourced from an environment variable and used for file writes without restriction. In environments where an attacker can influence env vars or invocation parameters, this can lead to arbitrary file creation/overwrite in attacker-chosen directories, especially when combined with the user-controlled output_name path component.

Tainted flow: 'moments_path' from os.getenv (line 438, credential/environment) → open (file write)

Medium
Category
Data Flow
Content
moments = detect_viral_moments(transcript, num_clips=num_clips, min_score=min_score)
    moments_path = output_dir / "viral_moments.json"
    with open(moments_path, "w") as f:
        json.dump(moments, f, indent=2)
    results["viral_moments"] = str(moments_path)
    print(f"   Done - Found {len(moments)} viral moments")
Confidence
87% confidence
Finding
This write has the same path-trust issue: the destination is derived from an environment-controlled base directory and a user-influenced folder name. If abused in a shared or automated environment, the program may overwrite files outside the intended workspace.

Tainted flow: 'output_dir' from os.getenv (line 397, credential/environment) → open (file write)

Medium
Category
Data Flow
Content
summary += f"- Time: {moment['start_time']:.1f}s - {moment['end_time']:.1f}s\n"
        summary += f"- Hook: {moment['hook']}\n- File: clip_{i+1:03d}.mp4\n\n"

    with open(output_dir / "summary.md", "w") as f:
        f.write(summary)
    results["summary"] = str(output_dir / "summary.md")
Confidence
87% confidence
Finding
Writing summary.md under output_dir is vulnerable to the same path traversal/arbitrary write class of issue. The skill context makes this more relevant because CLI tools are often run in automation where environment variables and arguments may be externally influenced.

Missing User Warnings

Low
Confidence
88% confidence
Finding
The skill describes transcription, clipping, and export behavior but does not warn that it will generate transcripts, scored-moment metadata, and clipped media files on disk. This is a privacy and data-handling issue because derivative artifacts may contain sensitive spoken content or copyrighted material and may persist after the task is complete.

Missing User Warnings

Low
Confidence
90% confidence
Finding
The supported sources section encourages downloading content from third-party platforms but omits a privacy and compliance warning. Users may not realize that providing URLs causes network requests to those services and may implicate platform terms, personal data, or sensitive media processing.

Static analysis

No suspicious patterns detected.