Back to skill

Security audit

AI Image Generation

Security checks for vulnerabilities and agentic risk

Overview

The skill is an image tool, but its edit and variation functions can upload any local file path the agent can read to OpenAI without clear limits or confirmation.

Review before installing. Use this only in an environment where the agent cannot read sensitive files, and do not pass arbitrary or untrusted file paths to the edit or variation tools. The skill should ideally be updated to disclose uploads, require explicit confirmation, restrict inputs to approved image files, validate file contents, and fix multipart authentication.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
Findings (2)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
ai_image.py:33
Finding

Unrestricted Local File Read and External Upload via Image Tools

Content
View full analysis

Vulnerability Details

File Location: ai_image.py, lines 33–59
Vulnerability Type: Arbitrary local file disclosure through unrestricted path input
Risk Level: High

Vulnerable Code

python
async def edit_image(self, image_path: str, prompt: str) -> Dict[str, Any]:
    import requests
    if not requests:
        return {"success": False, "error": "requests not available"}
    
    url = "https://api.openai.com/v1/images/edits"
    with open(image_path, "rb") as img:
        files = {"image": img}
        data = {"prompt": prompt}
        try:
            loop = asyncio.get_event_loop()
            resp = await loop.run_in_executor(None, lambda: requests.post(url, files=files, data=data, timeout=60))
            if resp.status_code == 200:
                return {"success": True, "url": resp.json().get("data", [{}])[0].get("url")}
            return {"success": False, "error": "Error"}
        except Exception as e:
            return {"success": False, "error": str(e)}

async def variations(self, image_path: str, n: int = 1) -> Dict[str, Any]:
    import requests
    if not requests:
        return {"success": False, "error": "requests not available"}
    
    url = "https://api.openai.com/v1/images/variations"
    with open(image_path, "rb") as img:
        files = {"image": img}
        data = {"n": n}
        try:
            loop = asyncio.get_event_loop()
            resp = await loop.run_in_executor(None, lambda: requests.post(url, files=files, data=data, timeout=60))

Technical Analysis

The public ai_edit_image and ai_image_variations tools accept an arbitrary filesystem path from the caller. That path is passed directly to open(image_path, "rb"), and the resulting content is attached to an outbound HTTP request.

The implementation does not:

  • Restrict access to a user-approved media directory.
  • Canonicalize the path and verify that it ...[truncated 1814 chars]
Remediation
View remediation

Remediation Suggestions

  1. Restrict file inputs to an explicitly configured, user-approved image directory.
  2. Resolve the requested path with Path.resolve() and verify that the resolved path is a descendant of the approved directory.
  3. Reject symbolic links, non-regular files, device files, and paths containing traversal outside the approved root.
  4. Validate content by decoding it with a trusted image library rather than relying on the filename extension or MIME type.
  5. Enforce conservative file-size and image-dimension limits before loading or uploading content.
  6. Require explicit user confirmation identifying the resolved file and external destination before transmission.
  7. Prefer opaque file handles or platform-provided attachment identifiers over caller-controlled filesystem paths.
  8. Run the Skill under a sandboxed account with access only to the workspace and approved media files.
  9. Add tests covering absolute paths, .. traversal, symlinks, non-image files, oversized files, and paths outside the permitted directory.

T09 · Insecure Skill Coding Practices

Warning
Location
ai_image.py:42
Finding

Authentication Header Omitted from Image Edit and Variation Requests

Content
View full analysis

Vulnerability Details

File Location: ai_image.py, lines 42 and 59
Vulnerability Type: Incorrect authentication handling for sensitive outbound requests
Risk Level: Medium

Vulnerable Code

python
resp = await loop.run_in_executor(
    None,
    lambda: requests.post(
        url,
        files=files,
        data=data,
        timeout=60
    )
)

The same unauthenticated request pattern is used by both edit_image and variations. By contrast, the generation operation supplies the client's configured headers:

python
resp = await loop.run_in_executor(
    None,
    lambda: requests.post(
        url,
        headers=self.headers,
        json=payload,
        timeout=60
    )
)

Technical Analysis

AIImageClient constructs an authorization header containing the configured OpenAI API key:

python
self.headers = {
    "Authorization": f"Bearer {api_key}",
    "Content-Type": "application/json"
}

However, the edit and variation methods do not pass an authorization header to requests.post. These calls are therefore expected to be rejected by the OpenAI API while still initiating an outbound request containing the selected local file.

This is primarily an insecure implementation and reliability defect. It also aggravates the file-disclosure issue because sensitive file content may be transmitted in requests that cannot complete their declared operation.

Multipart requests must not reuse the hardcoded JSON Content-Type header, because requests needs to generate a multipart boundary. Authorization should therefore be separated from content-type handling.

Attack Path

  1. A user or attacker invokes ai_edit_image or ai_image_variations with a local file.
  2. The Skill opens the file and constructs a multipart upload.
  3. It sends the request to the declared OpenAI endpoint without the required bearer token.
  4. The request ...[truncated 674 chars]
Remediation
View remediation

Remediation Suggestions

  1. Pass an authorization-only header to multipart requests:
