Glossary
What SARIF triage actually means (and why your scanner doesn't do it)
A working glossary: finding, triage, false positive, LFP, noise, decision record. The words your queue already runs on, defined so two engineers mean the same thing.
Two engineers can look at the same queue and mean completely different things by “triaged.” One means somebody read it. The other means somebody decided it, wrote down why, and the decision will still make sense in six months. That gap is where backlogs go bad.
This is a working glossary of the words a finding queue actually runs on. It is scanner-neutral on purpose: every term below applies whether your output comes from an open-source rule engine, a commercial platform, or a pile of exports nobody has opened since the last audit.
SARIF, in one paragraph
SARIF — Static Analysis Results Interchange Format — is an OASIS standard for how analysis tools report what they found. It is JSON, it is versioned, and its job is to let results move between the tool that produced them and everything downstream: your IDE, your code review, a dashboard, a triage layer. The 2.1.0 specification is the document to keep open; it is long, but the parts that matter to a triage workflow are a handful of objects.
A minimal result looks close to this. Everything you need to name a finding is here, and nothing you need to decide it is.
$ jq '.runs[0].results[0]' scan.sarif
{
"ruleId": "java.lang.security.audit.sqli.jdbc-sqli",
"level": "error",
"message": { "text": "Detected a SQL query built from user input" },
"locations": [
{
"physicalLocation": {
"artifactLocation": { "uri": "src/main/java/.../UserDao.java" },
"region": { "startLine": 118, "startColumn": 21 }
}
}
],
"partialFingerprints": { "primaryLocationLineHash": "a1c3…:1" }
}Three fields do most of the work downstream. ruleId tells you which rule fired. partialFingerprints is what lets you recognise the same finding across two runs after the file has shifted by twenty lines — without it, every re-scan looks like a new backlog. And suppressions, which is absent here, is the specification’s place to record that a result was dismissed, by whom, and under what justification.
The vocabulary
- Finding (SARIF calls it a result). One rule matching one location. It is a claim that a pattern occurred, not a claim that anything is exploitable. Treating a finding as a bug is the single most common category error in this work.
- Rule. The pattern that fired, usually carrying a CWE identifier. CWE is the stable name for a weakness class — CWE-89 for SQL injection, CWE-798 for hardcoded credentials — and it is what lets you say “we handle this class this way” instead of reasoning rule by rule.
- Triage. Deciding what happens to a finding and recording why. Reading it is not triage. Sorting it by severity is not triage. A finding is triaged when it has an outcome and the outcome has a stated basis.
- False positive (FP). The rule fired but the weakness is not present — the flagged value cannot reach the sink, or is constrained to a safe domain, or the sink is not the sink the rule thinks it is. An FP is a statement about the code, and you should be able to point at the thing in the code that makes it one.
- Likely false positive (LFP). The honest middle. The available evidence points strongly at “not exploitable,” but the analysis could not establish it outright — perhaps because a call goes through an interface with several implementations. LFP is not a hedge; it is a different claim, and it belongs in a different queue from a confirmed FP.
- Noise. Findings a team has decided it will never act on, as a class rather than one at a time: rules that do not fit the threat model, findings in generated code, entire categories the organisation has accepted. Noise is a policy position. FP is a technical one. Collapsing the two is how teams end up unable to explain their own suppressions.
- True positive that will not be fixed. Real, and accepted, deferred, or compensated elsewhere. This is neither FP nor noise, and it is the category most queues have nowhere to put — so it gets mislabelled as a false positive and the risk record quietly becomes false.
- Decision record. The artifact triage produces: the verdict, the evidence it rests on, and the assumption that would reverse it. If your triage output is a status column, you are storing the conclusion and discarding the argument.
- Proof obligation. For a finding that cannot be decided yet, the single specific thing that would decide it — “confirm no caller passes an unvalidated value to this method.” A queue of proof obligations is workable. A queue of undifferentiated open findings is not.
Why your scanner doesn’t do this
Not as a criticism — because it is a different job, and the split is structural.
A scanner’s contract is recall over a rule set. It runs patterns and dataflow queries over your code and reports where they match, and it is tuned to not miss things, because a missed vulnerability is a much worse failure for a scanner than an extra finding. That tuning is correct and it produces volume by design.
Triage is the opposite contract: precision about a specific finding in a specific codebase, using context the rule author never had. Whether your deployment sets that flag. Whether your binder validates before that call. Whether that path is reachable from any entry point that exists in your service. The rule cannot encode this, because the rule ships to everyone.
The severity field does not bridge the gap either. Severity in SARIF describes the weakness class in the abstract — how bad this kind of bug is when real. It says nothing about whether this instance is real. A queue sorted by severity is a queue sorted by how bad things would be if they were all true.
How to tell whether a queue is actually triaged
A test that takes about five minutes and does not require any tooling. Pick three closed findings at random from the last quarter and try to answer, from what is recorded:
- What specific fact made this safe to close?
- Which file and line would I open to check that fact today?
- What change to the codebase should make this finding come back?
If all three are answerable from the record, the queue is triaged. If they are answerable only by the person who closed it, the queue is triaged until that person leaves. If they are not answerable at all, the queue is suppressed, which is a different word.
Limitations of this glossary
- These are not universal definitions. LFP in particular is our usage, not a standardised term. Different teams draw the FP/noise line in different places, and vendors use “triage” to mean everything from sorting to auto-closing. The value is in your team agreeing on one meaning, not in adopting ours.
- SARIF conformance varies. The specification defines far more than most producers emit. Code flows, fingerprints and suppression provenance are all optional, and a triage workflow built on a field your scanner leaves empty will not work. Check your own output before designing around a field.
- This covers static findings only. Dependency findings, secret scanning and runtime alerts share some of the vocabulary and none of the evidence model. Reachability for a vulnerable dependency is a different question from reachability for a tainted parameter, and the same words hide the difference.
Where this goes next
Most of the terms above bottom out in one question — can the flagged value actually get there — which is the subject of reachability analysis, and it deserves its own post along with an honest account of what it cannot establish.
If you want the shape of a decision record rather than a description of one, how it works walks through the evidence we attach to a finding, and the directory lists the runs we have published, each with its own limitations stated alongside the verdicts.