Back to skill

Security audit

FFmpeg Video Watermark Remover

Security checks for vulnerabilities and agentic risk

Overview

This skill does what it claims, but its video-processing helpers use unsafe temporary file handling that could overwrite local files in some environments.

Review before installing. The skill appears purpose-aligned and does not show exfiltration, persistence, or hidden behavior, but it should be run only in a contained workspace or under an unprivileged account until its temporary-file handling is fixed with private unpredictable temp directories, no preexisting-path reuse, safer concat manifests, and clearer overwrite disclosure.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/process_video.py:29
Finding

Predictable Shared Temporary Paths Allow Local Symlink-Based File Overwrite

Content
View full analysis

Vulnerability Details

File Location: scripts/process_video.py:29-36, 62-63, 89
Vulnerability Type: Predictable temporary paths and unsafe file creation
Risk Level: Medium

The processor derives its temporary directory from the input video's basename and creates predictable segment and manifest filenames inside that directory.

python
def process_multi_segment(input_video, output_video, segments, temp_dir):
    """
    Process video with multiple delogo regions across different time segments.
    
    segments: list of (start_time, end_time, x, y, w, h)
    """
    temp_dir = Path(temp_dir)
    temp_dir.mkdir(parents=True, exist_ok=True)
    
    # Process each segment
    seg_files = []
    for i, (s, e, x, y, w, h) in enumerate(segments):
        seg_file = temp_dir / f'seg_{i+1}.mp4'

The predictable concatenation manifest is written without checking whether it is a symbolic link:

python
    concat_file = temp_dir / 'concat.txt'
    concat_file.write_text('\n'.join(f"file '{f}'" for f in seg_files))

The temporary directory itself is deterministically generated under globally shared /tmp:

python
    temp_dir = f"/tmp/watermark_proc_{Path(input_video).stem}"

SKILL.md:19-21 also documents predictable frame names in /tmp:

bash
for t in 1 2 3 4 5; do
  ffmpeg -y -ss 00:00:0$t -i "$INPUT" -frames:v 1 "/tmp/wm_frame_$t.jpg" 2>/dev/null
done

Technical Analysis

Temporary resources in a globally writable directory must be created with unpredictable names and exclusive ownership. Here, an attacker can determine the processing directory from the public input basename. The code accepts an already-existing directory through mkdir(..., exist_ok=True) and does not verify its owner, permissions, or contents.

Path.write_text() follows symbolic links when opening concat.txt. The segment files are also passed to FFmpeg with -y, which aut ...[truncated 2242 chars]

Remediation
View remediation

Remediation Suggestions

  1. Create a unique, private temporary directory using tempfile.TemporaryDirectory() rather than deriving its name from user-controlled input:

    python
    import tempfile
    
    with tempfile.TemporaryDirectory(prefix="watermark_proc_") as temp_dir:
        ok = process_multi_segment(
            input_video,
            output_video,
            segments,
            temp_dir,
        )
    
  2. Do not reuse an existing temporary directory. Ensure the directory is created atomically with permissions restricted to the current user.

  3. Avoid predictable standalone files in /tmp. Update the commands in SKILL.md to create a private directory first, for example with mktemp -d, and place all frames inside it.

  4. Where files are created directly by Python, use exclusive creation and reject symbolic links. On supported platforms, use secure open flags such as O_CREAT | O_EXCL | O_NOFOLLOW, then verify that the resulting file is a regular file owned by the current user.

  5. Before invoking FFmpeg, ensure each output path is inside the newly created private directory and does not already exist. Since only the current process can access a correctly permissioned private directory, this prevents another local user from pre-positioning links.

  6. Remove temporary resources reliably after both successful and failed processing. TemporaryDirectory() provides automatic cleanup when used as a context manager.

  7. Run video processing under a dedicated, unprivileged operating-system account with access limited to the required input, output, and private temporary locations. This limits impact if another file-handling defect is discovered.

Vulnerability Patterns
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (9)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill invokes shell commands and writes files to disk, but it does not declare any tool scope or permissions boundaries. That creates an unnecessary trust gap: an agent may execute ffmpeg and create or overwrite files without an explicit policy constraint, increasing the risk of unintended command execution or file modification if inputs or future edits are mishandled.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/extract_frames.py (reported line 13)May include surrounding context.

python
def get_video_info(input_path):
    cmd = ['ffprobe', '-v', 'quiet', '-print_format', 'json', '-show_streams', input_path]
    data = json.loads(subprocess.run(cmd, capture_output=True, text=True).stdout)
    vid = next((s for s in data['streams'] if s['codec_type'] == 'video'), None)
    if not vid:
        raise ValueError(f"No video stream found in {input_path}")

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/extract_frames.py (reported line 19)May include surrounding context.

python
def get_video_info(input_path):
    cmd = ['ffprobe', '-v', 'quiet', '-print_format', 'json', '-show_streams', input_path]
    data = json.loads(subprocess.run(cmd, capture_output=True, text=True).stdout)
    vid = next((s for s in data['streams'] if s['codec_type'] == 'video'), None)
    if not vid:
        raise ValueError(f"No video stream found in {input_path}")

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The ffmpeg command uses the '-y' flag, which forces overwriting of existing files in the output directory, and the function also creates/writes files there. While the script prints progress, it does not disclose that existing frame files may be replaced, and the module docstring does not warn about this destructive write behavior.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/extract_frames.py (reported line 43)May include surrounding context.

python
'-frames:v', '1', '-q:v', '2',
            str(frame_path)
        ]
        subprocess.run(cmd, capture_output=True)
        if frame_path.exists():
            frames.append((t, frame_path))
            print(f"  t={t:.2f}s: {frame_path}")

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/process_video.py (reported line 18)May include surrounding context.

python
'-vf', f'delogo=x={x}:y={y}:w={w}:h={h}',
        '-c:a', 'copy', output_video
    ]
    result = subprocess.run(cmd, capture_output=True, text=True)
    if result.returncode != 0:
        print(f"ERROR: {result.stderr[-500:]}", file=sys.stderr)
        return False

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/process_video.py (reported line 53)May include surrounding context.

python
'-vf', f'delogo=x={x}:y={y}:w={w}:h={h}',
        '-c:a', 'copy', output_video
    ]
    result = subprocess.run(cmd, capture_output=True, text=True)
    if result.returncode != 0:
        print(f"ERROR: {result.stderr[-500:]}", file=sys.stderr)
        return False

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
84% confidence
Finding

The concat step enables ffmpeg concat demuxer with -safe 0 and writes file entries using unescaped path data derived from a user-influenced temp directory. If the input filename stem contains quotes or crafted characters, it can break the concat file format and inject additional concat directives or alternate file paths, causing ffmpeg to read unintended local files during processing.

Content

Scanner excerpt · scripts/process_video.py (reported line 64)May include surrounding context.

python
concat_file = temp_dir / 'concat.txt'
    concat_file.write_text('\n'.join(f"file '{f}'" for f in seg_files))
    
    result = subprocess.run([
        'ffmpeg', '-y', '-f', 'concat', '-safe', '0',
        '-i', str(concat_file), '-c', 'copy', output_video
    ], capture_output=True, text=True)

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

This markdown file instructs the agent to save sample frames to /tmp and later save the processed output to a downloads directory. While the file does mention final delivery confirmation, it does not warn users that the skill will create intermediate and output files on disk, which is a user-impacting behavior covered by missing user disclosures for markdown files.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.