Back to skill

Security audit

fame graphic

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its image-generation purpose, but it has review-worthy handling of API credentials and generated HTML output.

Install only if you are comfortable with prompts being sent to an image API and saved locally. Before use, verify OPENAI_BASE_URL and OPENAI_API_BASE are unset or intentionally trusted, avoid confidential prompts, and be cautious opening or publishing generated index.html files from untrusted prompt content.

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/illustrate.py:33
Finding
API Credential and Prompt Disclosure Through an Unvalidated Configurable Endpoint<![CDATA[ ## Vulnerability Details **File Location**: `scripts/illustrate.py`, lines 33–41 and 171–181 **Vulnerability Type**: Unvalidated network destination for sensitive credentials **Risk Level**: High ### Vulnerable Code ```python def _api_url() -> str: base = ( os.environ.get("OPENAI_BASE_URL") or os.environ.get("OPENAI_API_BASE") or "https://api.openai.com" ).rstrip("/") if base.endswith("/v1"): return f"{base}/images/generations" return f"{base}/v1/images/generations" ``` ```python def _post_json(url: str, api_key: str, payload: dict, timeout_s: int) -> dict: body = json.dumps(payload).encode("utf-8") req = urllib.request.Request( url, data=body, headers={ "Authorization": f"Bearer {api_key}", "Content-Type": "application/json", }, method="POST", ) try: with urllib.request.urlopen(req, timeout=timeout_s) as resp: raw = resp.read() ``` The sensitive request is made at lines 342–358: ```python url = _api_url() items: list[dict] = [] for i, (prompt, metadata) in enumerate(prompts_with_meta, 1): print(f"Generating illustration {i}/{len(prompts_with_meta)}...") payload = { "model": args.model, "prompt": prompt, "size": args.size, "quality": args.quality, "n": 1, "response_format": "b64_json", } data = _post_json(url=url, api_key=api_key, payload=payload, timeout_s=args.timeout) ``` ### Technical Analysis The Skill legitimately needs to send a prompt and API credential to the OpenAI Images API. However, `_api_url()` accepts `OPENAI_BASE_URL` and `OPENAI_API_BASE` directly from the process environment without validating: - The destination hostname - Whether the URL uses HTTPS - Whether the URL contains embedded credentials or an unusual port - Whether the destination is an approved OpenAI or explicitly trusted proxy endpoint The selected U ...[truncated 1948 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Restrict the default destination to `https://api.openai.com`. 2. Parse configured URLs with `urllib.parse.urlsplit()` and reject: - Non-HTTPS schemes - Missing or unexpected hostnames - Embedded usernames or passwords - Unapproved ports - URL fragments 3. Maintain an explicit allowlist of approved API hostnames. 4. If custom proxies are required, require an explicit command-line option and informed user confirmation rather than silently trusting inherited environment variables. 5. Document that a custom endpoint receives both the API credential and complete prompt. 6. Use a separate proxy-specific credential when connecting to a third-party gateway instead of automatically forwarding the OpenAI API key. 7. Disable or validate cross-origin redirects for authenticated requests so credentials cannot be forwarded to a different host. 8. Reject plaintext HTTP endpoints under all normal operating modes. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/illustrate.py:219
Finding
Stored HTML Injection in the Generated Illustration Gallery<![CDATA[ ## Vulnerability Details **File Location**: `scripts/illustrate.py`, lines 219–242 **Vulnerability Type**: Stored HTML and script injection **Risk Level**: Medium ### Vulnerable Code ```python for it in items: html.append("<div class='card'>") html.append(f"<a href='{it['file']}'><img src='{it['file']}'></a>") html.append("<div>") if it.get("metadata"): meta = it["metadata"] html.append("<h3>" + meta.get("subject", "Illustration") + "</h3>") tags = [] if meta.get("style"): tags.append(f"style: {meta['style']}") if meta.get("mood"): tags.append(f"mood: {meta['mood']}") if meta.get("type"): tags.append(f"type: {meta['type']}") if meta.get("palette"): tags.append(f"palette: {meta['palette']}") if meta.get("composition"): tags.append(f"comp: {meta['composition']}") if tags: tag_html = " ".join(f'<span class="tags">{tag}</span>' for tag in tags) html.append(f'<div class="meta">{tag_html}</div>') html.append(f"<pre>{it['prompt']}</pre>") ``` ### Technical Analysis The generated `index.html` incorporates multiple user-controlled values directly into HTML without context-appropriate encoding. These values include: - Subject - Full custom prompt - Style - Mood - Illustration type - Palette - Composition Although the prompt is placed inside a `<pre>` element, that element does not make its contents safe. An input containing closing markup such as `</pre>` can leave the intended context and inject arbitrary HTML or JavaScript. Metadata values can similarly terminate or alter their surrounding elements. The payload is stored in the generated gallery, making this a stored injection issue. Execution occurs when the user or another recipient opens the resulting `index.html`. The risk becomes more significant if the generated directory is published through a web server, attached to a ...[truncated 1440 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Import and use Python's HTML-escaping functionality: ```python import html safe_subject = html.escape(str(meta.get("subject", "Illustration")), quote=True) safe_prompt = html.escape(str(it["prompt"]), quote=True) ``` 2. Apply `html.escape(..., quote=True)` to every dynamic value, including subject, prompt, tags, and any future attribute values. 3. Use a template engine with automatic escaping enabled instead of constructing HTML through string concatenation. 4. Validate generated filenames separately and URL-encode values used in `href` or `src` attributes. 5. Add a restrictive Content Security Policy, preferably disallowing scripts entirely: ```html <meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src 'self' data:; script-src 'none'; object-src 'none'; base-uri 'none'"> ``` 6. Add regression tests using payloads containing `<`, `>`, quotes, closing tags, event handlers, and `<script>` elements, and verify that the resulting gallery displays them only as text. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • 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 (3)

Direct Prompt Extraction

High
Category
System Prompt Leakage
Content
p.add_argument("--sleep", type=float, default=0.3, help="Pause between requests (seconds)")
    p.add_argument("--out-dir", default=None)
    p.add_argument("--api-key", default=None)
    p.add_argument("--dry-run", action="store_true", help="Print prompts + exit (no API calls)")
    args = p.parse_args(argv)

    api_key = args.api_key or os.environ.get("OPENAI_API_KEY")
Confidence
85% confidence
Finding
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill invokes a script that uses environment variables and network access to call the OpenAI Images API, but the manifest does not declare any tool scope or permissions. This weakens reviewability and containment because consumers cannot clearly see that the skill can access secrets and send data off-host.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The skill documentation does not warn that user prompts and generated images are transmitted to the OpenAI Images API and that outputs are saved locally. This can lead users to provide sensitive content without understanding the external data flow or the persistence of generated artifacts on disk.

Static analysis

No suspicious patterns detected.