T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/ai-cheese.ts:88
- Finding
- Remote Payment Instructions Can Trigger Unrestricted USDC Transfers<![CDATA[ ## Vulnerability Details **File Location**: `scripts/ai-cheese.ts`, lines 88-122; configurable server defined at line 14 **Vulnerability Type**: Insufficient validation of remotely supplied payment instructions **Risk Level**: High ### Vulnerable Code ```ts // Step 1: Get payment requirements console.log(`Sending to ${opts.to}...`); const firstTry = await fetch(`${SERVER}/api/v1/message`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(msg), }); if (firstTry.status === 200) { const result = await firstTry.json(); console.log(`✅ Delivered (no payment needed): ${result.messageId}`); return; } if (firstTry.status !== 402) { const err = await firstTry.json().catch(() => ({})); console.error(`❌ Error ${firstTry.status}: ${err.error || 'Unknown'}`); process.exit(1); } const requirements = await firstTry.json(); const payTo = requirements.accepts[0].payTo; const amount = BigInt(requirements.accepts[0].maxAmountRequired); const amountUsd = Number(amount) / 1e6; console.log(` Payment required: $${amountUsd.toFixed(2)} USDC to ${payTo}`); // Step 2: Pay USDC const usdc = new ethers.Contract(USDC_ADDRESS, USDC_ABI, wallet); console.log(` Sending USDC...`); const tx = await usdc.transfer(payTo, amount); ``` The source of the payment response can also be changed through an environment variable: ```ts const SERVER = process.env.AICHEESE_SERVER || 'https://aicheese.app'; ``` ### Technical Analysis The script treats an HTTP `402` response as sufficient authorization for an irreversible blockchain payment. Both the recipient address (`payTo`) and token amount (`maxAmountRequired`) are taken directly from the remote response and passed to the USDC contract without enforcing a local transaction policy. The implementation does not validate: - Whether `payTo` is a valid and expected recipient for the selected user. - Whether the requested amount mat ...[truncated 2659 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Require an explicit local spending ceiling, such as `--max-payment-usdc`, for every paid message. Reject any response above that ceiling before constructing a transaction. 2. Display the validated recipient, exact amount, network, token contract, and message recipient, then require explicit operator confirmation unless a separately configured automation policy authorizes the payment. 3. Compare the requested payment with the directory price previously retrieved for the selected user. Reject unexplained discrepancies. 4. Validate the complete payment schema before accessing `accepts[0]`, including: - Recipient address format. - Non-negative amount and safe upper bounds. - Expected Base chain identifier. - Exact USDC contract address. - Supported x402 protocol version. - Expected payment recipient binding. 5. Require cryptographic authentication of payment requirements and verify that the authenticated identity is trusted. 6. Restrict `AICHEESE_SERVER` to an explicit allowlist. If custom servers are necessary, require a separate opt-in flag and clear warning that the server can direct payments. 7. Use a dedicated low-balance wallet or a smart account with per-transaction and cumulative spending limits. Do not expose a general-purpose funded wallet to this workflow. 8. Handle post-payment delivery failures explicitly and provide a reconciliation or refund process. Do not imply that payment confirmation guarantees message delivery. 9. Add tests using hostile `402` responses, including excessive amounts, malformed addresses, empty arrays, unexpected chains, wrong assets, and mismatched prices. ]]>
