T09 · Insecure Skill Coding Practices
- Location
scripts/run_livedoc.mjs:215- Finding
Unvalidated API Base Override Can Exfiltrate Credentials and User Data
- Content
View full analysis
Vulnerability Details
File Location:
scripts/run_livedoc.mjs, lines 57–59, 74–77, 215, and 346–351
Vulnerability Type: Arbitrary API endpoint override and sensitive-data exfiltration
Risk Level: HighVulnerable Code
js async function apiRequest(method, apiPath, body, apiKey, apiBase, timeoutMs) { const url = `${apiBase}/v2${apiPath}`; const headers = { Accept: 'application/json', Authorization: `Bearer ${apiKey}` };js async function uploadFormData(apiPath, formData, apiKey, apiBase, timeoutMs) { const url = `${apiBase}/v2${apiPath}`; const res = await fetchWithRetry( url, { method: 'POST', headers: { Accept: 'application/json', Authorization: `Bearer ${apiKey}` }, body: formData }, timeoutMs, );js const apiBase = (process.env.FELO_API_BASE?.trim() || DEFAULT_API_BASE).replace(/\/$/, '');js const fileBuffer = await fs.readFile(args.file); const blob = new Blob([fileBuffer]); const formData = new FormData(); formData.append('file', blob, path.basename(args.file)); const payload = await uploadFormData(`/livedocs/${shortId}/resources/${endpoint}`, formData, apiKey, apiBase, timeoutMs);Technical Analysis
The script allows the
FELO_API_BASEenvironment variable to replace the intended Felo API origin,https://openapi.felo.ai. The supplied value is used directly to construct request URLs without validating its URL scheme, hostname, port, or origin.All API requests attach
FELO_API_KEYas a bearer credential. Consequently, an arbitrary endpoint selected throughFELO_API_BASEreceives the API key. Depending on the invoked command, it may also receive LiveDoc metadata, text-document contents, retrieval queries, supplied URLs, or complete uploaded files.The override is not documented in
SKILL.mdorREADME.md, and the implementation does not require explicit confirmation before transmitting sensitive information to a non-Felo origin. It also permits plaintext HTTP, which ca ...[truncated 1977 chars]- Remediation
View remediation
Remediation Suggestions
-
Remove the
FELO_API_BASEoverride from production builds and always use:js const apiBase = 'https://openapi.felo.ai'; -
If a development override is operationally necessary, parse and strictly validate it:
js const candidate = new URL( process.env.FELO_API_BASE?.trim() || DEFAULT_API_BASE ); if (candidate.protocol !== 'https:') { throw new Error('FELO_API_BASE must use HTTPS'); } const allowedHosts = new Set(['openapi.felo.ai']); if (!allowedHosts.has(candidate.hostname)) { throw new Error('Untrusted FELO_API_BASE hostname'); } -
Validate the complete origin, including protocol and port, rather than relying only on the hostname. Reject embedded credentials, unexpected ports, fragments, and non-root path prefixes.
-
Never attach
Authorizationwhen the destination origin differs from the approved Felo API origin. Perform this check immediately before every network request, including retries and multipart uploads. -
If non-production endpoints must be supported, require an explicit command-line opt-in, document the behavior clearly, and require separate non-production credentials rather than reusing production API keys.
-
Display the destination origin and require confirmation before sending file contents or document text to any explicitly approved non-production endpoint.
-
Add automated tests confirming that HTTP URLs, lookalike domains, subdomains, altered ports, credential-bearing URLs, and unrelated origins are rejected.
-
Rotate any API key that may already have been used while an untrusted
FELO_API_BASEvalue was present.
-
