Take a baseline scan of the current code, then compare every later scan against it and fail the build only on findings that are new. Legacy issues stay visible but do not block work, and a ratchet rule makes sure the old count never grows.
How comparison works
Scanners that emit SARIF can express this directly. The specification lets a run name a baseline run, and each result can then carry a baselineState of new, unchanged, updated or absent. A result is "new" if it was detected in the current run but not in the baseline run, and "absent" if it was in the baseline but is gone now.
Matching one run to another depends on fingerprints. SARIF defines a fingerprint as a stable value that a result management system can use to identify a result over time, and result matching as deciding whether two results report the same condition in the code. Stable matching is what keeps an unrelated edit from making an old finding look new.
Steps
- Freeze a baseline. Scan the main branch, store the SARIF output as an artifact, and note the commit and scanner version.
- Scan each pull request and compare against the stored baseline.
- Fail on new findings above a severity you choose. Start with high and critical only.
- Report unchanged findings as a count, not as comments, so reviewers are not buried.
- Refresh the baseline only through a reviewed change, and only to remove fixed findings. A baseline that quietly absorbs new findings defeats the purpose.
- Ratchet. After each release, regenerate the baseline from a clean main. Fixed items drop out and cannot return unnoticed.
Pitfalls
| Pitfall | Effect | Mitigation |
|---|---|---|
| Matching on line number | Edits above a finding make it look new | Use stable fingerprints or code context |
| Upgrading the scanner | New rules add many findings at once | Re-baseline with the upgrade, in its own reviewed pull request |
| Moving or renaming files | Findings appear as new in the new path | Check whether your tool tracks renames |
| Baseline stored only on one laptop | Builds fail for others | Keep it in CI artifacts or the repository |
| Never paying down the backlog | Legacy count stays forever | Schedule burn down work, see the related plan below |
Pair it with burn down
A baseline is a pause, not a pardon. Set a sprint budget for old findings, start with high severity in code that changes often, and track the unchanged count over time. See a burn down plan for legacy security debt and, for first scan flood, the first scan found 1,000 issues.
Try it yourself
Open the demo and watch how a gate decides what blocks a pull request.
Click through it at your own pace, or open it full screen.
One factual note on Safeguard
Safeguard supports policies and gates on pull requests. If you want a quick look at your own code, the free scan at gold.safeguard.sh/scan is a no-account place to start.
Quick check
Does a baseline hide old vulnerabilities?
It hides them from the build gate, not from reports. Keep reporting the unchanged count so the debt stays visible.
What if my scanner does not support baselines?
You can diff two result files yourself on rule, file and a code snippet hash, though stable matching takes care.
Sources
- SARIF 2.1.0 specification, OASIS, baselineState property and fingerprint terminology.
Last reviewed: 19 October 2026.