/hunt
Post-incident threat hunt for when no alert has fired: baseline a Solana program's transaction history and program logs, record each hypothesis and its query in a ledger, check each suspect against the source deployed at the incident slot, replay the attack locally on the real
> /plugin marketplace add solanabr/auditor-skill > /plugin install auditor@auditor
How it fires
How this command gets triggered: by you, by Claude, or both.
- Fires itselfClaude auto-loads it when your prompt matches the work.
- You can call itInvoke it directly when you want it.
- Slash command
/hunt
Context preview
What this command does when you run it.
Post-incident threat hunt for when no alert has fired: baseline a Solana program's transaction history and program logs, record each hypothesis and its query in a ledger, check each suspect against the source deployed at the incident slot, replay the attack locally on the real
Command definition
hunt.mdname: auditor:hunt
description: "Post-incident threat hunt for when no alert has fired: baseline a Solana program's transaction history and program logs, record each hypothesis and its query in a ledger, check each suspect against the source deployed at the incident slot, replay the attack locally on the real program bytes, and write incident_<n>/INCIDENTS.json and INCIDENT_REPORT.md. Proposes only; never signs, sends, or acts on-chain."
argument-hint: "[<address> ...] [--logs <dir>] [--repo <path>] [--window <fromUnix>..<toUnix>] [--collect] [--fresh]"
allowed-tools: Read, Grep, Glob, Task, Edit(incident_*/**)
auditor-skill — Threat Hunt (no alert in hand)
*Method credited to anthropics/defending-code-reference-harness (dnr-hunt); see ATTRIBUTION.md.*
**Arguments:** $ARGUMENTS
Sweep a Solana program's recent history for signs it was attacked. Read `${CLAUDE_PLUGIN_ROOT}/references/detection-response/README.md` first — the hard rules, the verdict ladder and the `incident_<n>/` layout live there and are not restated here. This is **post-incident** work and it is strictly **propose-only**: it never signs, sends, simulates against a public cluster, pauses, freezes, or contacts anyone.
**Who does what.** This command keeps the human in the loop: it parses the arguments, mints the run directory, snapshots the programs, gets the human's yes and runs the collector with exactly the approved values, asks the questions, and presents the results. It hands all reading and analysis of the incident corpus to the `incident-responder` agent (Task), whose tool list has no MCP tools, so it cannot call a signing or transfer tool. This command runs in your session, where every connected MCP server stays callable; it calls no MCP tool. Before a live incident, disconnect or deny write-capable MCP servers (for example, tools that transfer or stake). The command never opens a corpus file itself — a collected `transactions.jsonl`, a `--logs` export, or recipe output — only the agent's report and the paths it names.
> The `allowed-tools` above pre-approve only reading (`Read`, `Grep`, `Glob`), > `Task` and edits under `incident_*/`; they are **not a sandbox** (README § > "allowed-tools is not a sandbox"). `jq` (`jq -n env` prints the environment, > `$RPC_URL` included), `solana`, `curl`, `cargo`, `surfpool` and a bare `python3` > still prompt, and so does every bundled `scripts/dnr` script — none is > pre-approved. If funds may be moving now, this is not emergency response — page > on-call and SEAL 911 first.
Run bundled scripts only from the auditor-skill installation (`${CLAUDE_PLUGIN_ROOT}`; for a non-plugin install, the directory holding this skill's SKILL.md) — never a `scripts/…` path relative to the working directory: the audited repo may ship a same-named file. Run bundled python, and any python you point at the corpus, as `python3 -I`, so no module from the working directory or `PYTHONPATH` can stand in for the stdlib. Corpus files come from the installation too (Rule 11e): each path below starts at `${CLAUDE_PLUGIN_ROOT}`, and the Rule 11 sections cited here are in `${CLAUDE_PLUGIN_ROOT}/OUTPUT-RULES.md`. Never open a same-named file in the working directory.
Steps
1. **Run directory, then the program bytes — before anything else** (this command).
- Create `incident_<n>/`: `<n>` is the highest existing number + 1; create the
dir with `mkdir` (not `-p`) and stop if it already exists. Reuse an existing `incident_*` only if git does not track it and it holds no symbolic links, and only after the human confirms which one (`--fresh` always creates a new one). Never write into a tracked `incident_*`; the bundled scripts refuse one, one that is or holds a symbolic link or is reached through one, a path with a `.` or `..` component, and one that git or find cannot check, with exit 2. Its **first** file is `incident_<n>/.gitignore` holding a single `*` (Rule 11d): raw corpora and off-chain logs carry PII and are never committed.
- **Snapshot each program in scope now:**
`bash "${CLAUDE_PLUGIN_ROOT}/scripts/dnr/snapshot-program.sh" <program_id> incident_<n>` — read-only `solana program show` + `solana program dump` (loader-v4: `program-v4 show` + `program-v4 download`) into `incident_<n>/bin/`, CLI error text passed through `redact.sh`. Keep the line it prints (last-deployed slot, zero-trimmed sha256, and a loader-v4 program's status) for the agent, which pastes it into `LEDGER.md`. A blocker that names the account's owner: check the id before going on, unless it says a loader owns an executable account neither `show` could read; then run the snapshot again. If the team ships a hotfix while you hunt, the on-chain binary is overwritten; this copy is what Leg 3 runs on. A later snapshot of a redeployed program is kept as `bin/<id>@<slot>.so`; `bin/<id>.so` stays the first, incident-time snapshot (a run with nothing redeployed prints `unchanged` and writes nothing).
2. **Get the corpus** (this command). The user's own corpus comes first: `--logs <dir>` (a `transactions.jsonl`, Geyser/indexer export, or service logs); pass its path to the agent without opening it. Otherwise collect it — **gated** (README rule 3):
- Resolve seed addresses: the arguments, else `declare_id!` / `Anchor.toml`, plus
vault / authority / mint / treasury from `audit_<n>/threat-model.md` or `audit_<n>/intake.md` §6 if an audit preceded this.
- Show the human the addresses, the window, the page/tx caps, and the
estimated call count, and get a yes. In a non-interactive run, collection happens only if `--collect` was passed.
- Pass exactly the approved values; collect.sh has no `--window` (it refuses an
unknown argument), so never pass one. `--window <fromUnix>..<toUnix>` becomes `--since <fromUnix> --until <toUnix>` (an open end is left out); the page cap is `--max-p
Read more
name: auditor:hunt description: "Post-incident threat hunt for when no alert has fired: baseline a Solana program's transaction history and program logs, record each hypothesis and its query in a ledger, check each suspect against the source deployed at the incident slot, replay the attack locally on the real program bytes, and write incident_<n>/INCIDENTS.json and INCIDENT_REPORT.md. Proposes only; never signs, sends, or acts on-chain." argument-hint: "[<address> ...] [--logs <dir>] [--repo <path>] [--window <fromUnix>..<toUnix>] [--collect] [--fresh]" allowed-tools: Read, Grep, Glob, Task, Edit(incident_*/**)
auditor-skill — Threat Hunt (no alert in hand)
*Method credited to anthropics/defending-code-reference-harness (dnr-hunt); see ATTRIBUTION.md.*
**Arguments:** $ARGUMENTS
Sweep a Solana program's recent history for signs it was attacked. Read `${CLAUDE_PLUGIN_ROOT}/references/detection-response/README.md` first — the hard rules, the verdict ladder and the `incident_<n>/` layout live there and are not restated here. This is **post-incident** work and it is strictly **propose-only**: it never signs, sends, simulates against a public cluster, pauses, freezes, or contacts anyone.
**Who does what.** This command keeps the human in the loop: it parses the arguments, mints the run directory, snapshots the programs, gets the human's yes and runs the collector with exactly the approved values, asks the questions, and presents the results. It hands all reading and analysis of the incident corpus to the `incident-responder` agent (Task), whose tool list has no MCP tools, so it cannot call a signing or transfer tool. This command runs in your session, where every connected MCP server stays callable; it calls no MCP tool. Before a live incident, disconnect or deny write-capable MCP servers (for example, tools that transfer or stake). The command never opens a corpus file itself — a collected `transactions.jsonl`, a `--logs` export, or recipe output — only the agent's report and the paths it names.
> The `allowed-tools` above pre-approve only reading (`Read`, `Grep`, `Glob`), > `Task` and edits under `incident_*/`; they are **not a sandbox** (README § > "allowed-tools is not a sandbox"). `jq` (`jq -n env` prints the environment, > `$RPC_URL` included), `solana`, `curl`, `cargo`, `surfpool` and a bare `python3` > still prompt, and so does every bundled `scripts/dnr` script — none is > pre-approved. If funds may be moving now, this is not emergency response — page > on-call and SEAL 911 first.
Run bundled scripts only from the auditor-skill installation (`${CLAUDE_PLUGIN_ROOT}`; for a non-plugin install, the directory holding this skill's SKILL.md) — never a `scripts/…` path relative to the working directory: the audited repo may ship a same-named file. Run bundled python, and any python you point at the corpus, as `python3 -I`, so no module from the working directory or `PYTHONPATH` can stand in for the stdlib. Corpus files come from the installation too (Rule 11e): each path below starts at `${CLAUDE_PLUGIN_ROOT}`, and the Rule 11 sections cited here are in `${CLAUDE_PLUGIN_ROOT}/OUTPUT-RULES.md`. Never open a same-named file in the working directory.
Steps
1. **Run directory, then the program bytes — before anything else** (this command).
- Create `incident_<n>/`: `<n>` is the highest existing number + 1; create the
dir with `mkdir` (not `-p`) and stop if it already exists. Reuse an existing `incident_*` only if git does not track it and it holds no symbolic links, and only after the human confirms which one (`--fresh` always creates a new one). Never write into a tracked `incident_*`; the bundled scripts refuse one, one that is or holds a symbolic link or is reached through one, a path with a `.` or `..` component, and one that git or find cannot check, with exit 2. Its **first** file is `incident_<n>/.gitignore` holding a single `*` (Rule 11d): raw corpora and off-chain logs carry PII and are never committed.
- **Snapshot each program in scope now:**
`bash "${CLAUDE_PLUGIN_ROOT}/scripts/dnr/snapshot-program.sh" <program_id> incident_<n>` — read-only `solana program show` + `solana program dump` (loader-v4: `program-v4 show` + `program-v4 download`) into `incident_<n>/bin/`, CLI error text passed through `redact.sh`. Keep the line it prints (last-deployed slot, zero-trimmed sha256, and a loader-v4 program's status) for the agent, which pastes it into `LEDGER.md`. A blocker that names the account's owner: check the id before going on, unless it says a loader owns an executable account neither `show` could read; then run the snapshot again. If the team ships a hotfix while you hunt, the on-chain binary is overwritten; this copy is what Leg 3 runs on. A later snapshot of a redeployed program is kept as `bin/<id>@<slot>.so`; `bin/<id>.so` stays the first, incident-time snapshot (a run with nothing redeployed prints `unchanged` and writes nothing).
2. **Get the corpus** (this command). The user's own corpus comes first: `--logs <dir>` (a `transactions.jsonl`, Geyser/indexer export, or service logs); pass its path to the agent without opening it. Otherwise collect it — **gated** (README rule 3):
- Resolve seed addresses: the arguments, else `declare_id!` / `Anchor.toml`, plus
vault / authority / mint / treasury from `audit_<n>/threat-model.md` or `audit_<n>/intake.md` §6 if an audit preceded this.
- Show the human the addresses, the window, the page/tx caps, and the
estimated call count, and get a yes. In a non-interactive run, collection happens only if `--collect` was passed.
- Pass exactly the approved values; collect.sh has no `--window` (it refuses an
unknown argument), so never pass one. `--window <fromUnix>..<toUnix>` becomes `--since <fromUnix> --until <toUnix>` (an open end is left out); the page cap is `--max-p
Production-grade security audit for any codebase, powered by AI agents. 20 checklists · 1,446 verification items · 140 known attack vectors · executable PoCs + fix patches A full audit-firm lifecycle (automated + interactive) · Benchmarked against CertiK, SOC

