Back to skill

Security audit

Arch Video Cut

Security checks for vulnerabilities and agentic risk

Overview

The skill is a local video-editing workflow, but it builds shell commands from editable saved preferences, creating a real command-execution risk.

Review or patch the subprocess command construction before installing, especially if you might import or share preference files. Use it only in a contained workspace with trusted media and trusted config/user_preferences.json, and expect it to save local editing preferences and overwrite its fixed output video files.

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

Error
Location
scripts/full_workflow.py:166
Finding
Arbitrary Shell Command Injection Through Unvalidated Preference Values<![CDATA[ ## Vulnerability Details **File Location**: `scripts/full_workflow.py:24-32, 40-47, 166-174, 185-193`; configuration source in `scripts/preference_learner.py:39-47` **Vulnerability Type**: OS command injection through configuration values interpolated into shell commands **Risk Level**: High ### Vulnerable Code Configuration values are loaded from an editable JSON file without schema or character validation: ```python def load_preferences(): """加载用户偏好""" if PREFERENCE_FILE.exists(): with open(PREFERENCE_FILE, 'r', encoding='utf-8') as f: prefs = json.load(f) # 合并默认值(防止新版本缺少字段) return deep_merge(DEFAULT_PREFERENCES, prefs) return DEFAULT_PREFERENCES.copy() ``` The resulting values are assigned to global variables: ```python TARGET_DURATION = prefs["video"]["target_duration"] HORIZONTAL_FONT_SIZE = prefs["subtitles"]["horizontal_font_size"] VERTICAL_FONT_SIZE = prefs["subtitles"]["vertical_font_size"] FONT_NAME = prefs["subtitles"]["font_name"] AUTO_WRAP = prefs["subtitles"]["auto_wrap"] MARGIN_V = prefs["subtitles"]["margin_v"] BG_MUSIC_VOLUME = prefs["audio"]["background_music_volume"] FADE_DURATION = prefs["audio"]["fade_in_duration"] ``` Commands are then passed to a system shell: ```python def run_cmd(cmd, description=""): """运行命令并打印进度""" if description: print(f"🎬 {description}...") result = subprocess.run(cmd, shell=True, capture_output=True, text=True) if result.returncode != 0: print(f"❌ 失败:{result.stderr}") return False return True ``` For example, the unvalidated `FONT_NAME` value is embedded directly into an FFmpeg shell command: ```python def add_subtitles_to_video(): """添加字幕到视频(使用 ffmpeg-full + libass)""" print("\n📝 步骤 4: 烧录字幕到视频(横屏 16:9,720p)") # 使用 ffmpeg-full(支持 libass 字幕) ffmpeg_full = "/usr/local/Cellar/ffmpeg-full/8.0.1_3/bin/ffmpeg" # 字幕样式:从偏好读取,输出 720p (1280x720) wrap_style = "2" if not AUTO ...[truncated 3621 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Eliminate shell interpretation by constructing argument arrays and using `shell=False`: ```python subprocess.run( [ ffmpeg_full, "-y", "-i", str(VIDEO_FILE), "-i", str(TEMP_DIR / "mixed_audio.aac"), "-vf", filter_value, "-c:a", "copy", str(OUTPUT_FILE), ], shell=False, capture_output=True, text=True, check=False, ) ``` 2. Apply the same argument-list approach to every FFmpeg and FFprobe invocation. Do not attempt to solve the issue solely through manual shell escaping. 3. Validate the complete preference document before use: - Require `font_name` to be a string matching a conservative allowlist, such as letters, digits, spaces, underscores, and hyphens. - Require durations, font sizes, margins, and volume values to be numeric. - Enforce safe ranges, including positive durations, bounded font sizes and margins, volume between `0` and `1`, and fade durations no greater than the target duration. - Require Boolean values for Boolean settings. - Reject unknown keys where practical. 4. Handle malformed JSON and validation failures explicitly rather than continuing with partially trusted values. 5. Restrict the preference file to the owning user where the deployment environment warrants it. 6. Add regression tests using quotes, semicolons, command substitutions, newlines, and other shell metacharacters to verify that all values remain literal FFmpeg arguments. ]]>

T08 · Insecure Dependencies

Warning
Location
SKILL.md:28
Finding
Unpinned and Unlocked Third-Party Dependency Installation<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:28-35, 194-197`; duplicated in `README.md:45-50` **Vulnerability Type**: Mutable third-party dependency installation without version or integrity pinning **Risk Level**: Medium ### Vulnerable Documentation ```bash brew install ffmpeg-full # Required for libass subtitle support pip3 install faster-whisper # Optional: for speech transcription ``` The troubleshooting section repeats the unpinned Python installation: ```bash pip3 install faster-whisper # Or skip transcription and edit subtitle text directly in script ``` The README also instructs users to install the same mutable dependency: ```bash brew install ffmpeg-full pip3 install faster-whisper # 可选,用于语音转录 ``` ### Technical Analysis The installation commands do not specify audited versions, lock transitive dependencies, or verify package hashes. Consequently, installation resolves whatever versions the package repositories serve at the time the command is run. Python packages and their transitive dependencies may execute build or installation logic on the user's system. The audited implementation does not import or invoke `faster-whisper`; `transcribe_audio()` instead writes hard-coded subtitle text. The documented dependency therefore expands the supply-chain attack surface without supporting the current code path. There is no evidence in the audited project that `faster-whisper` or `ffmpeg-full` is malicious. The risk arises from mutable, unverified package resolution and the unnecessary installation instruction. ### Attack Path 1. A user follows the Skill's prerequisite or troubleshooting instructions. 2. The package manager contacts its configured repositories and resolves the latest available package and transitive dependency versions. 3. If an upstream release, transitive dependency, package index, mirror, or local package-manager configuration has been compromised, altered package content is downloaded. 4. Package build or i ...[truncated 1034 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the `faster-whisper` installation instruction unless actual transcription support is implemented. 2. If the dependency becomes necessary, declare an exact audited version in a dependency file rather than instructing users to install the latest release. 3. Generate and verify hashes for the package and all transitive dependencies. For pip-based deployment, use a locked requirements file and install with `pip install --require-hashes -r requirements.txt`. 4. Use an isolated virtual environment and avoid privileged package installation. 5. Pin the supported FFmpeg distribution or version and document a trusted installation source. Where feasible, verify downloaded artifacts through package-manager signatures or published checksums. 6. Add an explicit dependency inventory and update process so pinned versions receive controlled security updates. 7. Correct the documentation to state that current subtitle generation is hard-coded and does not perform speech transcription. ]]>
Vulnerability Patterns
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (18)

Tool Parameter Abuse

High
Category
Tool Misuse
Content
"""运行命令并打印进度"""
    if description:
        print(f"🎬 {description}...")
    result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
    if result.returncode != 0:
        print(f"❌ 失败:{result.stderr}")
        return False
Confidence
97% confidence
Finding
This is the core risky pattern: a generic command runner uses shell=True for all generated ffmpeg/ffprobe commands, magnifying the attack surface across the workflow. Several command components come from learned preferences and generated file lists, so hostile values can abuse shell parsing or break command boundaries.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
]
    
    # 获取音频时长
    result = subprocess.run(
        f'ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "{AUDIO_FILE}"',
        shell=True, capture_output=True, text=True
    )
Confidence
90% confidence
Finding
The shell is unnecessarily invoked for a simple ffprobe call. In this workflow context the immediate risk is moderated by the hard-coded audio path, but the coding pattern remains unsafe and could become exploitable when adapted to external input.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
# 显示文件信息
    if OUTPUT_FILE.exists():
        size = OUTPUT_FILE.stat().st_size / 1024 / 1024
        result = subprocess.run(
            f'ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "{OUTPUT_FILE}"',
            shell=True, capture_output=True, text=True
        )
Confidence
90% confidence
Finding
This shell-based ffprobe invocation repeats the same unsafe command-execution pattern for the output file. While less directly attacker-controlled in the current script, it reinforces an insecure habit and leaves room for exploitation if output paths become configurable or influenced by external state.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
The README content, headings, and usage guidance are written in Chinese and do not offer an alternative language or indicate that the skill is intentionally region- or audience-specific. Under the stated policy, forcing a specific language without user opt-in is a natural-language policy violation.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
This markdown file states that the script automatically remembers settings and retains the most recent 20 adjustment records, but it does not warn users that their preferences and usage history will be stored on disk. Because this behavior affects user data and privacy, the skill description should explicitly disclose the persistence and retention behavior.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The skill does disclose its self-learning behavior in several sections, but the flagged area indicates the main description lacks a clear upfront warning that user preferences are automatically recorded and reused. That can mislead users about persistence and privacy expectations, especially because editing habits may reveal sensitive workflow patterns or file usage behavior.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
The skill's natural-language interface and descriptive text are entirely in Chinese, with no indication that the user can choose another language or locale. Under the stated policy, a fixed language without user opt-in is a language/locale policy violation unless clearly justified.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The script imports preference-learning functions and immediately loads stored user preferences at startup, while the module header also advertises that it 'automatically learns user preferences.' Although this is partially documented in comments/docstrings, there is no runtime disclosure near startup that preference data is being read from or persisted for future runs, which affects user data handling.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
"""运行命令并打印进度"""
    if description:
        print(f"🎬 {description}...")
    result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
    if result.returncode != 0:
        print(f"❌ 失败:{result.stderr}")
        return False
Confidence
96% confidence
Finding
The helper executes shell commands via subprocess.run(..., shell=True) using command strings assembled from paths and preference-derived values. If any interpolated value contains shell metacharacters or is attacker-controlled through files, preferences, or environment manipulation, this can lead to arbitrary command execution.

Missing User Warnings

Medium
Confidence
84% confidence
Finding
The script creates and overwrites several files, including temporary lists, subtitle files, generated audio, and final video outputs. While progress messages appear after execution begins, there is no clear upfront warning that running the workflow will modify the filesystem and overwrite outputs via ffmpeg's '-y' option.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The function is named `transcribe_audio`, the workflow labels this step as `语音转录`, and the module docstring advertises `语音转录字幕`, implying subtitles are derived from the audio content. In reality, the code ignores the audio for transcription and writes a fixed list of subtitle strings, using the audio only to measure duration.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
]
    
    # 获取音频时长
    result = subprocess.run(
        f'ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "{AUDIO_FILE}"',
        shell=True, capture_output=True, text=True
    )
