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
آخر نشاط في المصدر
١٦ يوليو ٢٠٢٦ في ١٦:٣٥
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
٧٬٦٤٣
التفرعات
٦٢٩

خيارات التثبيت

يُحدَّد 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