T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/check-status.mjs:29
- Finding
- Bearer Credential Disclosure Through an Unrestricted Custom API Endpoint<![CDATA[ ## Vulnerability Details **File Location**: `scripts/check-status.mjs:29-58, 76`; related invocation guidance in `SKILL.md:78-81, 139, 199-234` and `SETUP.md:117-130` **Vulnerability Type**: Bearer credential exfiltration through insufficient destination validation **Risk Level**: High ### Vulnerable Code ```javascript const requestId = args.id const baseUrl = (args['base-url'] || '').replace(/\/+$/, '') const apiPath = args['api-path'] || '/api/v1' if (!requestId || !baseUrl) { process.stdout.write(JSON.stringify({ action: 'error', error: 'missing_args', message: '--id and --base-url are required', }) + '\n') process.exit(1) } const statusUrl = `${baseUrl}${apiPath}/research/${requestId}` const outputUrl = `${baseUrl}${apiPath}/research/${requestId}/output` async function main() { const headers = {} // Read API key from environment (injected by OpenClaw, never from CLI args) const apiKey = process.env.AIRESEARCHOS_API_KEY if (apiKey && apiPath === '/api/v1') { headers['Authorization'] = `Bearer ${apiKey}` } // Check status const statusResponse = await fetch(statusUrl, { headers }) ``` The same authorization headers are also reused for the report request: ```javascript const outputResponse = await fetch(outputUrl, { headers }) ``` The Skill instructions apply the same configurable origin to authenticated operations: ```bash curl -s -X POST "${AIRESEARCHOS_BASE_URL:-https://airesearchos.com}/api/v1/research" \ -H "Authorization: Bearer $AIRESEARCHOS_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query":"<USER_QUERY>","mode":"<MODE>","reportLength":"standard","skipClarifyingQuestions":false}' ``` ### Technical Analysis The status checker accepts an arbitrary `--base-url`, constructs request URLs from it, and attaches `AIRESEARCHOS_API_KEY` whenever `--api-path` equals `/api/v1`. It does not enforce HTTPS, validate the hostname, reject embedded URL credentials, or restrict the destination to A ...[truncated 1676 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Default to an exact allowlist containing only `https://airesearchos.com`. 2. Parse destinations with `new URL()` and reject: - Protocols other than HTTPS. - Embedded usernames or passwords. - Unexpected ports. - Unapproved hostnames. - Malformed or ambiguous URLs. 3. Do not forward the primary AIresearchOS key to custom origins. Require a separate, explicitly named credential for each custom endpoint. 4. Require explicit user approval before enabling a custom authenticated origin and display the normalized destination without displaying the credential. 5. Disable automatic redirects or validate every redirect target and prohibit cross-origin redirects when authorization headers are present. 6. Restrict `apiPath` to an explicit enumeration such as `/api/v1` or `/api/x402`; do not accept arbitrary path strings. 7. Apply the same endpoint policy to every authenticated `curl` command documented in `SKILL.md`. 8. Add automated tests confirming that credentials are never transmitted to HTTP URLs, unapproved origins, or redirect targets. ]]>
