Back to skill

Security audit

Skill Veo3 Video Gen

Security checks for vulnerabilities and agentic risk

Overview

This skill does what it claims, but its credential handling, dependency setup, and ffmpeg path handling need review before use.

Install only if you are comfortable sending prompts and reference images to Google's Gemini/Veo API and running local ffmpeg. Prefer GEMINI_API_KEY over --api-key, use a restricted API key, avoid sensitive reference images, use a private output directory, and review or pin dependencies before running.

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 (3)

T08 · Insecure Dependencies

Warning
Location
scripts/generate_video.py:3
Finding
Unpinned Runtime Dependencies Create Supply-Chain Risk<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate_video.py:3-7` **Vulnerability Type**: Unpinned third-party dependencies **Risk Level**: Medium ### Vulnerable Code ```python # /// script # requires-python = ">=3.10" # dependencies = [ # "google-genai>=1.0.0", # "pillow>=10.0.0", # ] # /// ``` ### Technical Analysis The inline dependency declaration specifies only minimum versions. It does not provide exact versions, upper bounds, package hashes, or an accompanying lockfile. Consequently, the documented `uv run` workflow may resolve and install future releases that were not present during the Skill's security review. Python packages execute code in the same process and under the same operating-system identity as the Skill. A compromised package release, compromised package index, or unexpectedly incompatible future release could therefore access the Gemini API key, prompts, reference images, generated media, and files available to the invoking user. The declared `pillow` dependency does not appear to be imported by the script. Keeping an unused dependency unnecessarily expands the supply-chain and installation attack surface. This finding does not establish that the currently available packages are malicious. The issue is that future dependency resolution is mutable and is not reproducibly constrained to reviewed artifacts. ### Attack Path 1. An attacker compromises a future release of `google-genai` or `pillow`, the configured package repository, or an associated publishing account. 2. A user invokes the documented `uv run scripts/generate_video.py ...` command in an environment that has not already locked the dependencies. 3. The resolver selects and installs the compromised release because it satisfies the broad `>=` constraint. 4. Malicious package code executes with the privileges of the user running the Skill. 5. The dependency can read process data, including the Gemini API key and generation inputs, and access fi ...[truncated 633 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin reviewed dependencies to exact versions rather than using open-ended minimum constraints. 2. Commit a reproducible `uv.lock` or equivalent lockfile and require locked or frozen installation during execution. 3. Use package hashes or another integrity-verification mechanism where supported. 4. Configure resolution to use a trusted package index and prevent unintended fallback to untrusted repositories. 5. Remove `pillow` unless it is required by a documented execution path. 6. Establish an update process that reviews and tests dependency changes before modifying the lockfile. 7. Run the Skill in an isolated environment with access only to the API credential and files needed for the requested generation operation. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/generate_video.py:243
Finding
Gemini API Key Can Be Exposed Through Command-Line Arguments<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate_video.py:31-34, 243-247, 255-259, 288` **Vulnerability Type**: Plaintext secret exposure through process arguments **Risk Level**: Medium ### Vulnerable Code ```python def get_api_key(provided_key: str | None) -> str | None: if provided_key: return provided_key return os.environ.get("GEMINI_API_KEY") ``` ```python parser.add_argument( "--api-key", "-k", help="API key (overrides GEMINI_API_KEY)", ) ``` ```python api_key = get_api_key(args.api_key) if not api_key: print("Error: No API key provided.", file=sys.stderr) print("Set GEMINI_API_KEY or pass --api-key", file=sys.stderr) sys.exit(1) ``` ```python client = genai.Client(api_key=api_key) ``` ### Technical Analysis The Skill permits a Gemini API key to be supplied directly through `--api-key` or `-k`. Command-line arguments are commonly exposed through shell history, process inspection utilities, process accounting, debugging output, job runners, audit logs, and orchestration telemetry. Passing the credential to the official Google SDK is necessary for the declared functionality. The vulnerable behavior is accepting the credential as plaintext command-line metadata rather than restricting secret intake to a protected environment variable, secret file descriptor, secret manager, or hidden interactive prompt. The script does not explicitly print the key. Nevertheless, once supplied as an argument, the secret may be recorded or visible outside the script before `argparse` processes it. ### Attack Path 1. A user invokes the supported interface, for example: ```bash python scripts/generate_video.py --api-key SECRET --prompt "..." --filename out.mp4 ``` 2. The command is stored in shell history, captured by a job runner, or exposed in the process argument list. 3. A local user, administrator, monitoring agent, or party with access to collected command telemetry retrieves the plaintex ...[truncated 879 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the `--api-key` and `-k` command-line options. 2. Continue supporting `GEMINI_API_KEY`, but inject it through a protected secret-management mechanism rather than placing it in shell initialization files or command history. 3. Where appropriate, support a hidden prompt using `getpass.getpass()` or retrieval from an operating-system secret store. 4. In automated environments, use the platform's secret manager and ensure command telemetry does not serialize secret values. 5. Document that credentials must never be included in command-line arguments, logs, filenames, prompts, or error reports. 6. Apply Google API-key restrictions, including API allowlists, quota limits, and other available usage restrictions. 7. Rotate any key that has previously been supplied through the command-line interface or captured in logs. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/generate_video.py:137
Finding
Unescaped Paths in ffmpeg Concat Manifest Permit Directive Injection<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate_video.py:137-160` **Vulnerability Type**: Unsafe temporary manifest and unescaped user-controlled path handling **Risk Level**: Medium ### Vulnerable Code ```python def ffmpeg_concat(inputs: list[Path], out_path: Path) -> None: require_bin("ffmpeg") out_path.parent.mkdir(parents=True, exist_ok=True) # Create concat list file. lst = out_path.with_suffix(out_path.suffix + ".concat.txt") lines = [f"file '{p.as_posix()}'" for p in inputs] lst.write_text("\n".join(lines) + "\n", encoding="utf-8") cmd = [ "ffmpeg", "-y", "-f", "concat", "-safe", "0", "-i", str(lst), "-c", "copy", str(out_path), ] p = subprocess.run(cmd, capture_output=True, text=True) ``` ### Technical Analysis The output filename is user-controlled through `--filename`. Segment paths are derived from that filename and then embedded directly into an ffmpeg concat-demuxer manifest: ```python lines = [f"file '{p.as_posix()}'" for p in inputs] ``` The code does not reject or escape single quotes, backslashes, carriage returns, or newline characters according to ffmpeg concat-file syntax. A crafted filename can therefore terminate the quoted path or introduce additional manifest lines and directives. The invocation also uses `-safe 0`, disabling the concat demuxer's normal safe-path restrictions. This is not shell-command injection because ffmpeg is executed with an argument list and `shell=False`. The risk instead exists inside the ffmpeg manifest parser: attacker-controlled path text can alter the data interpreted by ffmpeg. The manifest is created at a predictable path next to the requested output and is never removed. This leaves local path metadata behind and can also expose the operation to file-replacement or race conditions when the output directory is writable by another user. ### Attack Pa ...[truncated 1703 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Validate output paths before use and reject carriage returns, newlines, NUL characters, and other control characters. 2. Escape filenames according to ffmpeg concat-demuxer syntax rather than interpolating them directly between single quotes. 3. Retain `-safe 1` where possible and avoid disabling safe-path validation globally. 4. Create the manifest with `tempfile.NamedTemporaryFile()` or `mkstemp()` in a private directory with restrictive permissions and collision-resistant naming. 5. Delete the manifest in a `finally` block regardless of whether concatenation succeeds. 6. Avoid placing temporary control files in shared or attacker-writable output directories. 7. Consider avoiding the concat manifest entirely by using a safely constructed ffmpeg filter graph or another interface that does not parse user-derived control-file syntax. 8. Add tests covering filenames containing spaces, quotes, backslashes, Unicode characters, carriage returns, and newlines. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (7)

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill exposes capabilities involving environment access, file I/O, network access, and shell execution, but it does not declare any explicit tool scope or permissions. This weakens least-privilege controls and makes it harder for users or orchestrators to understand and constrain what the skill can do before execution.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The skill references Gemini API usage and reference-image inputs, but it does not clearly disclose that prompts and uploaded images are sent to an external third-party service. Users may unknowingly transmit confidential text, product assets, or personal data off-system, creating privacy, compliance, and data-handling risks.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def require_bin(name: str) -> None:
    if subprocess.run(["bash", "-lc", f"command -v {shlex.quote(name)}"], capture_output=True).returncode != 0:
        raise RuntimeError(f"Required binary not found on PATH: {name}")
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
"2",
        str(out_png),
    ]
    p = subprocess.run(cmd, capture_output=True, text=True)
    if p.returncode != 0:
        raise RuntimeError(f"ffmpeg last-frame extract failed: {p.stderr[-2000:]}")
    return out_png
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
"2",
        str(out_png),
    ]
    p = subprocess.run(cmd, capture_output=True, text=True)
    if p.returncode != 0:
        raise RuntimeError(f"ffmpeg last-frame extract failed: {p.stderr[-2000:]}")
    return out_png
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
"192k",
            str(out_path),
        ]
        p2 = subprocess.run(cmd2, capture_output=True, text=True)
        if p2.returncode != 0:
            raise RuntimeError(
                "ffmpeg concat failed.\n"
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Missing User Warnings

Low
Confidence
87% confidence
Finding
The skill documentation tells users it generates videos but does not clearly warn that MP4 outputs are written to local disk and that intermediate segment files may persist when stitching is enabled. This can lead to unintentional storage of sensitive or regulated content on the local filesystem, especially in shared or ephemeral environments.

Static analysis

No suspicious patterns detected.