T09 · Insecure Skill Coding Practices
- Location
scripts/main.mjs:6- Finding
Unvalidated API endpoint override can expose credentials and image data
- Content
View full analysis
Vulnerability Details
File Location:
scripts/main.mjs, lines 6 and 46–55
Vulnerability Type: Unvalidated sensitive-data transmission endpoint
Risk Level: MediumVulnerable Code
js const API_BASE = process.env.CRAZYROUTER_BASE_URL || "https://crazyrouter.com/v1";js const response = await fetch(`${API_BASE}/chat/completions`, { method: "POST", headers: { "Authorization": `Bearer ${apiKey}`, "Content-Type": "application/json" }, body: JSON.stringify({ model: args.model, messages: [{ role: "user", content: [ { type: "text", text: args.prompt }, { type: "image_url", image_url: { url: `data:${mimeType};base64,${base64}` } }, ]}], max_tokens: 4096, }), });Technical Analysis
The script allows
CRAZYROUTER_BASE_URLto replace the expected Crazyrouter API endpoint without validating its scheme or hostname. It then sends theCRAZYROUTER_API_KEYas a bearer credential, the user's prompt, and the entire Base64-encoded image to that endpoint.Sending an image and prompt to the default Crazyrouter service is necessary for the declared cloud OCR functionality. However, transmitting the same sensitive material to an arbitrary, undocumented endpoint exceeds the minimum privileges and network scope required by the Skill. Because HTTPS is not enforced, the override can also specify a plaintext HTTP destination.
Exploitation requires control over, or influence on, the process environment. This could arise from a poisoned shell profile, compromised launcher, CI configuration, container environment, or unsafe user configuration.
Attack Path
- An attacker causes
CRAZYROUTER_BASE_URLto be set to an attacker-controlled URL, such ashttps://attacker.example/v1or an insecure HTTP endpoint. - The user invokes the Skill on a private document, screenshot, receipt, or other image.
- The script reads and Base64-encodes the complete image.
- The script sends the image, prompt, selected model, ...[truncated 769 chars]
- An attacker causes
- Remediation
View remediation
Remediation Suggestions
-
Remove support for
CRAZYROUTER_BASE_URLif custom endpoints are not required, and use a fixed HTTPS endpoint:js const API_BASE = "https://crazyrouter.com/v1"; -
If endpoint customization is required, parse and validate the URL before reading the image or retrieving the API key:
js const apiUrl = new URL( process.env.CRAZYROUTER_BASE_URL || "https://crazyrouter.com/v1" ); if (apiUrl.protocol !== "https:") { throw new Error("The API endpoint must use HTTPS"); } const allowedHosts = new Set(["crazyrouter.com"]); if (!allowedHosts.has(apiUrl.hostname)) { throw new Error("Untrusted API endpoint"); } -
Maintain an explicit allowlist of trusted service hostnames. Do not rely on suffix checks that could accept attacker-controlled domains.
-
Document any supported endpoint override in
SKILL.md, including the fact that the API key, prompt, and full image are transmitted to that destination. -
If non-default endpoints must be supported, display the resolved destination and require explicit user approval before transmitting image data or credentials.
-
Avoid sending the Crazyrouter credential to third-party endpoints. Custom providers should use separate, provider-specific credentials with narrowly scoped permissions.
-
Ensure error handling does not print response bodies from untrusted endpoints when those bodies may contain reflected secrets or sensitive submitted content.
-
