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## 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
## 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.

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.

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.

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.

Lp3

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.

Missing User Warnings

Medium
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.

Vague Triggers

Medium
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.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
}
    )

    result = subprocess.run(cmd, check=False)
    emit(
        {
            "event": "fallback_polling_end",
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Session Persistence

Medium
Category
Rogue Agent
Content
"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": [
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.

Missing User Warnings

Low
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.

Static analysis

No suspicious patterns detected.