
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.
- Installs
- 224
- Last 24h
- —
- Trending rank
- —
- Security
- Not audited
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/anthropics/defending-code-reference-harness --skill dnr-respond- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads dnr-respond automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
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-hunthandoff - 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 totargets/dnrcanaryif it exists, else cwd).--repo= application source (defaults to the target'sapp/).--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)
- Log evidence alone never confirms a vulnerability.
confirmed_exploitedrequires log evidence + the flaw in source + a PoC fired locally this session. Otherwise the ceiling issuspected. - Query, don't read. Never
Readorcata log file;wc/head/tailto profile,grep/awk/sort/uniq/python3to interrogate. Small files (alert queues, error logs under ~200 lines) may be read whole. - Propose, never execute. No blocking, disabling, rotating, or config changes — in any environment, including the demo app. The plan names actions; humans execute them.
- Local only. PoCs fire at
127.0.0.1against an instance you started. Never touch a remote host. - 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-hunthandoff, 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 newestresults/TARGET/<timestamp>/directory that contains anINCIDENTS.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 callrundirfor anINC-nlead —rundirmints a fresh empty dir once a prior/dnr-respondin that dir has completed, which would strand the incident'sINCIDENTS.jsonin the old dir. Only if no run dir holds anINCIDENTS.json(you were handedINC-nbut never ran the hunt) fall through to therundirline 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--freshif it's in$ARGUMENTS). This adopts the newest run dir for the target if one exists, else mints a freshresults/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
- Resolve
--logs(dnr target config or bare directory) and inventory log files (du,wc -l,head -2each — identify formats and field positions). The corpus is expected to exist already — the target README covers generating it. - 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.
- 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_commandearly, 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_outrecord.
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:
- 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. - Seed and start the app locally per target config:
seed_command/app_commandare target-relative — run them with the target directory as cwd; launch in the background via a single command that prints the PID (the samepython3 -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, thenkill <pid>(never by port or pattern). - 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:
- Verdict & summary — three sentences: what happened, did it succeed, how far it went.
- Timeline — the merged entity timeline from Phase 1.
- 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).
- 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. - 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.
- 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.
- 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.jsonto 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.
FAQ
What does the dnr-respond skill do?
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.
How do I install dnr-respond?
Run npx skills add https://github.com/anthropics/defending-code-reference-harness --skill dnr-respond in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/dnr-respond/ (personal) or .claude/skills/dnr-respond/ (project).
Which AI agents can use dnr-respond?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is dnr-respond safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains dnr-respond?
It is published by anthropics in anthropics/defending-code-reference-harness. It has 224 recorded installs.
More from anthropics/defending-code-reference-harness
threat-model
anthropics/defending-code-reference-harness
Build a threat model for a target codebase. Three modes: "interview" walks an application owner through the four-question framework and produces a threat model from their answers; "bootstrap" derives a threat model from the code plus past vulnerabilities (CVEs, git history, pentest reports) when no owner is available; "bootstrap-then-interview" chains the two when both owner and codebase are present. All write THREATMODEL.md in a shared schema. Use when asked to "threat model", "build a threat model", "map the attack surface", or "what should we be worried about in this codebase".
vuln-scan
anthropics/defending-code-reference-harness
Static source-code vulnerability scan. Reads a target directory (and THREATMODEL.md if present), spawns parallel review subagents per focus area, and writes VULN-FINDINGS.json + .md for /triage to consume. Read-only — no building, running, or network. For execution-verified crashes, use vuln-pipeline instead. Use when asked to "scan for vulns", "review this code for security issues", "find bugs in ", or as the step between /threat-model and /triage.
triage
anthropics/defending-code-reference-harness
Triage a batch of raw security findings. Verify each is real, collapse duplicates, re-rank by derived exploitability, and tag with an owner. Takes a directory or file of scanner output and writes TRIAGE.json + TRIAGE.md sorted by what actually needs engineering attention. Use when asked to "triage findings", "validate scanner output", "prioritize vulns", or "review the backlog". Runs interactively by default; pass --auto to skip the interview.
patch
anthropics/defending-code-reference-harness
Generate candidate fixes for verified security findings. Consumes TRIAGE.json (preferred), VULN-FINDINGS.json, INCIDENTS.json, or a vuln-pipeline results directory. Pipeline input is delegated to the execution-verified vuln-pipeline patch ladder; static-analysis input gets a per-finding patch subagent + independent reviewer and is written as inert diffs for human review. Writes PATCHES/bugNN/{patch.diff,patchresult.json}, PATCHES.md, and PATCHES.json. Use when asked to "fix the findings", "patch these vulns", "generate fixes", or "close the loop on triage".
customize
anthropics/defending-code-reference-harness
Adapt this C/C++ ASAN vulnerability pipeline to a different vulnerability class, target shape, language, or detection mechanism. Use when the user wants to port, migrate, retarget, customize, or fork the pipeline for something other than C/C++ memory-safety bugs — web apps, smart contracts, deserialization, ML systems, or any other domain.
quickstart
anthropics/defending-code-reference-harness
The front door for this repo. With no argument: a 30-second intro, then an offer to walk you through your first run on the canary target. With a question: answers it from this repo's own docs and source, cites where it looked, and hands you the next command. Use for "how do I…", "why does…", "where is…", "can this…", or just "/quickstart" to get oriented.
Popular code quality & workflow skills
grill-me
mattpocock/skills
A relentless interview to sharpen a plan or design.
improve-codebase-architecture
mattpocock/skills
Scan a codebase for deepening opportunities, present them as a visual HTML report, then grill through whichever one you pick.
handoff
mattpocock/skills
Compact the current conversation into a handoff document for another agent to pick up.
domain-modeling
mattpocock/skills
Build and sharpen a project's domain model. Use when discussing codebase terminology, writing or editing a GLOSSARY.md, or recording or editing an ADR.
codebase-design
mattpocock/skills
Shared vocabulary for designing deep modules. Use when the user wants to design or improve a module's interface, find deepening opportunities, decide where a seam goes, make code more testable or AI-navigable, or when another skill needs the deep-module vocabulary.
diagnosing-bugs
mattpocock/skills
Diagnosis loop for hard bugs and performance regressions. Use when the user says "diagnose"/"debug this", or reports something broken/throwing/failing/slow.