T09 · Insecure Skill Coding Practices
Error
- Location
- voiceai-vo.cjs:3972
- Finding
- Arbitrary API Base URL Can Exfiltrate the Voice.ai API Key and Script Content<![CDATA[ ## Vulnerability Details **File Location**: `voiceai-vo.cjs:3972-4057` **Vulnerability Type**: Unrestricted credentialed endpoint override **Risk Level**: High ### Vulnerable Code ```js var VoiceAIClient = class { apiKey; mock; baseUrl; constructor(options) { this.apiKey = options.apiKey ?? null; this.mock = options.mock ?? false; this.baseUrl = process.env.VOICEAI_API_BASE ?? BASE_URL; } endpoint(path) { return `${this.baseUrl}/api/${API_VERSION}${path}`; } async listVoices(options) { if (this.mock) return this.mockListVoices(options); if (voiceCache.entry && Date.now() < voiceCache.entry.expiresAt) { return this.filterVoices(voiceCache.entry.data, options); } const params = new URLSearchParams(); if (options?.limit) params.set("limit", String(options.limit)); if (options?.offset) params.set("offset", String(options.offset)); const url = `${this.endpoint("/tts/voices")}?${params.toString()}`; const res = await fetch(url, { headers: { Authorization: `Bearer ${this.apiKey}`, "User-Agent": "voiceai-creator-voiceover-pipeline/0.1.0" } }); if (!res.ok) { const body = await res.text().catch(() => ""); throw new Error(`Voice.ai API error ${res.status}: ${body}`); } const json = await res.json(); const rawVoices = Array.isArray(json) ? json : json.voices ?? []; const voices = rawVoices.map((v) => ({ id: String(v.voice_id ?? v.id ?? ""), name: String(v.name ?? "Unnamed"), language: String(v.language ?? "en"), visibility: String(v.visibility ?? ""), status: String(v.status ?? "") })); const data = { voices, total: voices.length }; voiceCache.entry = { data, expiresAt: Date.now() + CACHE_TTL_MS }; return this.filterVoices(data, options); } async callTtsEndpoint(text, opts) { const body = { text, audio_format: opts.audio_format, language: opts.language, ...[truncated 2974 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Remove `VOICEAI_API_BASE` support from production builds if endpoint customization is not essential. 2. If an override is required, parse it with `new URL()` and enforce: - The `https:` protocol. - An explicit allowlist of trusted Voice.ai hostnames. - Approved ports only. - No embedded username or password. 3. Require an explicit command-line development flag before honoring a custom endpoint. 4. Bind credentials to the expected origin and refuse to attach the Authorization header to any other origin. 5. Configure redirect handling so credentials are never forwarded across origins. Prefer rejecting redirects for authenticated API calls. 6. Fail closed when URL validation fails. 7. Document `VOICEAI_API_BASE` in the Skill metadata and security/privacy documentation because it controls where credentials and scripts are sent. 8. Add automated tests covering HTTP URLs, lookalike domains, embedded credentials, redirects, and attacker-controlled hosts. ]]>
