T09 · Insecure Skill Coding Practices
Error
- Location
- miner/src/index.ts:39
- Finding
- AI API credentials can be transmitted over plaintext HTTP<![CDATA[ ## Vulnerability Details **File Location**: `miner/src/index.ts:39-51`, `miner/src/config.ts:50-51`, `miner/src/ai-api.ts:17-22` **Vulnerability Type**: Missing transport-security validation for a credential-bearing endpoint **Risk Level**: High ### Vulnerable Code ```ts // miner/src/index.ts:39-51 if (providerChoice === '2') { aiApiUrl = 'https://openrouter.ai/api/v1/chat/completions'; aiModel = 'x-ai/grok-4.1-fast'; aiApiKey = await ask('? Enter your OpenRouter API key: '); console.log(`\n ✓ Using OpenRouter → model: ${aiModel}`); } else if (providerChoice === '3') { aiApiUrl = await ask('? Enter custom AI API URL: '); aiModel = (await ask('? Enter AI model name (default: grok-4.1-fast): ')) || 'grok-4.1-fast'; aiApiKey = await ask('? Enter your API key: '); } else { aiApiUrl = 'https://api.x.ai/v1/chat/completions'; aiModel = 'grok-4.1-fast'; aiApiKey = await ask('? Enter your xAI API key: '); } ``` ```ts // miner/src/config.ts:50-51 aiApiKey: requireEnv('AI_API_KEY'), aiApiUrl: envOrDefault('AI_API_URL', 'https://api.x.ai/v1/chat/completions'), ``` ```ts // miner/src/ai-api.ts:17-22 const res = await fetch(config.apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${config.apiKey}`, }, ``` ### Technical Analysis The miner supports a user-controlled custom `AI_API_URL` and sends `AI_API_KEY` to that URL in an HTTP `Authorization` header. No validation requires the endpoint to use HTTPS. The project performs an HTTPS check for `ORACLE_URL`, but no equivalent validation is applied to `AI_API_URL`. Consequently, a value such as `http://example.test/v1/chat/completions` is accepted. HTTP does not provide transport confidentiality or server authentication, so any party capable of observing or modifying the connection can recover the Bearer token and alter the API response. This issue does not expose the Ethereum private key because the private key is not included in ...[truncated 1403 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Parse `AI_API_URL` with the standard `URL` class rather than using string-prefix checks. 2. Require the `https:` protocol for every non-loopback endpoint. 3. If local development requires HTTP, permit only explicitly recognized loopback hosts such as `localhost`, `127.0.0.1`, and `[::1]`. 4. Reject malformed URLs, embedded username/password components, and unsupported protocols. 5. Apply the validation both during interactive initialization and when loading environment variables so manually edited configurations cannot bypass it. 6. Consider requiring explicit confirmation before sending credentials to a custom hostname. Example validation: ```ts function validateCredentialEndpoint(rawUrl: string): string { let parsed: URL; try { parsed = new URL(rawUrl); } catch { throw new Error('AI_API_URL must be a valid absolute URL'); } const loopbackHosts = new Set(['localhost', '127.0.0.1', '[::1]']); const isLoopback = loopbackHosts.has(parsed.hostname); if (parsed.protocol !== 'https:' && !(isLoopback && parsed.protocol === 'http:')) { throw new Error( 'AI_API_URL must use HTTPS; HTTP is permitted only for loopback development endpoints' ); } if (parsed.username || parsed.password) { throw new Error('AI_API_URL must not contain embedded credentials'); } return parsed.toString(); } ``` Use the validated value in `loadConfig()` before constructing `MinerConfig`. ]]>
