T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/casino.js:14
- Finding
- Unvalidated Remote Transaction Data Can Induce Malicious Real-Money Transactions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/casino.js:5`, `scripts/casino.js:14-28`, `scripts/casino.js:70-135`; `SKILL.md:51-54`, `SKILL.md:108-115` **Vulnerability Type**: Trusting unvalidated transaction targets and calldata supplied by a remote API **Risk Level**: High ### Vulnerable Code ```javascript const BASE_URL = process.env.CASINO_URL || 'https://casino.lemomo.xyz'; ``` ```javascript async function request(method, path, body) { const opts = { method, headers: { 'Content-Type': 'application/json' }, }; if (body) opts.body = JSON.stringify(body); const res = await fetch(`${BASE_URL}${path}`, opts); const data = await res.json(); if (!res.ok) { console.error(`Error ${res.status}: ${data.error || JSON.stringify(data)}`); process.exit(1); } return data; } ``` The returned transaction fields are printed without validation: ```javascript case 'deposit': { if (!address || !amount) { console.error('--address and --amount required'); process.exit(1); } const d = await request('POST', '/deposit', { address, amount }); console.log(`Needs approval: ${d.needsApproval}`); console.log('Transactions to sign:'); d.transactions.forEach((tx, i) => { console.log(` ${i + 1}. ${tx.description}`); console.log(` to: ${tx.to}`); console.log(` data: ${tx.data}`); }); break; } case 'withdraw': { if (!amount) { console.error('--amount required'); process.exit(1); } const d = await request('POST', '/withdraw', { amount }); console.log(`${d.transaction.description}`); console.log(` to: ${d.transaction.to}`); console.log(` data: ${d.transaction.data}`); break; } ``` The documented workflow encourages signing the returned data: ```text 1. Deposit: POST /deposit → sign & send approve + deposit txs 2. Create: POST /create → sign & send createGame tx (save salt!) ``` ### Technical Analysis The remote service controls the transaction destination and calldata displayed to the ...[truncated 2204 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Pin the expected Base Mainnet chain ID and reject requests intended for any other chain. 2. Pin the documented CasinoRouter, RPSGame, and USDC addresses in trusted local code. 3. ABI-decode every returned transaction before it is displayed or handed to a wallet. 4. Enforce an allowlist of operation-specific destination addresses and function selectors. 5. Validate all decoded arguments, including: - Approval spender and exact or narrowly bounded approval amount. - Deposit and withdrawal amount. - Game ID, choice, commitment, and stake. - Recipient and player address. - Zero native-currency value where appropriate. 6. Reject unknown contracts, selectors, extra transactions, malformed calldata, and unexpected response fields. 7. Prefer constructing calldata locally from pinned, audited ABIs instead of accepting opaque transaction data from the API. 8. Display a human-readable, locally decoded transaction summary and require explicit confirmation for approvals and transfers. 9. Avoid unlimited token approvals. If approval is required, restrict it to the exact intended amount. 10. Treat `CASINO_URL` overrides as unsafe development functionality, or require an explicit warning and trusted-host allowlist. ]]>
