Back to skill

Security audit

中关村两院每周菜单识别

Security checks for vulnerabilities and agentic risk

Overview

The skill is meant to send cafeteria menu images to OpenRouter, but its script can send any readable local file path without validating that it is really an image.

Install only if you are comfortable sending menu images to OpenRouter and can control what file path the agent passes to the script. Avoid using it on images containing people, contact details, badges, or private information, and prefer a version that validates PNG/JPEG files, enforces the 5 MB limit, and restricts inputs to an upload/workspace directory.

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

T09 · Insecure Skill Coding Practices

Error
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: High

Vulnerable 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 .png is automatically labeled as image/jpeg. Its complete contents are Base64-encoded and embedded in a request sent to https://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

  1. Permit only explicitly supported extensions: .png, .jpg, and .jpeg; reject all others.
  2. Validate file signatures and decode the file with a trusted image library before transmission. Do not rely solely on the extension.
  3. Resolve the path to its canonical form and restrict inputs to an approved user-upload or workspace directory.
  4. Require the target to be a regular file and reject directories, devices, sockets, and unexpected symbolic links.
  5. Enforce the documented 5 MB maximum before reading or encoding the file, and use bounded reads to mitigate race conditions and resource exhaustion.
  6. Determine the MIME type from successfully decoded content rather than defaulting every non-PNG input to JPEG.
  7. Clearly notify the user that the selected image will be sent to OpenRouter and obtain explicit consent where appropriate.
  8. Minimize process filesystem permissions so the Skill cannot read credentials or unrelated private files.
  9. Consider copying validated uploads into a dedicated, permission-restricted staging directory before processing.
  10. 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.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (4)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill declares that it requires an API key and, by design, sends image data to a remote model provider, but it does not explicitly declare tool scope or allowed tools. This creates a governance and sandboxing gap: an agent or platform may permit broader environment and network access than users or reviewers expect, increasing the risk of unintended data access or exfiltration.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The description says the skill extracts menu content from an image but does not warn that the user-provided image will be transmitted to OpenRouter for remote processing. This is a real privacy and consent issue because cafeteria images may contain incidental personal or sensitive information, and users are not given clear notice before third-party disclosure.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
83% confidence
Finding

The title and output examples are entirely in Chinese, and the skill description does not say that users may choose another language or that the skill is intentionally limited to a Chinese-only environment. This can violate language or locale policy when a skill imposes a specific language without opt-in or documented justification.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The embedded natural-language prompt is entirely in Chinese and instructs the model to produce structured Markdown in Chinese, with no option for user language preference or opt-in. This creates a language/locale policy issue because the skill imposes a specific language regardless of user needs or environment.

Content

No source excerpt is available for this finding.