Skip to main content

dnr-respond

Incident response workup for a lead in hand — an alert, an IOC, or a /dnr-hunt finding. Scopes the lead across the logs, verdicts whether the attack succeeded, quantifies blast radius, confirms root cause in source with a local PoC, and writes a proposed (never executed) containment/remediation/recovery plan. Use when asked to "respond to this alert", "work this incident", "how bad is this", or "run dnr-respond". /dnr-hunt is the no-alert entry to the same track.

Informations de source

Dépôt
anthropics/defending-code-reference-harness
Dernière activité de la source
16 juillet 2026 à 16:34
Langue détectée de SKILL.md
anglais
Étoiles
7 585
Forks
622

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Explorateur de fichiers
2 fichiers

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
name
dnr-respond
description
Incident response workup for a lead in hand — an alert, an IOC, or a /dnr-hunt finding. Scopes the lead across the logs, verdicts whether the attack succeeded, quantifies blast radius, confirms root cause in source with a local PoC, and writes a proposed (never executed) containment/remediation/recovery plan. Use when asked to "respond to this alert", "work this incident", "how bad is this", or "run dnr-respond". /dnr-hunt is the no-alert entry to the same track.
argument-hint
<lead> [--logs dir] [--repo path] [--fresh]
allowed-tools
["Read","Glob","Grep","Write","Bash(rg:*)","Bash(grep:*)","Bash(awk:*)","Bash(sed:*)","Bash(sort:*)","Bash(uniq:*)","Bash(cut:*)","Bash(head:*)","Bash(tail:*)","Bash(wc:*)","Bash(ls:*)","Bash(du:*)","Bash(file:*)","Bash(jq:*)","Bash(python3:*)","Bash(curl:*)","Bash(kill:*)"]
# dnr-respond Lead-in-hand incident response: take one concrete starting point and answer the four questions an incident commander needs, in order — **what happened, did it succeed, how far did it go, and what do we do about it.** The output is a verdict, a quantified blast radius, a root cause proven in source, and a response plan that is *proposed, never executed*. Invoke with `/dnr-respond <lead> [--logs dir] [--repo path] [--fresh]`. **The lead (`$1`) can be:** - an incident id from the hunt's `INCIDENTS.json` (e.g. `INC-2`) — the `/dnr-hunt` handoff - an alert pasted from an alert queue (JSON line or prose) - an IOC: an IP, an account name, a route, a session identifier - free text ("a customer says their address was quoted in a phishing email") **Other arguments:** - `--logs` = logs directory or dnr target directory (same resolution as `/dnr-hunt`; defaults to `targets/dnrcanary` if it exists, else cwd). - `--repo` = application source (defaults to the target's `app/`). - `--fresh` = ignore any checkpoint in the run dir's `.dnr-respond-state/` and mint a new run dir. ## Hard rules (shared with /dnr-hunt — the track's contract) 1. **Log evidence alone never confirms a vulnerability.** `confirmed_exploited` requires log evidence + the flaw in source + a PoC fired locally this session. Otherwise the ceiling is `suspected`. 2. **Query, don't read.** Never `Read` or `cat` a log file; `wc`/`head`/ `tail` to profile, `grep`/`awk`/`sort`/`uniq`/`python3` to interrogate. Small files (alert queues, error logs under ~200 lines) may be read whole. 3. **Propose, never execute.** No blocking, disabling, rotating, or config changes — in any environment, including the demo app. The plan names actions; humans execute them. 4. **Local only.** PoCs fire at `127.0.0.1` against an instance you started. Never touch a remote host. 5. **Every number is a query result.** "How many records" and "how many customers" get answered by pipelines run this session, never estimated. ## Run directory and checkpointing **First action — establish the run directory**, the same way `/dnr-hunt` does, but as a *consumer*. Let `TARGET` be the basename of the resolved `--logs`/target path (default `targets/dnrcanary`) and `SCOPE` that path. - **Lead is `INC-n`** (the `/dnr-hunt` handoff, or a second lead from a hunt you already started responding to): work the response into the investigation that incident lives in. `{RUN}` is the newest `results/TARGET/<timestamp>/` directory that contains an `INCIDENTS.json` — find it with Bash, e.g. `ls -1d results/TARGET/*/ | sort -r | while read d; do [ -f "$d/INCIDENTS.json" ] && echo "$d" && break; done`. Do **not** call `rundir` for an `INC-n` lead — `rundir` mints a fresh empty dir once a prior `/dnr-respond` in that dir has completed, which would strand the incident's `INCIDENTS.json` in the old dir. Only if no run dir holds an `INCIDENTS.json` (you were handed `INC-n` but never ran the hunt) fall through to the `rundir` line below. - **Any other lead** (alert, IOC, free text): Bash `python3 .claude/skills/_lib/checkpoint.py rundir results/TARGET --state .dnr-respond-state --scope "SCOPE"` (append `--fresh` if it's in `$ARGUMENTS`). This adopts the newest run dir for the target if one exists, else mints a fresh `results/TARGET/<timestamp>/`. That path is `{RUN}`; substitute the literal path everywhere below, and double-quote it (and `SCOPE`) in Bash. Checkpoint mechanics are identical to `/dnr-hunt`, state dir `{RUN}/.dnr-respond-state/`, phases 0–4. When `{RUN}` already holds a **completed** `.dnr-respond-state` from an earlier lead, `load` reports `complete` and the rules below reset it cleanly — a second lead enriches the same `INCIDENTS.json` and writes its own `RESPONSE-<lead>.md`. On start: `python3 .claude/skills/_lib/checkpoint.py load {RUN}/.dnr-respond-state` — absent/complete/`--fresh` → `reset` and start at Phase 0; running with `phase_done == N` → read `phase0.json`…`phaseN.json`, resume at N+1. After each phase: Write payload to `{RUN}/.dnr-respond-state/_chunk.tmp`, then `checkpoint.py save {RUN}/.dnr-respond-state <N> <name> --from {RUN}/.dnr-respond-state/_chunk.tmp`. Never write `progress.json` directly; never pass payload via heredoc/stdin. After outputs: `checkpoint.py done {RUN}/.dnr-respond-state 4`. ## Phase 0 — Parse the lead, locate inputs 1. Resolve `--logs` (dnr target config or bare directory) and inventory log files (`du`, `wc -l`, `head -2` each — identify formats and field positions). The corpus is expected to exist already — the target README covers generating it. 2. Parse the lead into starting entities: - `INC-n` → read that entry from `{RUN}/INCIDENTS.json` (the run dir adopted above); its IPs, accounts, routes, and timeline rows are the seed set. - alert line → source IP, rule, path, timestamp. - IOC → the entity itself. - free text → extract the observables (who reported, what data, when); the seed set may start as just a route or a table name. 3. State the working question in one sentence (e.g. "did 203.0.113.66's activity against /login succeed, and if not, what did?"). Everything in Phase 1 serves that question. Checkpoint `phase0.json`: `{"lead": ..., "lead_type": ..., "entities": {"ips": [], "accounts": [], "routes": [], "windows": []}, "question": ..., "logs_dir": ..., "repo": ..., "files": [...]}` ## Phase 1 — Scope: pull everything the entities touched For each seed entity, extract its complete activity, then expand one ring: - **Per IP:** every request — time-ordered, with route, status, size, UA, and authenticated user. Summarize phases of behavior, don't paste hundreds of lines. - **Per account:** every login (from which IPs), every authenticated request. Compare against that account's historical pattern in the same corpus — new IP, new UA, new volume, new hours are all signal. (A new IP *alone* is baseline noise — users roam; combine it with volume, cadence, or what the session did next before treating it as a lead.) - **Per route:** who else hit it the same way in the window? One attacker found via an alert may not be the only one. - **Ring expansion (once):** entities discovered above join the set — the account an IP logged into, the second IP that touched the same account, the route a session pivoted to. Expand once, deliberately; /dnr-hunt's open-ended loop is the tool when scope keeps growing. - **Correlate the error log and alert queue** rows for all entities into one timeline. Checkpoint `phase1.json`: `{"activity": [{"entity", "summary", "first_seen", "last_seen", "requests", "key_lines": [...]}], "expanded_entities": [...], "timeline": [...]}` ## Phase 2 — Verdict and blast radius Answer the working question with evidence on both sides: - **Did it succeed?** Status codes alone don't answer this — a 401 wall says no; 200s with anomalous sizes, error-then-success progressions, and data-shaped responses say yes. State the verdict and the discriminating evidence. - **Blast radius, quantified.** What data was reached: which routes/tables, how many records, over what time span, belonging to how many distinct users/customers. Each figure comes from a pipeline (count the exfil-page sizes, count the distinct ids walked, join walked ids to owners via the app's own data if available locally — running the target's deterministic `seed_command` early, before Phase 3, is fine). - **Containment status check (read-only).** Is the activity ongoing at the end of the corpus window, or did it stop? When was the last attacker action? - If the verdict is "did not succeed", the workup doesn't end — say what the attacker was *trying* and why it failed; that feeds the plan's hardening section and the `ruled_out` record. Checkpoint `phase2.json`: `{"verdict": ..., "succeeded": bool, "discriminators": [...], "blast_radius": {"data": ..., "records": N, "distinct_victims": N, "window": ...}, "ongoing": bool, "last_activity": ...}` ## Phase 3 — Root cause and PoC Same bar as `/dnr-hunt` Phase 3–4: 1. Read the implicated handler(s) in `--repo`; name the flaw (or the defense) at file/function precision; check sibling routes for the same flaw class. 2. Seed and start the app locally per target config: `seed_command` / `app_command` are target-relative — run them with the target directory as cwd; launch in the background via a single command that prints the PID (the same `python3 -c "...subprocess.Popen..."` pattern /dnr-hunt Phase 4 shows, with the port override env var if the default port is busy). Fire the **minimal** PoC, record command + response excerpt, fire the negative PoC against defended routes, then `kill <pid>` (never by port or pattern). 3. No runnable app → verdict ceiling is `suspected`, stated explicitly. Checkpoint `phase3.json`: `{"root_cause": {"file", "function", "mechanism"}, "poc": {"command", "status", "response_excerpt"}, "negative_poc": {...}, "siblings_checked": [...]}` ## Phase 4 — The response plan (PROPOSED, NEVER EXECUTED) and outputs Write the plan to `{RUN}/RESPONSE.md` — or `{RUN}/RESPONSE-<lead>.md` (e.g. `RESPONSE-INC-2.md`) if a `RESPONSE.md` from a different lead already exists in the run dir; never silently overwrite another incident's plan. Sections, every action carrying **what / why / risk-if-wrong**: 1. **Verdict & summary** — three sentences: what happened, did it succeed, how far it went. 2. **Timeline** — the merged entity timeline from Phase 1. 3. **Containment (proposed)** — block/deny candidates (IPs, sessions, tokens), accounts to suspend, routes to rate-limit or disable. For each: the evidence line that justifies it and what breaks if the call is wrong (e.g. blocking a NAT IP locks out legitimate users behind it). 4. **Eradication & remediation (proposed)** — the code fix, described at file/function level, handed off as a `/triage`-compatible vuln entry; interim mitigations if the fix will take time. 5. **Recovery (proposed)** — exactly which credentials rotate (the queried list, not "all users"), which customers get notified (the distinct-victim list from Phase 2), what monitoring confirms the attacker is out. 6. **Detection engineering (proposed)** — the rules that would have caught this earlier, derived from what you actually used to find it: the discriminating queries from Phase 2 turned into alert logic (e.g. error-rate per source on a route; sequential-id access cadence; account-from-new-IP at volume). Note which existing alerts fired, which were noise, and what that says about the queue. 7. **Open questions** — what this corpus cannot answer (earlier history, other log sources, whether stolen data was used elsewhere). Then update `{RUN}/INCIDENTS.json` (same schema as `/dnr-hunt`; create it if absent): add or enrich this incident — verdict, quantified impact, root cause, PoC, timeline. Extra keys are fine — graders match on content, not structure. Negative outcomes go to `ruled_out`. Mark complete (`checkpoint.py done {RUN}/.dnr-respond-state 4`) and point the user onward (substitute the literal `{RUN}` path): - for the fix, from inside `{RUN}`: `/triage INCIDENTS.json --repo <repo>` then `/patch TRIAGE.json --repo <repo>` — both write to the directory they're invoked from, so running them there lands the verdicts and patches beside the incidents - on a dnr target: `python <target>/grade.py {RUN}/INCIDENTS.json` to self-score (the user runs this) - a human reviews and executes (or rejects) each proposed action; nothing in RESPONSE.md has been done ## Customizing beyond the demo - Feed it your real alert queue one alert at a time — the lead parser takes any JSON or prose alert; the method doesn't change. - Auth logs, CDN/WAF exports, and EDR threat events slot into Phase 1 as additional per-entity activity sources; keep the single merged timeline. - The Phase 4 plan template is the part most worth adapting to your org: map containment to your actual controls (WAF rules, IdP session revocation, feature flags) — still as proposals routed to the owning team, never as actions the agent takes.
Voir sur GitHub