Back to skill

Security audit

ai-image-skills

Security checks for vulnerabilities and agentic risk

Overview

This skill appears to be a real skills.video image-generation helper, but unsafe endpoint options could send the user's API key to arbitrary URLs.

Install only if you are comfortable giving this skill access to a skills.video API key and paid generation capability. Use relative open.skills.video endpoints only, avoid arbitrary --base-url or full --sse-endpoint values, do not store the key in shell profile files if you can use a secrets manager or session-only environment variable, and rotate the key if it may have been exposed.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/create_and_wait.py:63
Finding
API Key Disclosure Through Unrestricted Credential-Bearing Request Destinations<![CDATA[ ## Vulnerability Details **File Location**: `scripts/create_and_wait.py:63-68, 156-170, 221, 324-325, 354-363`; `scripts/wait_generation.py:79-100, 107, 154-159` **Vulnerability Type**: Unrestricted transmission of bearer credentials to user-controlled URLs **Risk Level**: High ### Vulnerable Code `scripts/create_and_wait.py:63-68`: ```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}" ``` `scripts/create_and_wait.py:156-170`: ```python def run_sse( url: str, api_key: str, payload: dict[str, Any], request_timeout: float, ) -> tuple[int, str | None, Any]: 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", ) ``` `scripts/create_and_wait.py:324-325, 354-363`: ```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 payload = load_payload(args) url = endpoint_url(args.base_url, args.sse_endpoint) emit({"event": "start", "url": url, "mode": "sse_then_poll_fallback"}) sse_rc, generation_id, terminal_payload = run_sse( url=url, api_key=api_key, payload=payload, request_timeout=args.sse_request_timeout, ) ``` `scripts/wait_generation.py:79-100`: ```python def fetch_generation( base_url: str, generation_id: str, api_key: str, request_timeout: float, ) -> tuple[int, Any]: url = f"{base_url.rstrip('/')}/generation/{generation_id}" req = request.Request( url, headers={ "Aut ...[truncated 3396 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Reject absolute values for `--sse-endpoint`; accept only relative API paths. 2. Parse all destinations with `urllib.parse.urlsplit` and enforce: - Scheme exactly `https`. - Hostname exactly `open.skills.video`. - No URL user information. - Only the expected HTTPS port. 3. Apply the same validation to `--base-url` in both scripts. 4. Prefer removing destination overrides entirely unless model-specific OpenAPI contracts demonstrably require them. 5. If overrides are required, use an explicit allowlist rather than suffix or substring checks. 6. Prevent authenticated requests from following redirects to a different origin, or revalidate every redirect target and strip authorization on any origin change. 7. Add tests confirming rejection of: - `http://open.skills.video/...` - `https://attacker.example/...` - `https://open.skills.video.attacker.example/...` - `https://attacker.example@open.skills.video/...` - Cross-origin redirects. 8. Use a narrowly scoped API key where the platform supports key-level permissions and rotation. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
scripts/ensure_api_key.py:39
Finding
Setup Guidance Encourages Plaintext Persistence of a Long-Lived API Key<![CDATA[ ## Vulnerability Details **File Location**: `scripts/ensure_api_key.py:39-42` **Vulnerability Type**: Plaintext secret storage in a persistent shell profile **Risk Level**: Low ### Vulnerable Code ```python "set_env_examples": [ "export SKILLS_VIDEO_API_KEY=\"<YOUR_API_KEY>\"", "echo 'export SKILLS_VIDEO_API_KEY=\"<YOUR_API_KEY>\"' >> ~/.zshrc && source ~/.zshrc", ], ``` ### Technical Analysis The setup output recommends appending the API key directly to `~/.zshrc`. This stores a long-lived bearer credential as plaintext in a persistent shell configuration file. The code does not itself modify the shell profile, so this is not covert persistence. Nevertheless, users following the documented command may expose the credential through file backups, profile synchronization, accidental configuration sharing, permissive file permissions, support bundles, or another local process or account capable of reading the file. The persistent storage is not necessary for the Skill's core operation. The scripts only require the credential to be available at runtime. ### Attack Path 1. The user runs `scripts/ensure_api_key.py` without a configured key. 2. The script displays the shell-profile persistence command. 3. The user replaces the placeholder with a real API key and executes the command. 4. The real key is stored in plaintext in `~/.zshrc`. 5. An attacker or local process with read access to the profile, its backups, or synchronized copies retrieves the key. 6. The attacker reuses the key against the Skills Video API. ### Impact Assessment The exposed credential grants the attacker the API privileges associated with the key. Potential consequences include unauthorized generation requests, consumption of paid credits, access to generation results, and use of other API functions available to that credential. This finding does not independently grant system privileges beyond access already available to a party that can read the profile or its co ...[truncated 100 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the example that writes the API key directly into `~/.zshrc`. 2. Recommend a platform credential manager, secrets vault, or runtime secret-injection facility. 3. If an environment variable must be used, recommend setting it only for the current process or session. 4. Where persistent file storage is unavoidable: - Use a dedicated secrets file rather than a general shell profile. - Require restrictive permissions such as mode `0600`. - Ensure the file is excluded from source control, synchronization, logs, and support archives. - Clearly warn that the value is a sensitive bearer credential. 5. Document API-key rotation and revocation procedures. 6. Recommend least-privilege, narrowly scoped credentials where supported. ]]>
Vulnerability Patterns
  • 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
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (6)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description says the skill builds and executes skills.video image generation REST requests from OpenAPI specs. The supplied code does not do any request construction, OpenAPI handling, HTTP calling, or image generation work. Its sole function is to verify presence of an API key in an environment variable and print configuration guidance. While API key checking could be a supporting utility for such a skill, this code chunk by itself has a materially different primary purpose than the declared description, so this is a mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared description focuses on constructing and executing skills.video image generation REST requests from OpenAPI specifications. The supplied code does something materially different: it accepts an HTTP status and response body, extracts an error message, classifies the failure, prints structured guidance, and exits with a mapped error code. While this could be a supporting debugging utility within the same ecosystem, the code chunk itself neither builds requests, executes API calls, nor works from OpenAPI specs. Its primary purpose is runtime error analysis and recovery guidance, including billing/credits checks, which is not accurately represented by the declared purpose.

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill instructs use of shell, file reads, environment access, and outbound network calls but does not declare any explicit tool scope or permission boundaries. This increases the chance that an agent executes sensitive operations without clear least-privilege constraints, including reading secrets from the environment and making authenticated external requests.

Vague Triggers

Medium
Confidence
92% confidence
Finding
The default prompt uses a very broad invocation phrase, 'Use $ai-image-skills to create images of {subject}', which can overlap with ordinary user requests about creating images. This increases the chance the skill is triggered in situations where the user did not explicitly intend to invoke this specific capability, potentially causing unintended external API usage or routing of requests through the skill.

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.

Static analysis

No suspicious patterns detected.