You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Adversarial scripts 126 (Permit2 batch), 127 (CowSwap order), 128 (EIP-2612 permit) currently fail to land only because vaultpilot-mcp deliberately excludes typed-data signing today (the WC namespace + Safe.approveHash design). This is defense-by-gap, not defense-by-design.
The moment a prepare_eip2612_permit / sign_typed_data_v4 / prepare_cowswap_order / prepare_permit2_* ships without paired Invariant #1b and Invariant #2b, a rogue MCP swaps spender inside Permit{...} and the existing skill catches nothing:
Hash recompute alone passes tautologically (the digest is over the tampered tree).
The asymmetry is enormous: ONE permit signature → perpetual transfer authority for the lifetime of the deadline. Permit2 batch with 5-year expiration on USDT (script 126) is the worst case.
Proposal — block coordinated release
Treat shipping the typed-data tool surface and shipping #1b+#2b as a single coordinated release.
Invariant #1b (typed-data tree decode):
Decode the typed-data tree locally: domain, types, primaryType, message.
Walk types, locate every address-typed field (spender, to, receiver, verifyingContract), surface each in CHECKS PERFORMED with bold + inline-code markup.
Surface deadline / validTo / expiration with delta-from-now in human-readable units; flag if > 90 days.
Pin verifyingContract against curated map (Permit2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3, USDC permit domain, CowSwap settlement, etc.); refuse on mismatch.
Invariant #2b (digest recompute): independently recompute keccak256("\x19\x01" || domainSeparator || hashStruct(message)) from the decoded tree and match against MCP-reported digest.
Hard precondition: Ledger device must clear-sign the typed-data type for the target token. If the device blind-signs the digest, the agent must refuse.
Summary
Adversarial scripts 126 (Permit2 batch), 127 (CowSwap order), 128 (EIP-2612 permit) currently fail to land only because vaultpilot-mcp deliberately excludes typed-data signing today (the WC namespace + Safe.approveHash design). This is defense-by-gap, not defense-by-design.
The moment a
prepare_eip2612_permit/sign_typed_data_v4/prepare_cowswap_order/prepare_permit2_*ships without paired Invariant #1b and Invariant #2b, a rogue MCP swapsspenderinsidePermit{...}and the existing skill catches nothing:permit(...)reference is for the on-chain submission selector, not the upstream typed-data signing where the attack lands.Proposal — block coordinated release
Treat shipping the typed-data tool surface and shipping #1b+#2b as a single coordinated release.
Invariant #1b (typed-data tree decode):
domain,types,primaryType,message.types, locate every address-typed field (spender,to,receiver,verifyingContract), surface each in CHECKS PERFORMED with bold + inline-code markup.deadline/validTo/expirationwith delta-from-now in human-readable units; flag if > 90 days.verifyingContractagainst curated map (Permit2 =0x000000000022D473030F116dDEE9F6B43aC78BA3, USDC permit domain, CowSwap settlement, etc.); refuse on mismatch.primaryType∈{Permit, PermitSingle, PermitBatch, Order}, treat as approval-class — apply Inv v0.2.3: document approval→wait→main-tx pattern to all agents #11 unlimited / long-lived rules per entry.Invariant #2b (digest recompute): independently recompute
keccak256("\x19\x01" || domainSeparator || hashStruct(message))from the decoded tree and match against MCP-reported digest.Hard precondition: Ledger device must clear-sign the typed-data type for the target token. If the device blind-signs the digest, the agent must refuse.
Source
Adversarial smoke-test, 2026-04-28. Scripts 126, 127, 128.
🤖 Generated with Claude Code