T09 · Insecure Skill Coding Practices
- Location
src/sdk.js:36- Finding
API Key Forwarding to Arbitrary or Plaintext Endpoints
- Content
View full analysis
Vulnerability Details
File Location:
src/sdk.js:36-38, 54-65, 71-77
Vulnerability Type: Unrestricted credential forwarding
Risk Level: MediumVulnerable Code
js export function createPodfetcherClient(options = {}) { const baseUrl = options.baseUrl || process.env.PODFETCHER_BASE_URL || DEFAULT_BASE_URL; const apiKey = options.apiKey ?? process.env.PODFETCHER_API_KEY; const apiKeyHeader = options.apiKeyHeader || process.env.PODFETCHER_API_KEY_HEADER || DEFAULT_API_KEY_HEADER; const timeoutMs = parsePositiveInt(options.timeoutMs, DEFAULT_TIMEOUT_MS);js const method = requestOptions.method || "GET"; const url = new URL(path, ensureTrailingSlash(baseUrl)); appendQuery(url, requestOptions.query || {}); const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeoutMs); const headers = { Accept: "application/json", [apiKeyHeader]: apiKey, };js response = await fetch(url, { method, headers, body: requestOptions.body === undefined ? undefined : JSON.stringify(requestOptions.body), signal: controller.signal, });Technical Analysis
Sending the Podfetcher API key to the default
https://api.podfetcher.comendpoint is necessary for the Skill's declared podcast-search and transcript-fetching functionality. The credential handling therefore does not, by itself, constitute covert exfiltration.However, the client accepts an arbitrary
baseUrlthrough SDK options,PODFETCHER_BASE_URL, or the CLI/MCP--base-urloption. It does not validate the destination hostname or require HTTPS before attaching the API key header. As a result, the production credential may be forwarded to an attacker-controlled origin or transmitted over plaintext HTTP.The behavior exceeds minimum privilege because a credential intended for Podfetcher is automatically delegated to any configured network destination without an explicit unsafe-development opt-in or origin ...[truncated 2006 chars]
- Remediation
View remediation
Remediation Suggestions
- Require the
https:protocol for all remote endpoints. - Allow plaintext HTTP only for explicitly recognized loopback development destinations, such as
localhost,127.0.0.1, or::1, and only after an explicit opt-in. - Default to an origin allowlist containing
https://api.podfetcher.com. - Require a clearly named option such as
allowCredentialForwardingToCustomOriginbefore attaching an API key to any non-default origin. - Reject URLs containing embedded username/password credentials.
- Consider requiring separate non-production credentials whenever a custom endpoint is configured.
- Warn users prominently that changing
PODFETCHER_BASE_URLchanges the recipient of the API key. - Add tests confirming that credentials are not sent to HTTP endpoints or unapproved origins.
Example hardening logic:
js const url = new URL(baseUrl); const isDefaultOrigin = url.origin === "https://api.podfetcher.com"; const isLoopback = url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "::1"; if (url.protocol !== "https:" && !isLoopback) { throw new Error("Refusing to send credentials over a non-HTTPS connection"); } if (!isDefaultOrigin && !options.allowCredentialForwardingToCustomOrigin) { throw new Error( "Explicit approval is required before forwarding an API key to a custom origin", ); }Additionally, discourage use of
--api-keybecause command-line arguments may appear in process listings or shell history. Prefer environment variables, protected credential stores, or secure standard-input mechanisms.- Require the
