feat(pre-sign): approve-allowlist as security recommendation (closes #617) - #618
Merged
Conversation
…not a hard refusal The pre-sign approve-allowlist (Aave Pool, Compound Comet, Morpho Blue, Lido Queue, EigenLayer, Uniswap NPM, Uniswap SwapRouter02, LiFi Diamond) hard-refused at preview/send time for any other spender. This blocked legitimate flows like prepare_curve_swap (steth_to_eth), whose approval target — the canonical Curve stETH/ETH pool 0xDC24316b9AE028F1497c275EB9192a3Ea0f67022 — sits outside the curated set despite being a 5+ year immutable contract. Mirror the existing `acknowledgeNonProtocolTarget` pattern: - New optional `acknowledgedNonAllowlistedSpender?: boolean` on UnsignedTx, flowed through the server-minted handle (agent-fabrication-resistant). - assertTransactionSafe: when an approve(spender) hits a non-allowlisted spender AND the flag is stamped, skip ONLY the spender refusal — every other defense (chainId, simulation, payload-hash, ABI-selector check, transfer-on-unknown-token) stays active. - prepare_curve_swap (steth_to_eth) requires the user-passed `acknowledgeNonAllowlistedSpender: true` schema gate; without it, the prepare path fails fast with a clear error so the agent surfaces the trade-off to the user before re-trying. With it, the approval tx carries the flag plus a "⚠ ADVISORY: spender NOT in allowlist" description so the prepare receipt makes the user-side decision visible. Tests: - 2 new pre-sign-check cases (acked-bypass accepts; un-acked still refuses). - 2 new curve-v1 cases (steth_to_eth refuses without ack; eth_to_steth ignores the ack since no approval is built); existing happy-path test updated to pass the ack and assert the stamped flag + advisory. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Collaborator
Author
|
Skill-side companion piece filed: vaultpilot-security-skill#26 — Inv #11 expansion to add a skill-maintained approve-spender allowlist + |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #617.
Summary
acknowledgedNonAllowlistedSpender?: booleanflag onUnsignedTx, flowed through the server-minted handle so the agent cannot fabricate it on a tx that didn't come through a prepare path that explicitly accepted the schema gate.assertTransactionSafe: whenapprove(spender)targets a non-allowlisted spender AND the flag is stamped, skip ONLY the spender-allowlist refusal. Every other pre-sign defense (chainId, simulation, payload-hash, ABI-selector check, transfer-on-unknown-token) stays active.prepare_curve_swap(steth_to_ethdirection) takes the user's schema-enforcedacknowledgeNonAllowlistedSpender: true. Without it, fail fast with a clear error so the agent surfaces the trade-off to the user before retry. With it, the approval tx carries the flag plus a⚠ ADVISORY: spender NOT in allowlistdescription so the prepare receipt makes the user-side decision visible.Mirrors the existing
acknowledgeNonProtocolTarget(prepare_custom_call) andsafeTxOrigin(prepare_safe_tx_*) patterns.Test plan
steth_to_ethrefuses without ack;eth_to_stethignores the ack since no approval is built.prepare_curve_swap→preview_send→send_transactionflow against a real Ledger to confirm the ack flag flows through end-to-end and the on-device clear-sign readsapprove(<curve-pool>, <amount>).🤖 Generated with Claude Code