Confidence
88% confidence
Finding
This ffprobe invocation uses shell=True with a formatted command string containing a filesystem path. Although AUDIO_FILE is hard-coded here, using the shell for simple argument passing is unsafe by default and becomes exploitable if the path is ever made configurable or derived from untrusted input.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# 显示文件信息
    if OUTPUT_FILE.exists():
        size = OUTPUT_FILE.stat().st_size / 1024 / 1024
        result = subprocess.run(
            f'ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "{OUTPUT_FILE}"',
            shell=True, capture_output=True, text=True
        )
Confidence
86% confidence
Finding
This ffprobe call again uses shell=True with a command string containing a path. The current output path is internally defined, but the pattern is dangerous because any future change that makes the path user-controlled could turn it into command injection.

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
This Python file contains its top-level usage documentation entirely in Chinese, and the rest of the script continues that language choice in user-facing output. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation unless the locale constraint is explicitly documented and justified.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The interactive experience uses Chinese-only prompts and confirmations, which imposes a specific language on all users. The file does not present a language option or explain a region-specific requirement that would justify the restriction.

Natural-Language Policy Violations

Medium
Confidence
90% confidence
Finding
This code's user-facing description and CLI output are written exclusively in Chinese, and the file does not indicate that the skill is region-specific or that users can opt into another language. Under the language/locale policy, forcing a specific language without user choice is a natural-language policy concern.

Missing User Warnings

Low
Confidence
83% confidence
Finding
This markdown file documents creation of output video files and a configuration reset command, but it does not warn users that running the workflow may overwrite existing outputs or that reset will modify stored preferences. For markdown files, SQP-2 applies when descriptions omit warnings about behaviors that could affect user data or system state.

Missing User Warnings

Low
Confidence
91% confidence
Finding
The guide instructs users to run `python3 scripts/manage_preferences.py reset` and says it restores all settings to defaults, but it does not explicitly warn that previously learned preferences will be lost. For a user-data-affecting operation, the markdown should include a clearer warning about this overwrite/reset effect.

Static analysis

No suspicious patterns detected.