T09 · Insecure Skill Coding Practices
Warning
- Location
- src/index.js:9
- Finding
- Environment API Key Can Be Exfiltrated Through a Caller-Controlled Base URL<![CDATA[ ## Vulnerability Details **File Location**: `src/index.js:9-11` and `src/index.js:55-62` **Vulnerability Type**: Credential disclosure through an untrusted network destination **Risk Level**: Medium ### Vulnerable Code ```javascript class PortfolioTracker { constructor(options = {}) { this.apiKey = options.apiKey || process.env.PRISM_API_KEY || null; this.baseUrl = options.baseUrl || PRISM_BASE; this.holdings = new Map(); } ``` ```javascript async fetchPrices() { const symbols = Array.from(this.holdings.keys()); if (symbols.length === 0) return { prices: [] }; const url = `${this.baseUrl}/crypto/prices/batch?symbols=${symbols.join(',')}`; const headers = this.apiKey ? { 'X-API-Key': this.apiKey } : {}; const response = await fetch(url, { headers }); ``` ### Technical Analysis The constructor independently accepts a caller-controlled `baseUrl` while automatically reading `PRISM_API_KEY` from the process environment. `fetchPrices()` then sends that credential in the `X-API-Key` header to the configured URL without validating its protocol, hostname, port, or origin. A caller that can influence the constructor options does not need to know or explicitly provide the API key. Supplying only a malicious `baseUrl` is sufficient to cause the Skill to retrieve the credential from the environment and disclose it to the selected endpoint. The custom endpoint behavior is not documented as part of the declared portfolio-tracking functionality. Sending the key to arbitrary origins therefore exceeds the minimum network privilege required to retrieve prices from the documented Prism API. The request also discloses the symbols held in the portfolio through the `symbols` query parameter. Portfolio amounts and cost bases are not included in the network request. ### Attack Path 1. A legitimate application runs with `PRISM_API_KEY` in its environment. 2. An attacker influences configuration or code that constructs `PortfolioTracker`. ...[truncated 1390 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Send the Prism credential only when the destination origin exactly matches the trusted Prism API origin: ```javascript const PRISM_ORIGIN = new URL(PRISM_BASE).origin; const target = new URL('/crypto/prices/batch', this.baseUrl); if (target.origin === PRISM_ORIGIN && this.apiKey) { headers['X-API-Key'] = this.apiKey; } ``` 2. Remove configurable `baseUrl` support if it is not required by the public functionality. 3. If custom endpoints are required, do not implicitly reuse `PRISM_API_KEY`. Require callers to provide an explicit credential associated with that endpoint. 4. Restrict accepted URLs to HTTPS and reject URLs containing embedded credentials, unexpected ports, or malformed hostnames. 5. Where server-side request forgery is relevant, resolve and reject loopback, private, link-local, multicast, and cloud metadata destinations. Revalidate redirects or disable them. 6. Document that token symbols are sent to the selected pricing provider. 7. Add tests verifying that: - `PRISM_API_KEY` is sent to `https://api.prismapi.ai` only. - Custom origins never receive the environment credential. - HTTP and restricted network destinations are rejected. - Redirects cannot transfer credentials to another origin. ]]>
