T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/main.ts:170
- Finding
- API Bearer Token Can Be Disclosed to an Arbitrary Result URL<![CDATA[ ## Vulnerability Details **File Location**: `scripts/main.ts:170-203` **Duplicate Location**: `scripts/vendor/shared-image-generation/scripts/main.ts:170-203` **Vulnerability Type**: Credential disclosure through an untrusted network destination **Risk Level**: High ### Vulnerable Code ```ts async function fetchImageBytes(url: string, quiet: boolean): Promise<FetchResult> { let lastErr = ""; const transientStatus = (status: number) => status === 408 || status === 429 || status === 502 || status === 503 || status === 504 || (status >= 520 && status <= 599); const transientNetwork = (e: unknown) => { const name = e instanceof Error ? e.name : ""; const msg = e instanceof Error ? e.message : String(e); return ( name === "AbortError" || msg.includes("timeout") || msg.includes("Timeout") || msg.includes("fetch failed") || msg.includes("ECONNRESET") || msg.includes("ETIMEDOUT") || msg.includes("EAI_AGAIN") ); }; for (let attempt = 1; attempt <= DOWNLOAD_MAX_ATTEMPTS; attempt++) { let res: Response | null = null; let threw: unknown = null; for (const useAuth of [false, true] as const) { try { const init: RequestInit = { signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS) }; if (useAuth) init.headers = { Authorization: `Bearer ${getApiKey()}` }; res = await fetch(url, init); threw = null; if (res.ok) break; lastErr = `HTTP ${res.status}`; if (!useAuth && (res.status === 401 || res.status === 403)) continue; break; } catch (e) { threw = e; res = null; lastErr = e instanceof Error ? e.message : String(e); if (!useAuth) continue; break; } } ``` ### Technical Analysis The image download URL is obtained from the gateway's task response and passed directly to `fetchImageBytes`. The code first makes an unauthenticated request and then ...[truncated 1920 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Never send the API credential to arbitrary result URLs. 2. Parse URLs with `new URL(url)` and require `protocol === "https:"`. 3. Maintain an explicit allowlist of WeryAI-controlled API and CDN hostnames. 4. Send authorization only when the exact origin is approved and protected downloads genuinely require it. 5. Disable automatic redirects or validate every redirect target before following it. 6. Do not retry general network exceptions with credentials. 7. Reject loopback, private, link-local, and metadata-service addresses after DNS resolution where feasible. 8. Apply the same correction to the duplicated vendored implementation. 9. Add tests proving that attacker-controlled hosts never receive an `Authorization` header. ]]>
