Skip to content

Add sign-safe — offline signing-time transaction safety gate#4

Open
lrafasouza wants to merge 16 commits into
solanabr:mainfrom
lrafasouza:feat/sign-safe-skill
Open

Add sign-safe — offline signing-time transaction safety gate#4
lrafasouza wants to merge 16 commits into
solanabr:mainfrom
lrafasouza:feat/sign-safe-skill

Conversation

@lrafasouza

@lrafasouza lrafasouza commented Jun 22, 2026

Copy link
Copy Markdown

sign-safe v0.6.4 — offline pre-sign transaction review for Solana agents

Submitting sign-safe for the Solana AI Kit skill bounty.

Problem

Agent wallets increasingly receive transaction bytes from tools, protocols, and other agents. A spending limit or a post-hoc debugger does not protect the signer if the transaction itself silently transfers authority, delegates spend rights, changes ownership, abuses durable-nonce flows, or hides the dangerous action behind a proposal/execution wrapper.

sign-safe answers the pre-sign question: what am I about to authorize?

What it does

An offline, deterministic review gate. It decodes opaque Solana transaction/message bytes (legacy + v0 + Address Lookup Tables), derives writable/signer roles, clear-signs supported program instructions, detects danger primitives, computes signer outflow, and emits a machine-readable verdict — SIGN / HOLD / REJECT — with the CLI exit code mirroring it (0 / 10 / 20). The core has zero runtime dependencies; any RPC work is isolated behind adapters so the engine stays deterministic and testable.

What's new since v0.5

  • Published to npm (sign-safe): npx sign-safe <tx.b64> for humans, npx sign-safe-mcp for agents, plus a library import.
  • Real on-chain attack evidence: the two real Drift Protocol (~$285M, Apr 1 2026) exploit transactions, decoded offline → both HOLD (durable-nonce + Squads v4). See docs/real-attacks.md. Honest framing: HOLD = stop + human review; documented incidents, not "every attack."
  • IDL-cross-verified registry: 69/69 Anchor instruction discriminators checked programmatically against canonical on-chain IDLs.
  • Mutation + fuzz testing: Stryker (70.2% behavioral kill rate) + fast-check — 44,288 single-byte mutations of an authority transfer, 0 ever flip to SIGN (fail-closed, falsifiable).
  • Benign corpus 100 → 500 real mainnet txs; precision 18.4% SIGN / 81.6% HOLD / 0% false-REJECT.

Validation

One line, no clone:

npx sign-safe <tx.b64>

Or reproduce the full suite from the public repo:

git clone https://github.com/lrafasouza/sign-safe-skill
cd sign-safe-skill
npm ci
npm run verify:all
npm run demo:attack-pack

Current evidence at e288e9d:

  • 803 tests across 42 files
  • TypeScript build clean
  • fixture runner: 80 PASS / 0 FAIL
  • attack replay: 37/37 held or rejected before signing; False SIGN: 0, plus 2 real Drift transactions held
  • npm audit --omit=dev: 0 vulnerabilities; zero runtime dependencies
  • public CI green on Node 20 and Node 22: https://github.com/lrafasouza/sign-safe-skill/actions/runs/28447798616

Honest limits

  • SIGN does not mean "all risk is gone"; it means the transaction matches recognized benign patterns under the current policy.
  • The 37/37 malicious result is for the curated replay pack, not a universal detection claim.
  • sign-safe is pre-sign review, not on-chain enforcement. It complements simulation, wallet UX, human review, Squads policies, and runtime spending controls.

Why it fits the bounty

A reusable skill for agent wallets: a deterministic pre-sign review layer that runs offline, is tested exactly, ships on npm, and fails closed into HOLD/REJECT instead of silently approving opaque transaction bytes.

