T09 · Insecure Skill Coding Practices
- Location
src/index.ts:65- Finding
Ambiguous Amount Parsing Can Cause Transfers Far Larger Than Requested
- Content
View full analysis
{ let decimals = TOKEN_DECIMALS[token.toUpperCase()]; // For contract addresses not in TOKEN_DECIMALS, try on-chain lookup if (decimals === undefined && kit && token.startsWith('0x') && token.length === 42) { decimals = await kit.fetchTokenDecimals(token); } decimals = decimals ?? 18; // Contains a decimal point => definitely human-readable if (amount.includes('.')) { return parseUnits(amount, decimals); } // Pure integer: if it's very large (>10 digits), assume it's already in wei. // This handles backward compatibility for callers passing raw wei. if (/^\d+$/.test(amount) && amount.length > 10) { return BigInt(amount); } // Short integer like "1", "100" => treat as human-readable return parseUnits(amount, decimals); } ``` The runtime manifest describes affected parameters as raw units, for example: ```json "amount": { "type": "string", "description": "Amount in wei" } ``` The parser is used by asset-moving operations such as: ```typescript async send_tokens({ to, amount }: { to: string; amount: string }) { const kit = getClankerKit(); const amountWei = await parseHumanAmount(amount, 'MON', kit); const result = await kit.send(to as Address, amountWei); ``` ### Technical Analysis The public interface and implementation assign different meanings to integer amount strings. The manifest instructs an Agent to supply wei or the token's smallest unit, while `parseHumanAmount` treats any integer containing ten or fewer digits as a human-readable whole-token amount. For an asset with 18 decimals, the manifest-compatible input ` ...[truncated 2074 chars]- Remediation
View remediation
