Triage
Triage a batch of raw security findings. Verify each is real,
- Skill ID
- anthropics/defending-code-reference-harness/triage
- Publisher
- anthropics
- Repository
- defending-code-reference-harness
- Installs
- 0
- Files
- 3
- Synced
- Sep 23, 2026
Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.
anthropics/defending-code-reference-harness/triageInstalls these files- README.md
- SKILL.md
- fixtures/canary-findings.json
What this skill tells the agent
triage
Adversarial triage of raw security-scanner output. Does four jobs: verify each finding is real, deduplicate across runs and scanners, rank survivors by derived exploitability rather than the scanner's claimed severity, and route each to a component owner. Output is a short, ranked, owned list instead of a raw dump.
Invoke with /triage <findings-path> [--auto] [--votes N] [--repo PATH] [--fp-rules FILE].
Arguments (parse from $ARGUMENTS; positional $1/$2 expansion is not stable across runtimes):
- findings path (first positional, required): a JSON file, a directory of JSON files, a
VULN-FINDINGS.json, anINCIDENTS.json, a pipelineresults/<target>/<ts>/directory, or a markdown report. --auto: skip the interview and use defaults. Default mode is interactive.--votes N: verifier votes per finding (default 3; use 1 for a quick pass, 5 for high-stakes batches).--repo PATH: path to the target codebase, read-only (default cwd). Verification needs source access; the skill stops with an error if the cited files aren't reachable.--fp-rules FILE: append the contents of FILE to the verifier's exclusion-rule list (Phase 3a). Use for org-specific precedents: "we use Prisma ORM everywhere — raw-query SQLi only", "k8s resource limits cover DoS", etc. Plain text, one rule per line or paragraph.--fresh: ignore any existing checkpoint in./.triage-state/and start from Phase 0. Without this flag the skill resumes from the last completed phase if a checkpoint is present.
Tools: Read, Glob, Grep, Write, Task, AskUserQuestion. Bash is permitted only for git, find, wc, ls, jq, and python3 .claude/skills/_lib/checkpoint.py (checkpoint I/O).
Do not execute target code. No building, running, installing dependencies, or sending requests. A proof-of-concept that accidentally works against something real is unacceptable, and "couldn't write a working PoC" is weak evidence of non-exploitability. Every conclusion comes from reading source. This applies to the orchestrator and every subagent; include the constraint in every Task prompt. For high-confidence HIGH findings, recommend a human-built PoC as a follow-up instead.
Do not reach the network. No package-registry lookups, CVE-database queries, or upstream-commit fetches.
Checkpointing (runs before Phase 0 and after every phase)
On large finding batches a full run can exhaust context or hit rate limits mid-way — particularly Phase 3, which spawns candidates × votes verifiers. Phase state persists to ./.triage-state/ so a fresh /triage session can resume without re-asking the interview or re-spawning verifiers.
All checkpoint I/O goes through python3 .claude/skills/_lib/checkpoint.py (atomic writes, JSON-validated). Never use the Write tool for progress.json directly. Never pass payload via heredoc or stdin; target-derived strings could collide with the heredoc delimiter and break out to shell. The Write→--from pattern keeps repo-derived bytes out of Bash argv.
State files in ./.triage-state/:
progress.json— single source of truth for resume position:{"status": "running"|"complete", "phase_done": N, "shards_done": [...]}. Resume decisions read ONLY this file, never a glob ofphase*.jsonor shard files (stale files from a prior run must not be trusted).phaseN.json— data payload for phase N (schemas at the tail of each phase section below)._chunk.tmp— transient payload buffer; overwritten before everysave/shard/appendcall.
Start of run — resume check. Bash: python3 .claude/skills/_lib/checkpoint.py load ./.triage-state
status == "absent"OR"complete", OR--freshin$ARGUMENTS→ fresh start. Bash:python3 .claude/skills/_lib/checkpoint.py reset ./.triage-state, then proceed to Phase 0.status == "running"withphase_done == N→ resume. Read./.triage-state/phase0.jsonthroughphaseN.jsonin order (and anyshard_*.jsonfiles listed inshards_done), merging keys into working state (later files override earlier — checkpoints may be deltas). PrintResuming from checkpoint: Phase N complete (./.triage-state/phaseN.json), and skip directly to Phase N+1.
End of every phase N. Two tool calls:
- Write tool →
./.triage-state/_chunk.tmpcontaining the phase's output JSON (schema at the tail of each phase section). - Bash →
python3 .claude/skills/_lib/checkpoint.py save ./.triage-state <N> <name> --from ./.triage-state/_chunk.tmp
End of run. After writing TRIAGE.json and TRIAGE.md, Bash: python3 .claude/skills/_lib/checkpoint.py done ./.triage-state 6
Phase 0: Mode select and interview
0a. Parse arguments
From $ARGUMENTS: extract the findings path (first positional), --auto flag, --votes N (default 3), --repo PATH (default .), --fp-rules FILE (default none). If no findings path was given, ask for one and stop. If --fp-rules was given, Read the file now and carry its contents as context.extra_fp_rules for injection into the Phase 3a verifier prompt.
0b. Interactive mode (default): interview the user
Unless --auto was passed, use AskUserQuestion to gather context that shapes verification and ranking. Batch into one or two calls of up to four questions. Expect free-text answers via "Other"; the multiple-choice options are prompts, not constraints.
Round 1 (single AskUserQuestion call):
- Environment & trust boundary (header
Environment, single-select)What kind of system are these findings from, and where does untrusted input enter it?Options:Internet-facing web service (HTTP is untrusted),Internal service (callers are authenticated peers),Library / SDK (caller is the trust boundary),CLI / batch tool (operator inputs trusted, file inputs not),Embedded / firmware (physical access in scope). Reachability is judged against this boundary; "command injection from env var" is a true positive in a multi-tenant web service and a rule-8 false positive in an operator CLI.
- Threat model (header
Threat model, multi-select)What does a worst-case attacker look like for this system, and what must never happen? Free text is best.Options:Unauthenticated remote code execution,Tenant-to-tenant data leakage,Privilege escalation to admin,Supply-chain compromise of downstream users,Denial of service against a paid SLA,Compliance-scoped data exposure (PII / PCI / PHI). Phase 4 boosts findings that map onto a stated threat.
- Scoring standard (header
Scoring, single-select)How should severity be expressed in the output?Options:Derived HIGH/MEDIUM/LOW from preconditions (default),CVSS v3.1 vector + base score,CVSS v4.0 vector + base score,OWASP Risk Rating (likelihood x impact),Organization bug-bar (describe in Other). The precondition rule is always computed; this controls whatseverity_labeladditionally shows.
- Noise tolerance (header
Noise tolerance, single-select)When verifiers disagree, which way should ties break?Options:Precision: drop anything not majority-confirmed (fewer FPs, may miss real bugs),Recall: keep split votes as needs_manual_test (more to review, fewer misses),Ask me per-finding when it happens.
Round 2 (conditional): if the threat-model answer was empty or generic, or the scoring answer was Organization bug-bar, ask one targeted follow-up.
Record the answers as a context dict carried through every phase and echoed in the output under triage_context.
0c. Auto mode defaults
When --auto is set, do not call AskUserQuestion. Use:
- Environment:
Unknown. Treat any externally-reachable entry point as untrusted; flag trust-boundary assumptions explicitly in rationale. - Threat model: empty (no boost).
More from anthropics
Use this skill in a project
Open any RiverX project, search the Skills panel for anthropics/defending-code-reference-harness/triage, and install it in one click.
