T09 · Insecure Skill Coding Practices
Warning
- Location
- src/index.ts:486
- Finding
- Incomplete Preflight Validation for Financial Transactions<![CDATA[ ## Vulnerability Details **File Location**: `src/index.ts:486-523` and `src/index.ts:577-610` **Vulnerability Type**: Incomplete transaction validation and weak confirmation binding **Risk Level**: Medium ### Vulnerable Code Minting performs only limited state checks before signing and broadcasting the transfer: ```ts // Look up token config const tokens = await getTableRows(rpcEndpoint, { code: XMD_TREASURY, scope: XMD_TREASURY, table: 'tokens', limit: 50, }); const token = tokens.find((t: any) => { const parsed = parseExtSym(t.symbol); return parsed?.symbol === sym; }); if (!token) { const available = tokens.map((t: any) => parseExtSym(t.symbol)?.symbol).filter(Boolean); return { error: `Token "${sym}" not supported. Available: ${available.join(', ')}` }; } if (!token.isMintEnabled) { return { error: `Minting with ${sym} is currently disabled` }; } const parsed = parseExtSym(token.symbol); if (!parsed) return { error: 'Could not parse token symbol' }; // Check treasury is not paused const globals = await getTableRows(rpcEndpoint, { code: XMD_TREASURY, scope: XMD_TREASURY, table: 'xmdglobals', limit: 1, }); if (globals.length > 0 && globals[0].isPaused) { return { error: 'XMD treasury is currently paused' }; } const quantity = formatAsset(amount, parsed.precision, parsed.symbol); const { api: eosApi, account, permission } = await getXmdSession(); const result = await eosApi.transact({ actions: [{ account: parsed.contract, name: 'transfer', authorization: [{ actor: account, permission }], data: { from: account, to: XMD_TREASURY, quantity, memo: 'mint', }, }], }, { blocksBehind: 3, expireSeconds: 30 }); ``` The redeem path has the same weakness: ```ts // Validate the target collateral exists and redeem is enabled const tokens = await getTableRows(rpcEndpoint, { code: XMD_TREASURY, scope: XMD_TREASURY, table: 'tokens', limit: 50, }); const token = tokens.find((t: any) => { ...[truncated 3823 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Perform an atomic preflight immediately before signing: - Fetch `xmdglobals`. - Reject when the treasury is paused. - Fetch the selected collateral configuration. - Verify mint or redeem status. - Fetch the current aggregate oracle price. - Enforce `minOraclePrice`. - For minting, calculate and enforce the projected treasury percentage. - Fetch and verify the user's token balance. - For redemption, verify treasury liquidity for the requested collateral. 2. Generate a canonical transaction preview containing: - Source account and permission. - Token contract. - Asset and exact fixed-point quantity. - Destination. - Memo. - Current oracle price. - Fees. - Expected output. - Minimum acceptable output or slippage bound. - Expiration time. 3. Bind confirmation to that exact preview using a nonce or hash. Do not treat a generic Boolean supplied alongside transaction parameters as sufficient confirmation. 4. Re-fetch time-sensitive state after confirmation and abort if the price, fee, cap, enabled status, destination, memo, or quantity differs from the approved preview. 5. Parse amounts as decimal strings or integer smallest units rather than JavaScript floating-point numbers. Reject values with excessive precision, non-finite values, or values outside configured limits. 6. Add configurable per-transaction and cumulative amount limits. Require elevated confirmation for unusually large transactions. 7. Preserve contract-side validation as defense in depth rather than using it as the only enforcement mechanism. ]]>
