feat(pair_ledger): mandate per-address device verification in instructions - #660
Merged
Conversation
…tions Closes #577. Update the runtime `instructions` field of every `pair_ledger_*` tool (btc, ltc, tron, solana, live) to require the cooperating agent surface the FULL address (no truncation) and have the user verify it character-by- character against the device's address-display screen before any subsequent `prepare_*` / `send_transaction` may consume the paired address. Defense-in-depth for the cooperating-MCP case. The actual rogue-MCP defense lives skill-side — a rogue MCP fabricates both the address and any advisory text — so the binding rule (forced user-echo-back of device-displayed chars) is filed at vaultpilot-security-skill#42 with an explicit cooperating-agent scope statement. Together they cover Role-B (rogue-MCP + cooperating agent); rogue-agent remains an architectural gap per Rogue-Agent-Only triage. Regression test in test/security-audit.test.ts greps the source for the verification mandate (≥5 hits across `VERIFY BEFORE FIRST USE`, `FULL string|address`, `On any mismatch, abort`) so a future "address paired, you can now send" softening fails CI. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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 #577.
Summary
Update the runtime
instructionsfield of everypair_ledger_*tool (btc, ltc, tron, solana, live) to mandate that the cooperating agent surface the FULL paired address (no truncation) and force the user to verify it character-by-character against the device's address-display screen before any subsequentprepare_*/send_transactionmay consume the paired address.Threat / scope split
Threat: rogue MCP returns an attacker-controlled address from
pair_ledger_*. Caught in matrix-sampled smoke-test batch-3 (newcomer-n192-C.2).Why this is mostly a skill-side fix: the actual rogue-MCP defense is the user reading the Ledger device screen (out-of-band trust anchor outside the MCP boundary) and typing back chars to the agent. A rogue MCP fabricates both the address and any "verification proof" it might emit — MCP-side advisory text alone is structurally circular under rogue-MCP per the project's tautological-mitigation rule. The binding rule has to live skill-side so a cooperating agent drives the user-verification flow even when the MCP omits the advisory.
This MCP-side change is defense-in-depth for the cooperating-MCP case. The skill-side companion is filed at vaultpilot-security-skill#42 with the full Inv #18 rule (force user echo-back of first 6 + last 6 chars from device, refuse on mismatch) and an explicit cooperating-agent scope statement. Together they cover Role-B (rogue-MCP + cooperating agent); rogue-agent remains an architectural gap per the Rogue-Agent-Only Triage rule (skill text is in agent context — a hostile agent ignores it).
Pre-sign gate sweep
Per the project's surface-sweep rule, this change does not modify any
assertTransactionSafeblock. It only adds advisory text topair_ledger_*runtime responses. Noprepare_*paths are gated.Regression test
test/security-audit.test.ts(mirrors the existingConnected Appstopic-cross-check pattern):VERIFY BEFORE FIRST USE(one perpair_ledger_*)FULL string|addressOn any mismatch, abortA future "address paired, you can now send" softening fails CI loudly.
What is intentionally NOT in scope
display=trueon USB pair calls. Adding it would force a device prompt at pair time, but a rogue MCP just doesn't passdisplay=true— UX cost without rogue-MCP benefit. Skipped.verificationRequired: truefield. Same tautological-mitigation argument: rogue MCP omits it. Plain text in theinstructionsfield is enough for the cooperating-MCP case the skill-side rule already covers more rigorously.prepare_*calls. A rogue MCP doesn't enforce the gate on itself. The agent-bound enforcement lives skill-side.Test plan
npm run build(tsc, no errors)npm test(2559 / 2559 tests pass)pair_ledger_btcagainst a real device — confirm the newinstructionstext surfaces in the responsenewcomer-n192-C.2— confirm the verification mandate is what the agent surfaces (cooperating-agent regression check)— Rogue-MCP Trust Boundary (agent-5ade)