Back to skill

Security audit

Azure Image Gen

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its image-generation purpose, but it handles an Azure API key in a way that can send it to an unvalidated endpoint and writes unescaped prompt text into a generated gallery.

Install only if you trust and control the skill directory and its .env file. Verify AZURE_OPENAI_ENDPOINT is your real Azure HTTPS endpoint before running, avoid sharing generated galleries from untrusted prompts until HTML escaping is fixed, and treat manifest/gallery files as containing prompt history.

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/generate.py:43
Finding
Azure API Key Disclosure Through an Unrestricted Configurable Endpoint<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate.py`, lines 43–63 **Vulnerability Type**: Credential disclosure through insufficient endpoint validation **Risk Level**: High ### Vulnerable Code ```python url = f"{endpoint}/openai/deployments/{deployment}/images/generations?api-version={api_version}" payload = { "prompt": prompt, "size": size, "quality": quality, "style": style, "n": 1, "response_format": "b64_json" } headers = { "Content-Type": "application/json", "api-key": api_key } data = json.dumps(payload).encode("utf-8") req = urllib.request.Request(url, data=data, headers=headers, method="POST") try: with urllib.request.urlopen(req, timeout=120) as response: ``` ### Technical Analysis The `AZURE_OPENAI_ENDPOINT` value is incorporated directly into the destination URL without validating its scheme, hostname, port, embedded credentials, or ownership. The Azure API key is then attached to the request as the `api-key` header. Although network access and authentication are necessary for the declared Azure image-generation functionality, sending credentials to an arbitrary environment-controlled endpoint exceeds the minimum privileges required. The implementation permits both attacker-controlled hosts and plaintext HTTP destinations. The endpoint is loaded from either the process environment or the project-local `.env` file. Anyone able to influence either source can redirect the authenticated request. This is not evidence that the project intentionally exfiltrates credentials, but it creates a concrete credential-disclosure vulnerability. ### Attack Path 1. An attacker gains the ability to modify the project `.env` file, influence the launch environment, or persuade the user to use a malicious endpoint configuration. 2. The attacker sets `AZURE_OPENAI_ENDPOINT` to an attacker-controlled URL, such as `https://attacker.example`, or to a plaintext HTTP endpoint. 3. The user invokes the image-g ...[truncated 993 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse the endpoint with `urllib.parse.urlsplit` before constructing the request. 2. Require the `https` scheme and reject plaintext HTTP. 3. Reject endpoints containing embedded usernames, passwords, fragments, unexpected query parameters, or unsupported ports. 4. Enforce an explicit hostname allowlist. If appropriate for supported Azure environments, validate against documented Azure OpenAI hostname suffixes while preventing suffix-confusion attacks. 5. Prefer pinning an expected hostname through trusted configuration rather than accepting arbitrary URLs. 6. Resolve configuration from a protected source and ensure `.env` permissions restrict unauthorized modification. 7. Avoid following cross-origin redirects for authenticated requests, or explicitly verify every redirect destination before retaining the credential header. 8. Consider Azure identity-based authentication and narrowly scoped credentials where supported. 9. Add tests confirming rejection of HTTP endpoints, deceptive subdomains, embedded credentials, attacker domains, malformed URLs, and credential-bearing redirects. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/generate.py:142
Finding
Stored HTML Injection in the Generated Image Gallery<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate.py`, lines 142–150 **Vulnerability Type**: Stored HTML injection caused by unescaped prompt data **Risk Level**: Medium ### Vulnerable Code ```python html += f""" <div class="card"> <a href="{img['filename']}" target="_blank"> <img src="{img['filename']}" alt="{img['prompt'][:100]}"> </a> <div class="card-body"> <p class="prompt">{img['prompt']}</p> <p class="meta">{img['size']} • {img['quality']} • {img['style']}</p> </div> </div> """ ``` ### Technical Analysis The gallery generator inserts `img['prompt']` directly into both an HTML attribute and an HTML text context without escaping it. The value can originate from the user-supplied prompt or from the Azure response's `revised_prompt` field. An attacker-controlled value containing quotation marks and HTML markup can terminate the `alt` attribute or inject elements through the paragraph body. When the generated `index.html` is opened, the browser interprets the injected content as markup rather than inert text. The risk is amplified by the unrestricted endpoint issue: a maliciously configured service can return a crafted `revised_prompt`, which is subsequently stored in the generated gallery. ### Attack Path 1. An attacker supplies a crafted prompt, or controls the configured endpoint and returns a crafted `revised_prompt`. 2. The script stores that value in the `images` collection. 3. `create_gallery_html` concatenates the value into `index.html` without context-appropriate escaping. 4. The user opens the generated gallery in a web browser. 5. The injected HTML is parsed and any browser-permitted active content executes in the local gallery's origin context. ### Impact Assessment Exploitation can alter the gallery, display deceptive content, initiate unwanted browser requests, and execute JavaScript where the browser's handling of the local document permits it. The exact capabilities ...[truncated 411 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Escape all dynamic HTML values with context-appropriate encoding. 2. For the current implementation, use `html.escape(value, quote=True)` for prompt text and attribute values. 3. Prefer a template engine with automatic escaping instead of constructing HTML through string concatenation. 4. Treat `revised_prompt` as untrusted network input even when it is returned by the expected service. 5. Keep filenames constrained to script-generated safe values; if future versions accept external filenames, validate or encode those values as well. 6. Add a restrictive Content Security Policy to the generated page as defense in depth, such as disabling scripts and external resource loading where these features are unnecessary. 7. Add regression tests using prompts containing quotation marks, angle brackets, event-handler attributes, and script-like markup, then verify that the generated document displays them only as text. ]]>
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • 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
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (5)

Credential Access

High
Category
Privilege Escalation
Content
def load_env():
    """Load environment variables from .env file if present."""
    env_file = Path(__file__).parent.parent / ".env"
    if env_file.exists():
        with open(env_file) as f:
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
def load_env():
    """Load environment variables from .env file if present."""
    env_file = Path(__file__).parent.parent / ".env"
    if env_file.exists():
        with open(env_file) as f:
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
def load_env():
    """Load environment variables from .env file if present."""
    env_file = Path(__file__).parent.parent / ".env"
    if env_file.exists():
        with open(env_file) as f:
            for line in f:
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill documentation describes capabilities that require access to environment variables, network communication with Azure OpenAI, and file writes, but it does not declare any tool scope or permissions boundary. This creates a transparency and least-privilege problem: an agent or reviewer cannot easily determine what the skill is allowed to access, increasing the risk of overbroad execution and misuse if the implementation is run in a permissive environment.

Indirect Prompt Extraction

Medium
Category
System Prompt Leakage
Content
**404 Not Found**: Verify your `AZURE_OPENAI_DALLE_DEPLOYMENT` name matches exactly

**Content Policy**: Azure has strict content filters. Rephrase prompts that get blocked.
Confidence
75% confidence
Finding
Skill contains patterns that could indirectly extract system prompts through rephrasing, translation, summarization, or side-channel techniques.

Static analysis

No suspicious patterns detected.