Back to skill

Security audit

Skill Runway Video Gen

Security checks for vulnerabilities and agentic risk

Overview

The skill is a straightforward Runway video-generation wrapper with disclosed network use, credential lookup, billing impact, and local output writes, though users should review credential handling and dependency hygiene.

Install only if you are comfortable sending the selected image and prompt to Runway and being charged for generated output. Prefer RUNWAY_API_KEY or a dedicated config file, avoid storing unrelated secrets in ~/tiktok-api.json, and consider pinning dependencies before routine use.

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

T08 · Insecure Dependencies

Warning
Location
scripts/generate_video.py:2
Finding
Unpinned Third-Party Dependency## Vulnerability Details **File Location**: `scripts/generate_video.py`, lines 2–5 **Vulnerability Type**: Unrestricted dependency resolution **Risk Level**: Medium ### Vulnerable Code ```python # /// script # requires-python = ">=3.10" # dependencies = ["requests"] # /// ``` ### Technical Analysis The script declares `requests` without an exact version, lockfile, or integrity hash. The documented execution command, `uv run scripts/generate_video.py`, may therefore resolve and install packages from mutable package-index state at runtime. This creates a supply-chain risk because the exact dependency graph used during execution is not guaranteed to match the graph reviewed during the audit. A compromised package release, compromised transitive dependency, unsafe package source, or future incompatible release could introduce attacker-controlled code into the process. Python dependency initialization and imports execute with the same permissions as the script. In this case, the process can access the Runway API key, the user-selected source image, the configured output location, and the network. ### Attack Path 1. An attacker compromises a future release of `requests`, one of its resolved dependencies, or the package source used by `uv`. 2. A user invokes the documented command: ```bash uv run scripts/generate_video.py ... ``` 3. Because no exact dependency version or verified lockfile is enforced, `uv` resolves and installs the compromised package version. 4. Attacker-controlled initialization or import code executes inside the Skill process. 5. The malicious dependency can read the `RUNWAY_API_KEY` environment variable or `~/tiktok-api.json`, access the supplied image and other files available to the invoking user, and transmit data through the process's available network access. ### Impact Assessment Successful exploitation would provide code execution with the privileges of the user running th ...[truncated 552 chars]
Remediation
## Remediation Suggestions 1. Pin `requests` to a reviewed exact version rather than using an unrestricted package name. 2. Generate and commit a lockfile containing the complete transitive dependency graph. 3. Include and verify cryptographic hashes for downloaded distributions where supported. 4. Execute with locked or frozen dependency resolution so runtime execution fails if the environment differs from the reviewed lockfile. 5. Use a trusted package index and prevent fallback to unapproved indexes. 6. Add automated dependency vulnerability and integrity scanning to release workflows. 7. Periodically update dependencies through a controlled review process rather than resolving arbitrary current versions during normal execution.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (9)

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill invokes code that requires environment and network access, but it declares no explicit tool scope or permissions boundary. This creates an ambiguity gap where an agent or user may run the skill without realizing it can access secrets and make outbound API calls, increasing the chance of unintended credential exposure or misuse.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The skill instructs the runtime to read an API key from an environment variable or a local credential file, but it provides no warning about sensitive secret handling, storage, or least-privilege usage. In an agentic environment, this can normalize silent secret access and increase the risk of credential leakage through logs, prompts, error messages, or misuse of an unintended local file.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The skill reads credentials from ~/tiktok-api.json, which is unrelated to its stated Runway video-generation purpose and broadens access to local secrets beyond least privilege. This creates unnecessary coupling to another app's config file and increases the chance of accidental secret exposure or misuse if the file contains additional tokens or is shared across tools.

External Transmission

Medium
Category
Data Exfiltration
Content
}

    print(f"[runway] Submitting job — {args.duration}s @ {args.ratio} ...")
    resp = requests.post(
        "https://api.dev.runwayml.com/v1/image_to_video",
        headers=headers,
        json=payload,
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
}

    print(f"[runway] Submitting job — {args.duration}s @ {args.ratio} ...")
    resp = requests.post(
        "https://api.dev.runwayml.com/v1/image_to_video",
        headers=headers,
        json=payload,
Confidence
80% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
print(f"[runway] Submitting job — {args.duration}s @ {args.ratio} ...")
    resp = requests.post(
        "https://api.dev.runwayml.com/v1/image_to_video",
        headers=headers,
        json=payload,
        timeout=30,
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
print(f"[runway] Submitting job — {args.duration}s @ {args.ratio} ...")
    resp = requests.post(
        "https://api.dev.runwayml.com/v1/image_to_video",
        headers=headers,
        json=payload,
        timeout=30,
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Tainted flow: 'poll_url' from requests.post (line 86, network input) → requests.get (network output)

Medium
Category
Data Flow
Content
poll_url = f"https://api.dev.runwayml.com/v1/tasks/{task_id}"
    while True:
        time.sleep(8)
        r = requests.get(poll_url, headers=headers, timeout=15)
        if not r.ok:
            print(f"ERROR: Poll failed {r.status_code}: {r.text}", file=sys.stderr)
            sys.exit(1)
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Tainted flow: 'video_url' from requests.get (line 103, network input) → requests.get (network output)

Medium
Category
Data Flow
Content
sys.exit(1)
            video_url = outputs[0]
            print(f"[runway] Downloading video from {video_url[:60]}...")
            video_resp = requests.get(video_url, timeout=60)
            os.makedirs(os.path.dirname(os.path.abspath(args.output)), exist_ok=True)
            with open(args.output, "wb") as f:
                f.write(video_resp.content)
Confidence
91% confidence
Finding
The script downloads a URL taken directly from API response data without validating the hostname, scheme, or response size. If the upstream service is compromised, misconfigured, or returns an unexpected URL, the skill could be induced to fetch arbitrary remote content and write it to disk, creating an SSRF-like outbound fetch and untrusted file write risk.

Static analysis

No suspicious patterns detected.