T09 · Insecure Skill Coding Practices
- Location
scripts/coinpilot_cli.mjs:359- Finding
Primary Wallet Private Key Is Transmitted on Every Coinpilot Request
- Content
View full analysis
{ return withCoinpilotLock(async () => { await throttle(); const primary = getPrimaryWallet(wallets); const baseUrl = getApiBaseUrl(wallets); const url = new URL(route, baseUrl); if (query) { for (const [key, value] of Object.entries(query)) { if (value === undefined || value === "") continue; url.searchParams.set(key, String(value)); } } console.log(`[coinpilot] ${method} ${url.toString()}`); const headers = { "Content-Type": "application/json", "x-api-key": wallets.apiKey, "x-wallet-private-key": primary.privateKey, "x-user-id": wallets.userId, ...extraHeaders, }; const res = await fetch(url.toString(), { method, headers, body: body ? JSON.stringify(body) : undefined, }); ``` ### Technical Analysis The common Coinpilot request function attaches the primary funding wallet's reusable raw private key as the `x-wallet-private-key` header. Because every Coinpilot operation uses this function, the key is transmitted not only for trading operations but also for read-only activities such as lead discovery, subscription listing, history retrieval, and activity inspection. This violates least privilege. Public discovery endpoints should not require wallet authority, and authenticated read operations should use a scoped API credential rather than a private key capable of authorizing blockchain transactions. The destination is constrained to HTTPS origins in a hardcoded allowlist, which reduces arbitrary exfiltration risk. However, that allowlist includes production, development, and staging environments under both `coinpilot.bot` and `coi ...[truncated 1638 chars]- Remediation
View remediation
