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
Follow-up to #480 which introduced src/security/canonical-dispatch.ts — the MCP-side mirror of skill v8's Invariant #1.a allowlist — but did NOT wire assertCanonicalDispatchTarget into individual prepare_* handlers.
A correct wiring needs per-flow review to identify which leg(s) of each tx tree carry the canonical-target check. Bundling that review into #480 would have violated the smallest-solution discipline (CLAUDE.md). The helper exists with a regression test that asserts the allowlist stays in sync with src/config/contracts.ts; the wiring is the second-order improvement.
Scope
For each prepare_* tool the helper covers (prepare_aave_*, prepare_compound_*, prepare_lido_stake/_unstake, prepare_morpho_*, prepare_uniswap_swap, prepare_uniswap_v3_*, prepare_eigenlayer_deposit):
Identify the leg of the returned UnsignedTx chain whose to should match the canonical target (typically the action leg, possibly nested under tx.next after an approval).
Call assertCanonicalDispatchTarget(toolName, chain, leg.to) at that point.
Add an integration test that mocks the underlying build* function to return a to outside the allowlist and asserts the prepare flow throws INV_1A.
This MCP-side wiring is defense-in-depth: catches an internal bug or supply-chain tamper that substitutes the target inside a build* function. The skill's defense doesn't go away if this wiring is missing — but the layered defense is the design goal of [security] Add Invariant #1.a — outer-to canonical-contract allowlist #461.
Summary
Follow-up to #480 which introduced
src/security/canonical-dispatch.ts— the MCP-side mirror of skill v8's Invariant #1.a allowlist — but did NOT wireassertCanonicalDispatchTargetinto individualprepare_*handlers.Why deferred from #480
Different prepare flows have different tx-chain shapes:
prepare_lido_stake→ outertois stETH directly)prepare_aave_supply→ outertois the token contract for approval,tx.next.tois the Aave Pool)A correct wiring needs per-flow review to identify which leg(s) of each tx tree carry the canonical-target check. Bundling that review into #480 would have violated the smallest-solution discipline (CLAUDE.md). The helper exists with a regression test that asserts the allowlist stays in sync with
src/config/contracts.ts; the wiring is the second-order improvement.Scope
For each
prepare_*tool the helper covers (prepare_aave_*,prepare_compound_*,prepare_lido_stake/_unstake,prepare_morpho_*,prepare_uniswap_swap,prepare_uniswap_v3_*,prepare_eigenlayer_deposit):UnsignedTxchain whosetoshould match the canonical target (typically the action leg, possibly nested undertx.nextafter an approval).assertCanonicalDispatchTarget(toolName, chain, leg.to)at that point.build*function to return atooutside the allowlist and asserts the prepare flow throwsINV_1A.Defense layering
build*function. The skill's defense doesn't go away if this wiring is missing — but the layered defense is the design goal of [security] Add Invariant #1.a — outer-to canonical-contract allowlist #461.Source
#461, #480.