python
auth_headers = {"Authorization": f"Bearer {self.api_key}"}
requests.post(
    url,
    headers=auth_headers,
    files=files,
    data=data,
    timeout=60
)
  1. Do not manually set Content-Type: application/json for multipart uploads; allow requests to generate the correct multipart content type and boundary.
  2. Apply the path restrictions, image validation, size limits, and user-confirmation controls described for the arbitrary file upload vulnerability before enabling authenticated uploads.
  3. Use resp.raise_for_status() or structured status handling and preserve safe API error details for diagnosis.
  4. Add integration tests verifying that all API operations authenticate correctly and that rejected requests do not retry or upload files unnecessarily.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (12)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
96% confidence
Finding

The skill declares use of an environment variable and, by description, performs outbound API calls, but it does not declare any explicit tool scope such as permissions or allowed-tools. That mismatch weakens reviewability and enforcement, making it easier for a skill to access secrets and the network without clear operator consent or policy restriction.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · ai_image.py (reported line 19)May include surrounding context.

python
if not requests:
            return {"success": False, "error": "requests not available"}
        
        url = "https://api.openai.com/v1/images/generations"
        payload = {"prompt": prompt, "model": model, "size": size, "n": 1}
        try:
            loop = asyncio.get_event_loop()

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · ai_image.py (reported line 23)May include surrounding context.

python
payload = {"prompt": prompt, "model": model, "size": size, "n": 1}
        try:
            loop = asyncio.get_event_loop()
            resp = await loop.run_in_executor(None, lambda: requests.post(url, headers=self.headers, json=payload, timeout=60))
            data = resp.json()
            if resp.status_code == 200:
                return {"success": True, "url": data.get("data", [{}])[0].get("url")}

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The skill manifest describes image generation, but the implementation also reads arbitrary local files from user-supplied paths for edit/variation operations. In an agent environment, this expands capability from prompt submission to local file access and can expose sensitive local images or other reachable files to external services if the path is attacker-influenced.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
90% confidence
Finding

This external transmission is more sensitive because it involves uploading local file contents, not just prompts, to a third-party service. Combined with arbitrary path input and limited disclosure, this increases the risk of unintended data exfiltration from the host environment.

Content

Scanner excerpt · ai_image.py (reported line 36)May include surrounding context.

python
if not requests:
            return {"success": False, "error": "requests not available"}
        
        url = "https://api.openai.com/v1/images/edits"
        with open(image_path, "rb") as img:
            files = {"image": img}
            data = {"prompt": prompt}

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The edit function uploads a locally read file to an external API without any built-in notice, confirmation, or disclosure. In agent contexts, users may believe they are performing a local transformation, while the code actually transmits file contents off-system, creating privacy and data-handling risk.

Content

No source excerpt is available for this finding.

Tainted flow: 'data' from requests.post (line 24, network input) → requests.post (network output)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · ai_image.py (reported line 42)May include surrounding context.

python
data = {"prompt": prompt}
            try:
                loop = asyncio.get_event_loop()
                resp = await loop.run_in_executor(None, lambda: requests.post(url, files=files, data=data, timeout=60))
                if resp.status_code == 200:
                    return {"success": True, "url": resp.json().get("data", [{}])[0].get("url")}
                return {"success": False, "error": "Error"}

Tainted flow: 'data' from requests.post (line 24, network input) → requests.post (network output)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · ai_image.py (reported line 60)May include surrounding context.

python
data = {"prompt": prompt}
            try:
                loop = asyncio.get_event_loop()
                resp = await loop.run_in_executor(None, lambda: requests.post(url, files=files, data=data, timeout=60))
                if resp.status_code == 200:
                    return {"success": True, "url": resp.json().get("data", [{}])[0].get("url")}
                return {"success": False, "error": "Error"}

External Transmission

Medium
Category
Data Exfiltration
Confidence
90% confidence
Finding

As with the edit endpoint, this call sends local image data to an external provider. In the context of an agent skill, the danger is elevated because the manifest emphasizes image generation, while the code can also export local files, creating a meaningful privacy boundary crossing.

Content

Scanner excerpt · ai_image.py (reported line 54)May include surrounding context.

python
if not requests:
            return {"success": False, "error": "requests not available"}
        
        url = "https://api.openai.com/v1/images/variations"
        with open(image_path, "rb") as img:
            files = {"image": img}
            data = {"n": n}

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The variation function similarly reads a local file and sends it to a third-party API with no user-facing warning in code or tool schema. This can lead to unintended exfiltration of sensitive images when the caller does not understand that a local path causes remote upload.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The tool descriptions are generic and provide no clear constraints on when the agent should invoke them, which increases the risk of inappropriate or overly eager tool use. In an image-generation skill, unconstrained activation can cause unintended API calls, prompt forwarding of sensitive user content, or editing/variation actions on arbitrary local image paths without sufficient user confirmation.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
78% confidence
Finding

The file tells users to set the OPENAI_API_KEY environment variable but does not include any warning about handling credentials carefully or the privacy implications of transmitting prompts/images to an external AI service. For markdown files, safety-relevant behaviors that could affect privacy or system integrity should be disclosed to the user.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.