AntFleet

Anatomy · d121d4bf-1

Unlimited token allowance (MAX_UINT256) to third-party router is a security risk

mediumsecurity
repo a16d2030·PR #4·reviewed 1 week ago

The vulnerable code

symbiosis/scripts/symbiosis-swap.py:18-19

Code snippet unavailable.

The reasoning

Opus

to_smallest_units mishandles empty integer part and signs, producing wrong on-chain amounts

highbughigh
  • symbiosis/scripts/symbiosis-quote.py:18-23
  • symbiosis/scripts/symbiosis-swap.py:22-27
If a user passes `.1` (no leading 0) the integer part is the empty string, then `int(integer + frac)` becomes `int("100000000000000000")` which works, but if integer is empty and frac is also empty (e.g. amount=".") this raises ValueError with a non-user-friendly traceback. More importantly, the function silently truncates the fractional component past `decimals` digits without any warning (e.g., 0.1234567 USDC with 6 decimals becomes 0.123456, dropping value), and it silently accepts arbitrary garbage like '1e3', '1,5', '-1', etc. A negative amount '-1' would be coerced to int('-1...') and sent to the API as a negative smallest-units string, which is then signed by the user; while the API will likely reject it, there is no input validation in the script. Combined with no docs warning, this is a real correctness/data-loss risk because the swap amount the user typed is not what gets sent.

Recommendation

Validate `amount` matches a positive decimal regex; reject negatives and non-numeric values; raise a clear error if the user provides more fractional digits than `decimals` (or at least warn). Also handle the empty-integer case explicitly.

GPT-5

Unconditional unlimited ERC20 approve may revert on tokens requiring allowance reset to zero first (e.g., USDT)

highbughigh
  • symbiosis/scripts/symbiosis-swap.py:18-19
  • symbiosis/scripts/symbiosis-swap.py:145-156
Many ERC20 tokens (notably USDT on some chains) require setting allowance to 0 before changing it from a non-zero value. This code always tries to set allowance directly to MAX_UINT256 with a single approve call, which will revert on such tokens if an existing non-zero allowance is present. The script will then exit with an error, blocking swaps for affected tokens.

Recommendation

Implement a safe-approve flow: - Check current allowance (allowance(owner, spender)); if < required amount, then: - First call approve(spender, 0) - Then approve(spender, exact required amount) or a reasonable cap. If reading allowance is not available, conservatively send two approvals: approve(spender, 0) followed by approve(spender, amount). Alternatively, integrate Permit/Permit2 when available.

The agreement

Both frontier models flagged this within the same line range. AntFleet's unanimous gate fired — the finding posted on the PR.

Closure

Tweet thread template

tweet 1 of 8167 / 280

Two frontier models reviewed PR #4 on a16d2030. Both found this bug: medium security: Unlimited token allowance (MAX_UINT256) to third-party router is a security risk

tweet 2 of 8127 / 280

The vulnerable code (symbiosis/scripts/symbiosis-swap.py:18-19): (full snippet at https://www.antfleet.dev/anatomy/d121d4bf-1)

tweet 3 of 8280 / 280

What Opus saw: "If a user passes `.1` (no leading 0) the integer part is the empty string, then `int(integer + frac)` becomes `int("100000000000000000")` which works, but if integer is empty and frac is also empty (e.g. amount=".") this raises ValueError with a non-user-friendl…

tweet 4 of 8280 / 280

What GPT-5 saw: "Many ERC20 tokens (notably USDT on some chains) require setting allowance to 0 before changing it from a non-zero value. This code always tries to set allowance directly to MAX_UINT256 with a single approve call, which will revert on such tokens if an existing …

tweet 5 of 897 / 280

Both flagged the same line range. AntFleet's unanimous gate fired — the finding posted on the PR.

tweet 6 of 893 / 280

The fix landed in commit pending: (view diff at https://www.antfleet.dev/anatomy/d121d4bf-1)

tweet 7 of 881 / 280

AntFleet reviews every PR with two frontier models. Only unanimous findings post.

tweet 8 of 877 / 280

Full anatomy + reasoning + diffs: https://www.antfleet.dev/anatomy/d121d4bf-1

Paste into X composer one tweet at a time. X has no multi-tweet intent API.

medium security: Unlimited token allowance (MAX_UINT256) to third-party rout… — AntFleet anatomy