lrafasouza and others added 9 commits June 22, 2026 15:06
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…decode), 238 tests

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
sign-safe v0.4: ALT resolution + Token-2022 mint screening + Squads clear-sign via --rpc,
two-tier REJECT posture + --strict, 12-program registry, real-mainnet precision study. 607 tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
v0.5: simulation, Native Stake, programmatic API + signTransaction/MWA gate + MCP
server, schema (requiresHumanReview/category), durable-nonce asymmetry, Lighthouse
INFO, Marginfi/Squads registry; full adversarial review + 3 fix passes; 728 tests.
bhuvarloka added a commit to bhuvarloka/solana-skill-bounty that referenced this pull request Jun 28, 2026
Engine fixes:
- layout: backtrack the sizeOf `seen` set so sibling fields of the same
  defined type both resolve (was corrupting all later offsets to NaN) (solanabr#1)
- layout: resolve `kind: "type"` aliases to the aliased size (solanabr#9)
- diff: stop flagging newly-added accounts as UNSAFE; report removed
  accounts as UNSAFE instead (solanabr#3)
- diff: typeKey normalizes via typeLabel so Anchor spec spellings of the
  same defined type compare equal — no false retype warnings (solanabr#6)
- diff: detect removed instructions (was silently skipped) (solanabr#8)
- diff: detect error variants inserted before existing ones; appends stay
  safe (added variants were never inspected) (solanabr#7)
- codegen: migration V1/V2 use bare Borsh derives, not #[account], and the
  scaffold preserves the original 8-byte discriminator on read+write (solanabr#2)
- codegen: size realloc from the actual Borsh length + top up rent, instead
  of std::mem::size_of (which mishandles String/Vec and padding) (solanabr#5)
- codegen: minimalIdl keeps the full type table so nested defined types
  resolve in the generated regression test (solanabr#4)

Tests (built to catch, not to pass): the old suite ran every one of these
bugs green because it only tested the first case of each feature. Added the
adversarial second case:
- 5 fixture pairs (added/removed account, removed instruction, mid-inserted
  error, two spec spellings of one defined type) — these feed both the diff
  suite and the offline benchmark
- 2 layout tests asserting exact offsets for sibling same-type fields and
  type aliases

Suite 38/38; benchmark with-skill 100% (16/16), baseline drops to 25% as the
new cases expose failure modes the old corpus couldn't.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
lrafasouza and others added 7 commits June 29, 2026 10:40
Reflects the competitive-analysis release: evaluator Quickstart, reproducible
sample verdicts, Squads HOLD walkthrough, MCP example, failure-recovery doc,
CLI e2e + RPC-adversarial tests; 777 tests / 40 files, verify:all green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Real on-chain Drift attack corpus (held), npm sign-safe, rpc-adversarial coverage,
fuzz + fail-closed fix. 800 tests/42 files, verify:all green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
IDL-verified registry (69/69 ixNames vs canonical IDLs), benign corpus 500
(18.4% SIGN / 81.6% HOLD / 0 false-REJECT), Stryker mutation testing. verify:all green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fix: npx sign-safe bin was inert (exit 0 for every tx); now correct exit codes + regression test. 803 tests/42 files, verify:all green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Updates the pinned sign-safe-skill from v0.6.2 (02b3150) to the current main
(e288e9d): v0.6.4 (npm-published, README install + What's-new) plus the repo
security hardening (CI least-privilege + SHA-pinned actions, Dependabot,
branch protection).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The submission is a submodule pin, so the PR tree showed only an opaque
gitlink. Add a concise README presenting sign-safe — the verdict model,
npx install, real-Drift-attack evidence, test/precision numbers, honest
scope, and links — so a reviewer can evaluate it without clicking through
the submodule. Mirrors the strongest file-based submissions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… output, agent usage)

Enriched the landing README after reviewing peer submissions (solanabr#5 strongest,
solanabr#28 closest competitor): added npm/CI/license badges, a concrete "what it
catches" primitive list, verbatim SIGN/REJECT CLI output, Claude/agent +
MCP + guardedSignTransaction usage, the proof numbers, and honest scope —
so a judge can fully evaluate the skill from the PR without clicking through
the submodule.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant