T09 · Insecure Skill Coding Practices
- Location
- lib/client.mjs:39
- Finding
- Authentication Requests Can Be Redirected to an Arbitrary Network Destination<![CDATA[ ## Vulnerability Details **File Location**: `lib/client.mjs:39-41, 47-57, 86-103, 131-132` **Vulnerability Type**: Unrestricted authentication endpoint configuration **Risk Level**: Medium ### Complete Code Snippet ```js const defaults = JSON.parse(readFileSync(resolve(__dirname, 'defaults.json'), 'utf-8')); export const BASE = process.env.AICOIN_BASE_URL || 'https://open.aicoin.com'; export const KEY = process.env.AICOIN_ACCESS_KEY_ID || defaults.accessKeyId; const SECRET = process.env.AICOIN_ACCESS_SECRET || defaults.accessSecret; export const USING_OWN_KEY = !!(process.env.AICOIN_ACCESS_KEY_ID && process.env.AICOIN_ACCESS_SECRET); // HMAC-SHA1(signStr, secret) → hex → base64. The 4 values ride in X-Aic-* headers. function authHeaders(keyId = KEY, secret = SECRET) { const nonce = randomBytes(8).toString('hex'); const ts = Math.floor(Date.now() / 1000).toString(); const signStr = `AccessKeyId=${keyId}&SignatureNonce=${nonce}&Timestamp=${ts}`; const hex = createHmac('sha1', secret).update(signStr).digest('hex'); return { 'X-Aic-AccessKey-Id': keyId, 'X-Aic-Signature-Nonce': nonce, 'X-Aic-Timestamp': ts, 'X-Aic-Signature': Buffer.from(hex).toString('base64'), }; } // Core request. Returns { httpStatus, body }; body is the parsed envelope. export async function request(method, path, params = {}) { const full = normalizePath(path); const m = (method || 'GET').toUpperCase(); const headers = authHeaders(); let url = `${BASE}${full}`; const init = { method: m, headers, signal: AbortSignal.timeout(30000) }; if (m === 'GET' || m === 'DELETE') { const qs = new URLSearchParams(); for (const [k, v] of Object.entries(params || {})) { if (v === undefined || v === null || v === '') continue; qs.set(k, Array.isArray(v) ? v.join(',') : String(v)); } const s = qs.toString(); if (s) url += `?${s}`; } else { headers['Content-Type'] = 'application/json'; init.body = JSON.stringify(para ...[truncated 2471 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Pin the API origin to `https://open.aicoin.com` unless alternate origins are an explicit functional requirement. 2. If configurability is required, parse the value with `new URL()` and enforce: - The `https:` protocol. - An explicit allowlist of trusted hostnames. - No embedded username or password. - An expected port. 3. Reject malformed URLs and fail closed rather than silently using an untrusted destination. 4. Review redirect handling and ensure authentication headers are never forwarded to a different origin. 5. Separate test configuration from production configuration so test endpoints cannot be enabled through an ordinary workspace `.env`. 6. Document the exact network destination and data sent by the Skill. ]]>
