# Finding Triage

> Triage a single security finding — from a scanner, audit, advisory, or report — to a defensible disposition with a mitigation plan, false-positive justification, or accepted-risk writeup. Use when the user mentions 'triage this finding,' 'is this a real vulnerability,' 'mitigation plan,' 'false positive,' 'accept this risk,' 'compensating controls,' 'risk justification,' 'security ticket,' 'CVSS this,' 'should we fix this,' 'disposition,' 'sign off on,' or has a single security finding and needs to decide what to do.

- Skill: `briiirussell/finding-triage` (Agent Skill)
- Install (CLI): `npx skillmds@latest add briiirussell/finding-triage`
- Raw SKILL.md: https://api.skillmd.com/api/skills/briiirussell/finding-triage/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Security
- Author: briiirussell (https://skillmd.com/u/briiirussell)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/briiirussell/finding-triage

---


# Finding Triage — Single-Finding Disposition with Defensible Justification

Every other skill in this repo *generates* findings. This skill *closes the loop* — for a single finding, walk through whether it's real, what severity it deserves in your context, and what to do about it. Output is a complete ticket-ready writeup with the right fields, the right justification, and an audit trail that survives a regulator reading it six months later.

The dispositions match `owasp-audit`'s Three-Disposition rule: **Fixed**, **Deferred**, or **Accepted Risk**. False positive is a fourth — but it isn't a disposition for a real finding, it's a determination that there *is* no finding.

This skill works on findings from any source: SAST output, DAST scanner, dependency advisory, manual audit, threat-hunt hit, pentest report, vendor disclosure, internal red-team writeup, bug bounty submission.

Cross-references:
- `vuln-research` for the technical CVE deep-dive that feeds reachability assessment here
- `owasp-audit` Three-Disposition rule (the framework this implements per-finding)
- `security-comms` for translating the disposition writeup into stakeholder-readable language when the finding has to leave the security context
- Any audit skill — this consumes their findings as input

## Workflow

The agent works through these steps with the user. Stop and ask clarifying questions where the user has context the finding alone doesn't reveal.

### Step 1 — Restate the finding in your own words

If the finding came from a scanner, restate what's actually being claimed. Scanners produce noise; restating filters out the boilerplate.

A good restatement names:
- **What** the issue is (specific weakness — CWE if applicable)
- **Where** it lives (file:line, endpoint, resource ARN, host)
- **How** it could be exploited (preconditions, attacker capability needed)
- **Impact** if it were exploited (data loss, privilege escalation, availability)

If you can't restate it clearly, you don't understand it yet. Ask the user for context.

### Step 2 — Is this actually true?

Half of automated-scanner findings are false positives by volume. The triage:

| Question | If yes | If no |
|---|---|---|
| Does the vulnerable code path exist as described? | Continue | False positive — scanner found a phantom |
| Is the code path reachable from any attacker-controllable input? | Continue | Continue, but severity drops |
| Does the exploit precondition match your environment? | Continue | Severity drops or false positive |
| Is there a public PoC, or has anyone confirmed this in the wild? | Severity stays / rises | Severity may drop |
| Are existing controls (WAF, auth, network segmentation) preventing exploitation? | Severity drops; controls become the mitigation | Severity stays |

**Common false-positive patterns:**
- SAST flag on test files or dead code
- Dependency scanner flag on package in `devDependencies` only — runtime-unreachable (see `dependency-audit` reachability column)
- DAST flag on a path that returns 404 in your real environment but was confused by SPA routing
- Outdated advisory — vendor silently fixed it before the CVE; your version contains the fix
- Pattern match on code that *looks* vulnerable but is inside a function never invoked

**Document a false-positive determination as carefully as a real finding.** If a future scanner or auditor flags the same thing, the prior false-positive note saves them the work.

### Step 3 — Contextual severity

The scanner's CVSS or severity rating is a starting point, not the answer. Adjust for your context.

**Factors that increase severity beyond the rating:**
- Vulnerable endpoint is internet-facing, not internal
- Authentication preconditions are easy to satisfy (open signup) in your app, even if the CVE assumes "authenticated"
- Vulnerable data is regulated (PII, PCI cardholder data, PHI) — exploitation has reportable-incident consequences
- Public PoC exists or active exploitation observed
- Vulnerable component is in the critical path (every request touches it)
- Compensating controls are missing or weak

**Factors that decrease severity below the rating:**
- Vulnerable code path is unreachable in your usage (read the patch, grep for the function — see `vuln-research`)
- Strong compensating controls (WAF blocks the payload pattern, network segmentation prevents reach)
- Exploit requires preconditions that don't exist in your environment (specific OS version, specific config)
- Authentication preconditions are hard in your app (closed signup, MFA, employee-only)
- Component is dev-only or build-only, not runtime-reachable

**Severity scale (use the one your org uses; here's a common one):**

| Level | Definition |
|---|---|
| **Critical** | Pre-auth or trivially exploitable; immediate data loss / RCE / takeover; patch within 24-72 hours |
| **High** | Auth required but minimal privilege; or post-auth path to significant impact; patch within 1-2 weeks |
| **Medium** | Requires meaningful privilege or chain; realistic but not trivial; patch within 30 days |
| **Low** | Defense-in-depth; hard to chain; patch within 90 days |
| **Info** | Hardening or hygiene; documented behavior; patch when convenient |

### Step 4 — Pick the disposition

| Disposition | When to choose | Required fields |
|---|---|---|
| **Fix now** (synonym: **Fixed**) | Patch / mitigation deployable within the severity's SLA | Fix description, deploy plan, verification method |
| **Defer** | Severity warrants action, but operational constraints make immediate fix infeasible | Reason for deferral, new deadline, who owns, alerting if conditions change before deadline |
| **Accept risk** | Fix isn't planned at current configuration | (1) Why fix doesn't apply, (2) Compensating controls, (3) Re-evaluation trigger |
| **False positive** | Not actually a vulnerability | Evidence for the determination, scanner rule ID to suppress (with care) |

**On Defer:** severity does NOT change because you decided to defer. Recording a "High deferred to Q3" is honest; downgrading a High to Medium because Q3 is far away is risk-laundering.

**On Accept Risk:** all three fields are required. An "Accepted Risk" without all three is a real finding being silently dropped. The re-evaluation trigger is the most-skipped field — name a specific condition (plan upgrade, dependency bump, traffic pattern change, audit anniversary).

### Step 5 — Write the disposition

Produce a ticket-ready writeup. Use one of these templates depending on disposition.

#### Template: Fix

```markdown
## Finding: [Title]
**Source:** [scanner / audit / advisory]
**Severity:** [contextual] (Scanner reported: [original])
**CWE / CVE:** [if applicable]
**Location:** [file:line / endpoint / resource]

### What
[Plain-English description — what the issue is]

### Why this severity
[Contextual reasoning — what the scanner missed, what your environment adds]

### Fix
[Specific change — code diff, config update, dependency upgrade]

### Verification
[Concrete test — adversarial input + observed result that proves the fix holds]
- Run [command / test case]
- Observe [response / behavior]

### Owner: [name]
### Target deploy: [date]
```

#### Template: Defer

```markdown
## Finding: [Title] — DEFERRED
**Severity:** [unchanged]
**Original target:** [original SLA date]
**New target:** [date]

### Why deferred
[Operational constraint — release freeze, dependency on third party, etc.]

### Risk during deferral window
[What's the exposure? What controls reduce it?]

### Alerting / conditions that would escalate
[What would force action sooner than the new target?]

### Owner: [name]
### Re-evaluation: [date — usually before new target]
```

#### Template: Accept Risk

```markdown
## Finding: [Title] — ACCEPTED RISK
**Severity:** [contextual]
**Approver:** [name + role]
**Date accepted:** [date]

### Why fix doesn't apply
[Cost tier, dependency version constraint, deployment topology, vendor limitation, etc. — be specific]

### Compensating controls
- [Control 1 — what it is, why it reduces impact / likelihood]
- [Control 2]
- ...

### Re-evaluation trigger
[Specific condition — plan upgrade, dependency bump, traffic pattern change, calendar anniversary]
- Trigger: [what would change this decision]
- Calendar review: [date — at minimum, annually]

### Approvals
- [ ] Engineering owner: [name, date]
- [ ] Security: [name, date]
- [ ] (if required) Compliance / Legal: [name, date]
```

#### Template: False Positive

```markdown
## Finding: [Title] — FALSE POSITIVE
**Scanner:** [name + rule ID]
**Original severity:** [as reported]

### What the scanner claimed
[Restate the claim]

### Why it's not real
[Specific evidence — code path not reachable, version contains the fix, etc.]

### Suppression decision
- [ ] Suppress this exact finding (location + rule ID)
- [ ] Add allow-list rule (with care — broad suppression breeds blind spots)
- [ ] No suppression — re-evaluate if it returns

### Determination by: [name, date]
### Reviewed by: [name, date — for non-trivial suppressions]
```

### Step 6 — Validate the writeup

Before submitting:

- Could a future you (or auditor) reading this in 12 months understand the decision without further context?
- Is the verification step concrete enough that someone other than the original author could run it?
- For Accept Risk: are all three required fields filled in with specifics, not platitudes ("standard controls in place" is not a compensating control)?
- Does the severity reflect your environment, or just the scanner's default?

## When to escalate

Findings that should NOT be triaged unilaterally by a single engineer:

- Anything Critical
- Anything pre-auth exploitable
- Anything affecting regulated data (PII / PCI / PHI)
- Anything with public PoC or active exploitation
- Anything where the proposed disposition is Accept Risk and the severity is High or above

These get a second reviewer (Tier 3 / security team / approver named in policy).

## Output Format

The primary output is the disposition writeup itself (templates above). For a triage session that covers multiple findings or a batch import, summarize:

```markdown
# Finding Triage Session
## Source: [scanner / audit / report]
## Date: [date]
## Triaged by: [name]

### Summary
| Finding | Original severity | Contextual severity | Disposition |
|---------|-------------------|---------------------|-------------|

### Detail
[Per-finding writeup using the appropriate template]

### Escalations
[Findings that need senior review or approval]
```

## Boundaries

- This skill operates on findings the user has authority to triage and dispose of
- Severity decisions and Accept Risk dispositions affect organizational risk posture — for High+ findings, ensure the listed approver actually approves; don't fabricate sign-offs
- False-positive determinations should be evidence-based, not "we don't want to fix it" rebranded — push back if the user wants to FP a real finding
- Refuse to help downgrade severities to avoid disclosure or audit obligations
- If a finding surfaces an active incident, hand off to `incident-triage` — disposition triage is for steady-state findings, not fires

## References

- CVSS v4.0 specification — `first.org/cvss`
- EPSS — Exploit Prediction Scoring System (FIRST.org)
- NIST SP 800-30 (Guide for Conducting Risk Assessments)
- "FAIR" (Factor Analysis of Information Risk) — quantitative risk framework if you need dollar-denominated decisions
- ISO 27005 — risk management
- "Measuring and Managing Information Risk" — Jack Freund, Jack Jones (the FAIR book)
- OWASP Risk Rating Methodology

