T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/compound.ts:198
- Finding
- Untrusted Odos API Transactions Are Signed Without Sufficient Validation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/compound.ts:198-221, 383-407`; `scripts/test-swap.ts:89-110, 166-188` **Vulnerability Type**: Arbitrary transaction signing from an untrusted remote response **Risk Level**: High ### Vulnerable Code ```ts async function assembleOdosTransaction( pathId: string, userAddress: Address ): Promise<OdosAssembleResponse | null> { const response = await rateLimitedFetch('https://api.odos.xyz/sor/assemble', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ userAddr: userAddress, pathId, simulate: false, }), }); if (!response.ok) { console.log(` ⚠️ Odos assemble failed: ${response.status}`); return null; } const data = await response.json() as OdosAssembleResponse; if (!data.transaction?.to || !data.transaction?.data) { console.log(` ⚠️ Invalid Odos assemble response`); return null; } return data; } ``` ```ts const assembled = await assembleOdosTransaction(quote.pathId, account.address); if (!assembled) { console.log(` ⚠️ Could not assemble transaction, skipping ${token.symbol}\n`); continue; } const gasEstimate = BigInt(assembled.transaction.gas); const gasWithBuffer = gasEstimate + (gasEstimate * 50n / 100n); const nonce = await getFreshNonce(publicClient, account.address); const swapHash = await walletClient.sendTransaction({ to: assembled.transaction.to as Address, data: assembled.transaction.data as Hex, value: BigInt(assembled.transaction.value), gas: gasWithBuffer, nonce, }); ``` ### Technical Analysis The Odos assembly API controls the transaction destination, calldata, native token value, and gas estimate. Validation only verifies that the `to` and `data` properties exist. The implementation does not: - Require `transaction.to` to equal the known `ODOS_ROUTER`. - Decode and validate the returned calldata. - Verify the input token, input amount, output token, recip ...[truncated 1499 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Require the returned destination to exactly match the verified Odos router address for Base. 2. Reject nonzero native value unless it is explicitly required and bounded by the operation. 3. Decode the calldata and verify: - Function selector. - Input token and exact maximum input amount. - Output token. - Recipient. - Minimum output. - Deadline. 4. Simulate the complete assembled transaction locally using the configured RPC before signing. 5. Compare the assembly response against the original quote and reject inconsistent values. 6. Enforce balance-delta checks after execution. 7. Require explicit confirmation for material swaps and establish configurable per-transaction and daily limits. 8. Consider constructing the router call locally rather than signing opaque API-provided calldata. ]]>
