Skip to main content

triage

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.

소스 정보

저장소
anthropics/defending-code-reference-harness
최근 소스 활동
2026년 7월 16일 16:35
감지된 SKILL.md 언어
영어
스타
7,585
포크
622

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

파일 탐색기
3 개 파일

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
triage
description
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.
argument-hint
<findings-path> [--auto] [--votes N] [--repo PATH] [--fp-rules FILE] [--fresh]
allowed-tools
["Read","Glob","Grep","Write","Task","AskUserQuestion","Bash(git log:*)","Bash(jq:*)","Bash(find:*)","Bash(ls:*)","Bash(wc:*)","Bash(python3 .claude/skills/_lib/checkpoint.py:*)"]
# 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`, an `INCIDENTS.json`, a pipeline `results/<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 of `phase*.json` or 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 every `save`/`shard`/`append` call. **Start of run — resume check.** Bash: `python3 .claude/skills/_lib/checkpoint.py load ./.triage-state` - `status == "absent"` OR `"complete"`, OR `--fresh` in `$ARGUMENTS` → **fresh start.** Bash: `python3 .claude/skills/_lib/checkpoint.py reset ./.triage-state`, then proceed to Phase 0. - `status == "running"` with `phase_done == N` → **resume.** Read `./.triage-state/phase0.json` through `phaseN.json` **in order** (and any `shard_*.json` files listed in `shards_done`), merging keys into working state (later files override earlier — checkpoints may be deltas). Print `Resuming from checkpoint: Phase N complete (./.triage-state/phaseN.json)`, and **skip directly to Phase N+1**. **End of every phase N.** Two tool calls: 1. Write tool → `./.triage-state/_chunk.tmp` containing the phase's output JSON (schema at the tail of each phase section). 2. 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): 1. **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. 2. **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. 3. **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 what `severity_label` additionally shows. 4. **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). - Scoring: derived HIGH/MEDIUM/LOW. - Noise tolerance: precision. **Checkpoint:** Write tool → `./.triage-state/_chunk.tmp`: ```json {"phase": 0, "context": {mode, environment, threat_model, scoring, noise_tolerance, votes_per_finding, repo, findings_path}} ``` Then Bash: `python3 .claude/skills/_lib/checkpoint.py save ./.triage-state 0 interview --from ./.triage-state/_chunk.tmp` On resume past Phase 0, the interview is **not** re-asked; `context` is restored from this file. --- ## Phase 1: Ingest and normalize Turn the input into a flat `findings[]` list with stable ids, regardless of source format. ### 1a. Detect input shape Inspect the findings path: - **Directory**: Glob for `**/*.json` and `**/*.jsonl`. Recognized containers, in priority order: - `VULN-FINDINGS.json` (a `{findings: [...]}` container): read `.findings[]`. - `INCIDENTS.json` (the `/dnr-hunt` / `/dnr-respond` output): read `.incidents[]`; each incident is one finding. Map the nested fields — `vuln.file` → `file`, `vuln.line` → `line`, `vuln.type` → `category`, `vuln.function` → `function`, `description` ← `vuln.mechanism` plus `impact` — and keep `id`, `title`, `verdict`, and `poc`; the 1b aliases route `poc` → `exploit_scenario`, and a `confirmed_exploited` verdict means the incident arrived execution-verified, not statically guessed — verifiers should weight that. The top-level `ruled_out` array is not findings — don't ingest it. Incidents with no `vuln.file` follow the 1b missing-`file` rule like any other finding. - `reports/bug_*/report.json` or `reports/manifest.jsonl` (this repo's pipeline output): one finding per `bug_NN`. Map `crash.crash_type` → `category`, `verdict.severity_rating` → `severity`, the prose `report` → `description`, crash file from the ASAN top frame → `file`/`line`. - `found_bugs.jsonl`: one finding per line. - Any other `*.json` whose top level is a list of objects, or an object with a `findings`/`results`/`issues`/`vulnerabilities` array: that array. - **Single `.json` / `.jsonl` file**: same recognition as above. - **Markdown / text**: split on level-2/3 headings or `---` rules; for each section, extract `file`, `line`, `category`, `severity`, `description` by pattern (`File:`, `Line:`, `Severity:` labels or `path:NN` spans). Best-effort; mark `source_format: "markdown_heuristic"`. If nothing parseable is found, stop and report what was seen. ### 1b. Normalize fields For each raw record, build a finding dict. **Pull what's present; never guess what's absent.** Field map (source-key aliases → canonical): | Canonical | Also accept | |-----------------|----------------------------------------------------------| | `file` | `path`, `location.file`, `filename`, ASAN top-frame file | | `line` | `line_number`, `location.line`, `lineno` | | `category` | `type`, `cwe`, `rule_id`, `crash_type`, `vulnerability_class` | | `severity` | `severity_rating`, `level`, `priority`, `risk` | | `title` | `name`, `summary`, `message` | | `description` | `details`, `report`, `body`, `evidence` | | `exploit_scenario` | `attack_scenario`, `poc`, `reproduction` | | `preconditions` | `requirements`, `assumptions` | | `recommendation`| `fix`, `remediation`, `mitigation` | | `scanner_confidence` | `confidence`, `score`, `certainty` (normalize to 0.0-1.0) | Attach to every finding: - `id`: `f001`, `f002`, ... in ingest order. If `scanner_confidence` is present on most findings, order ingest by it descending so high-signal findings get verified (and surface in partial output) first; otherwise keep source order. This is a scheduling prior only — it does not affect verdicts. - `source`: relative path of the file it came from, plus source format. - `missing_fields`: list of canonical fields that were absent. If `file` is missing or does not resolve under `--repo`, the finding is **unlocatable**: it skips dedup and verification and is emitted directly with `verdict: false_positive`, `verify_verdict: needs_manual_test`, `confidence: 0`, `refute_reasons: ["doesnt_exist"]`, `rationale: "no source location in input; cannot verify statically; human review required"`. Never emit a confident verdict on a finding you could not locate, and never let it absorb or be absorbed by dedup. ### 1c. Locate the target codebase Resolve `--repo` (default cwd). For the first 5 findings with a `file`, check the path resolves under the repo. Try, in order: (a) `repo/file` as-given; (b) `file` as an absolute or cwd-relative path; (c) `repo/file` with common prefixes stripped from `file` (`src/`, `app/`, `./`, or the repo's own basename, e.g. `harness/grade.py` with `--repo harness`). Record which resolution worked and apply it to every finding. If none resolve, **stop**: tell the user verification needs source access and the cited files aren't reachable, and suggest a `--repo` value based on the longest common suffix you can see. **Checkpoint:** Write tool → `./.triage-state/_chunk.tmp`: ```json
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기