T09 · Insecure Skill Coding Practices
- Location
scripts/extract_menu.py:39- Finding
Arbitrary Local File Disclosure to an External API
- Content
View full analysis
Vulnerability Details
File Location:
scripts/extract_menu.py, lines 39–47 and 63–73
Vulnerability Type: Unrestricted local file read followed by external transmission
Risk Level: HighVulnerable Code
python ext = os.path.splitext(image_path)[1].lower() mime = "image/png" if ext == ".png" else "image/jpeg" with open(image_path, "rb") as f: img_b64 = base64.b64encode(f.read()).decode() payload = { "model": MODEL, "messages": [ { "role": "user", "content": [ { "type": "image_url", "image_url": {"url": f"data:{mime};base64,{img_b64}"}, }, {"type": "text", "text": PROMPT}, ], } ], "max_tokens": 3000, } req = urllib.request.Request( API_URL, data=json.dumps(payload).encode(), headers={ "Authorization": f"Bearer {api_key}", "Content-Type": "application/json", }, ) with urllib.request.urlopen(req, timeout=120) as resp: result = json.loads(resp.read())Technical Analysis
The script accepts a caller-controlled path and reads the referenced file in full without verifying that it is a supported image. It does not validate the extension against an allowlist, inspect image magic bytes, use an image decoder to confirm the format, require a regular file, enforce the documented 5 MB limit, or restrict access to an approved upload directory.
Any file whose extension is not
.pngis automatically labeled asimage/jpeg. Its complete contents are Base64-encoded and embedded in a request sent tohttps://openrouter.ai/api/v1/chat/completions. Base64 encoding is a normal requirement for the declared vision API and is not, by itself, evidence of covert obfuscation. The security flaw is that unrestricted local content can reach that encoding and network-transmission path....[truncated 1794 chars]
- Remediation
View remediation
Remediation Suggestions
- Permit only explicitly supported extensions:
.png,.jpg, and.jpeg; reject all others. - Validate file signatures and decode the file with a trusted image library before transmission. Do not rely solely on the extension.
- Resolve the path to its canonical form and restrict inputs to an approved user-upload or workspace directory.
- Require the target to be a regular file and reject directories, devices, sockets, and unexpected symbolic links.
- Enforce the documented 5 MB maximum before reading or encoding the file, and use bounded reads to mitigate race conditions and resource exhaustion.
- Determine the MIME type from successfully decoded content rather than defaulting every non-PNG input to JPEG.
- Clearly notify the user that the selected image will be sent to OpenRouter and obtain explicit consent where appropriate.
- Minimize process filesystem permissions so the Skill cannot read credentials or unrelated private files.
- Consider copying validated uploads into a dedicated, permission-restricted staging directory before processing.
- Add tests confirming that unsupported formats, forged extensions, oversized files, special files, symlinks escaping the approved directory, and paths outside the approved workspace are rejected before any network request occurs.
- Permit only explicitly supported extensions:
