T09 · Insecure Skill Coding Practices
Warning
- Location
- references/sdk-examples.md:72
- Finding
- Overly Broad Token Approvals Violate Least Privilege## Vulnerability Details **File Location**: `references/sdk-examples.md`, lines 72–85 **Vulnerability Type**: Persistent and maximum-value token allowances **Risk Level**: Medium ### Vulnerable Code ```typescript const ERC20_ABI = ['function approve(address,uint256) returns (bool)']; const PERMIT2_ABI = ['function approve(address token, address spender, uint160 amount, uint48 deadline)']; // 1. Approve Permit2 const token0 = new ethers.Contract(currency0, ERC20_ABI, signer); await token0.approve(CONTRACTS.permit2, ethers.MaxUint256); // 2. Approve PositionManager via Permit2 // Permit2's approve() takes uint160 amount — use uint160 max, NOT ethers.MaxUint256 (uint256). // Passing MaxUint256 into a uint160 argument is out-of-range and causes the call to revert. const MaxUint160 = (2n ** 160n) - 1n; const permit2 = new ethers.Contract(CONTRACTS.permit2, PERMIT2_ABI, signer); const deadline = Math.floor(Date.now() / 1000) + 3600; await permit2.approve(currency0, CONTRACTS.positionManager, MaxUint160, deadline); ``` ### Technical Analysis The liquidity example grants Permit2 the maximum possible ERC-20 allowance and then grants PositionManager the maximum Permit2 allowance. Although the Permit2 allowance expires after one hour, the underlying ERC-20 approval granted to Permit2 is unlimited and has no documented expiration or revocation step. These permissions materially exceed the amount required for the demonstrated liquidity operation. This violates least privilege and increases the consequences of a compromised approved contract, incorrect contract configuration, malicious authorization, or vulnerable spending path. The example also does not wait for the ERC-20 approval transaction to be confirmed before submitting the subsequent Permit2 approval, which can lead to unreliable execution, although this is primarily a correctness concern. ### Attack Path 1. A user follows the documented liquidity-provision example. 2. The user approves Permit2 for `ethers ...[truncated 1382 chars]
- Remediation
- ## Remediation Suggestions 1. Replace maximum-value approvals with the exact token amount required for the pending operation: ```typescript const requiredAmount = amount0Max; const approveTx = await token0.approve(CONTRACTS.permit2, requiredAmount); await approveTx.wait(); ``` 2. Restrict the Permit2 allowance to the exact amount needed rather than `MaxUint160`: ```typescript if (requiredAmount > (2n ** 160n) - 1n) { throw new Error('Required amount exceeds Permit2 uint160 allowance range'); } const permit2Tx = await permit2.approve( currency0, CONTRACTS.positionManager, requiredAmount, deadline ); await permit2Tx.wait(); ``` 3. Use the shortest practical deadline and require explicit user confirmation of the token, spender, amount, chain ID, and expiration before signing. 4. Verify that the connected network has chain ID `770` and that every spender address exactly matches the expected deployment before creating approvals. 5. Wait for each approval transaction receipt and verify successful execution before continuing. 6. Add a documented revocation or reset procedure after the operation, such as setting the Permit2 allowance and underlying ERC-20 allowance to zero when they are no longer required. 7. If unlimited approval is retained as an optional gas-saving optimization, do not present it as the default. Clearly disclose that it persists beyond the demonstrated operation and require separate, explicit user consent.
