Back to skill

Security audit

ai-video-skills

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its video-generation purpose, but its helper scripts can send the user's bearer API key to arbitrary URLs if invoked with unsafe endpoint options.

Review before installing. Use this only if you trust the publisher and are comfortable sending prompts, payloads, generation IDs, and your Skills Video API key to the service. Do not run the helper scripts with custom --base-url values or full --sse-endpoint URLs unless you fully trust that origin and intend to send your API key there; prefer relative endpoints under https://open.skills.video/api/v1. Consider using a limited or revocable API key and monitoring credit usage.

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

Error
Location
scripts/create_and_wait.py:63
Finding

Bearer API Key Can Be Exfiltrated Through Attacker-Controlled Request URLs

Content
View full analysis

Vulnerability Details

File Location: scripts/create_and_wait.py:63-68, 161-169, 221, 324-325, 334-364; scripts/wait_generation.py:85-97, 107, 118, 153-159
Vulnerability Type: Unrestricted authenticated network destination
Risk Level: High

Vulnerable Code

In scripts/create_and_wait.py, an endpoint may be supplied as an unrestricted full URL:

python
def endpoint_url(base_url: str, endpoint: str) -> str:
    if endpoint.startswith("http://") or endpoint.startswith("https://"):
        return endpoint
    if not endpoint.startswith("/"):
        endpoint = "/" + endpoint
    return f"{base_url.rstrip('/')}{endpoint}"

The API key is attached to the resulting destination without validating its hostname or transport security:

python
req = request.Request(
    url,
    headers={
        "Authorization": f"Bearer {api_key}",
        "Accept": "text/event-stream",
        "Content-Type": "application/json",
    },
    data=json.dumps(payload).encode("utf-8"),
    method="POST",
)
python
with request.urlopen(req, timeout=request_timeout) as resp:

Both the endpoint and base URL are caller-controlled:

python
parser.add_argument("--sse-endpoint", required=True, help="SSE create endpoint path or full URL")
parser.add_argument("--base-url", default="https://open.skills.video/api/v1")
python
api_key = os.environ.get("SKILLS_VIDEO_API_KEY", "").strip()
payload = load_payload(args)
url = endpoint_url(args.base_url, args.sse_endpoint)

sse_rc, generation_id, terminal_payload = run_sse(
    url=url,
    api_key=api_key,
    payload=payload,
    request_timeout=args.sse_request_timeout,
)

The polling helper in scripts/wait_generation.py has the same trust-boundary issue:

python
def fetch_generation(
    base_url: str,
    generation_id: str,
    api_key: str,
    request_timeout: float,
) -> tuple[int, An
...[truncated 3757 chars]
Remediation
View remediation

Remediation Suggestions

  1. Remove support for unrestricted absolute endpoint URLs. Require --sse-endpoint to be a relative API path beginning with an approved prefix such as /generation/sse/.

  2. Validate the final URL before attaching credentials. Parse it with urllib.parse.urlsplit and require:

    • Scheme exactly equal to https.
    • Hostname exactly equal to open.skills.video.
    • No embedded username or password.
    • No unexpected port.
    • No IP-literal or deceptive suffix hostname.
  3. Restrict or remove --base-url. If custom environments are required, make them an explicit opt-in mode and require a separate credential intended for that origin. Do not reuse the production key automatically.

  4. Constrain redirects. Disable automatic redirects for authenticated calls or implement a redirect handler that permits redirects only when the destination remains on the same approved HTTPS origin. Strip the Authorization header before any cross-origin redirect.

  5. Apply one shared origin-validation function in both create_and_wait.py and wait_generation.py before constructing an authenticated request.

  6. Fail closed. Reject malformed URLs, plaintext HTTP, scheme-relative URLs, unexpected ports, and any origin not explicitly approved.

  7. Add security regression tests covering:

    • http://open.skills.video
    • https://attacker.example
    • https://open.skills.video.attacker.example
    • URLs containing user-info
    • IP-address destinations
    • Cross-origin redirects
    • Valid relative paths on https://open.skills.video
  8. Rotate exposed credentials. If either helper has been run with an untrusted URL, revoke the affected API key, issue a new one, and review API usage and credit consumption.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (10)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The declared purpose suggests a request-construction/execution utility for the skills.video API. However, the actual code is a simple environment validation helper that checks for an API key and emits setup instructions. While API key checking is related to API usage, it is only a supporting prerequisite and does not implement the core declared functionality. Therefore, the code chunk does not accurately represent the stated primary purpose.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The declared purpose centers on constructing and executing skills.video video-generation API calls from OpenAPI specs. The supplied code instead handles post-response error analysis: it reads a status code and response body, extracts messages, classifies errors, suggests remediation, builds a credits-check curl command, and exits with mapped codes. While this may support debugging of API calls, it is not the described primary functionality and lacks any request-building, request execution, or OpenAPI-driven behavior. Therefore the code materially differs from the declared description.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The code is a read-only inspection utility. It parses local OpenAPI/docs JSON, lists endpoints, resolves schemas, finds polling/SSE variants, and outputs endpoint metadata and optional request templates. There is no HTTP client logic, no network request execution, and no actual invocation of open.skills.video APIs. The description is partially accurate about documenting/debugging calls from OpenAPI specs, but materially overstates the capability to build and execute requests. It also broadens scope beyond videos by supporting images. Therefore the description does not accurately represent the actual behavior.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding

The code does interact with the declared service (open.skills.video) and concerns video generation workflows, but its primary purpose is narrower and materially different from the stated description. It does not build requests from OpenAPI specs or create general video generation calls; instead, it monitors an already-created generation task by polling a status endpoint. This is an undeclared operational capability relative to the description, so the description does not accurately represent the supplied code chunk.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill instructs the agent to use shell, environment variables, local files, and network access, but it does not declare any explicit tool scope or permission boundaries. That increases the chance of overbroad execution in environments that rely on skill metadata for containment, making unintended file access, credential use, or outbound requests more likely.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill encourages sending prompts and request payloads to an external API service but does not clearly disclose that user-provided content will leave the local environment. This can lead to inadvertent disclosure of sensitive prompts, proprietary data, or personal information, especially because the workflow normalizes execution through curl and helper scripts.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The default prompt uses a broad natural-language invocation pattern, "Use $ai-video-skills to create videos of {subject}," which can overlap with ordinary user requests about creating videos. This increases the chance of unintended skill activation or prompt-trigger collisions, especially in systems that infer tool use from conversational phrasing rather than explicit user consent.

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/create_and_wait.py (reported line 311)May include surrounding context.

python
}
    )

    result = subprocess.run(cmd, check=False)
    emit(
        {
            "event": "fallback_polling_end",

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · scripts/ensure_api_key.py (reported line 36)May include surrounding context.

python
"dashboard_url": "https://skills.video/dashboard/developer",
                "how_to_get_key": [
                    "Sign in at the dashboard URL.",
                    "Click 'Create API Key'.",
                    "Copy the generated key.",
                ],
                "set_env_examples": [

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

This code performs network requests to an external service using a bearer token from SKILLS_VIDEO_API_KEY and includes the user-supplied generation ID in those requests. While the module docstring explains that it polls generation status, there is no explicit warning, confirmation, or comment disclosing that data and credentials are sent over HTTP.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.