T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/main.mjs:6
- Finding
- Unvalidated API Base URL Enables Credential and Media Disclosure<![CDATA[ ## Vulnerability Details **File Location**: `scripts/main.mjs`, lines 6 and 37-50 **Vulnerability Type**: Unvalidated destination for sensitive authenticated uploads **Risk Level**: High ### Vulnerable Code ```js const API_BASE = process.env.CRAZYROUTER_BASE_URL || "https://crazyrouter.com/v1"; ``` ```js const apiKey = getApiKey(); const fileBuffer = await readFile(args.input); const fileName = path.basename(args.input); console.error(`Transcribing: ${fileName} (${(fileBuffer.length / 1024).toFixed(1)}KB)`); console.error(`Model: ${args.model}${args.translate ? " (translate to English)" : ""}`); const endpoint = args.translate ? "translations" : "transcriptions"; const formData = new FormData(); formData.append("file", new Blob([fileBuffer]), fileName); formData.append("model", args.model); if (args.language) formData.append("language", args.language); const response = await fetch(`${API_BASE}/audio/${endpoint}`, { method: "POST", headers: { "Authorization": `Bearer ${apiKey}` }, body: formData, }); ``` ### Technical Analysis The script permits `CRAZYROUTER_BASE_URL` to control the origin receiving transcription requests. The value is used without validating its protocol, hostname, port, or trust relationship. Every request to the configured origin contains: - The `CRAZYROUTER_API_KEY` bearer credential in the `Authorization` header. - The complete contents of the user-selected audio or video file. - The original file's basename. - The requested model and optional language metadata. Although `SKILL.md` describes uploading media to `https://crazyrouter.com`, it does not document this destination override. If an attacker can influence the process environment, the attacker can set the base URL to a server they control. A normal transcription invocation will then disclose the credential and media directly to that server. This is an insecure trust-boundary implementation: sensitive authentication data and privat ...[truncated 1717 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Remove support for `CRAZYROUTER_BASE_URL` if changing the service destination is not a required feature: ```js const API_BASE = "https://crazyrouter.com/v1"; ``` 2. If an override is operationally necessary, validate it before processing any file or obtaining the API key: ```js const allowedOrigins = new Set(["https://crazyrouter.com"]); const apiUrl = new URL( process.env.CRAZYROUTER_BASE_URL || "https://crazyrouter.com/v1" ); if ( apiUrl.protocol !== "https:" || !allowedOrigins.has(apiUrl.origin) || apiUrl.username || apiUrl.password ) { throw new Error("Invalid or untrusted Crazyrouter API base URL"); } ``` 3. Enforce an explicit allowlist of trusted HTTPS origins rather than accepting arbitrary hostnames, IP addresses, protocols, or ports. 4. Reject URLs containing embedded credentials, fragments, unexpected ports, or ambiguous hostname representations. 5. Disable automatic redirects or validate every redirect destination. Never forward the bearer credential or media body to a different origin. 6. Document every supported configuration variable in `SKILL.md`, including its security implications. 7. Use a narrowly scoped API key where the provider supports scoped credentials, and rotate the key immediately if destination manipulation may already have occurred. 8. Validate the destination before reading the media file so invalid configuration fails without unnecessarily loading sensitive content. ]]>
