Give every container scan finding one of a small set of states: open, fixed, suppressed with a recorded reason, or reopened. Add a short owner and a date to each change. This keeps reports honest, because a number that only goes down when findings are hidden is not progress.
A simple state model
| State | Meaning | Needs |
|---|---|---|
| Open | Present in the latest scan, not yet handled | Owner, due date |
| Fixed | No longer present in a later scan of the new digest | Link to the new digest |
| Suppressed | Present, but accepted or judged not applicable | Reason, approver, expiry |
| Reopened | Was fixed or suppressed, and appears again | Cause noted |
Four states are enough for most teams. More states tend to be used inconsistently.
Why "fixed" must come from a scan
Mark a finding fixed only when a scan of the new image no longer reports it, not when a pull request merges. A merged change may not have been built, pushed or deployed. Tie the state change to a digest so you can show which image no longer has the problem.
Suppressed needs a reason
A suppression without a reason is a hidden finding. Record why: the vulnerable component is not present, the code path is not used, a compensating control exists, or the risk was accepted. Add an expiry so the item comes back for review.
The OpenVEX specification gives useful vocabulary here. It defines statuses including not_affected, affected, fixed and under_investigation. For not_affected, a statement must include a justification or an impact statement explaining why. For under_investigation, the spec says it is not yet known whether the product versions are affected, and that updates should follow as knowledge evolves. You can borrow these words so your internal states line up with what customers may ask for.
| Your state | Closest VEX status |
|---|---|
| Open | affected |
| Fixed | fixed |
| Suppressed, not applicable | not_affected |
| Suppressed, still investigating | under_investigation |
Why reopened matters
Findings come back. A rebuild may pull an older base layer, or a revert may reintroduce a package. If your tool has no reopened state, the item appears as new and the history is lost. That history is what shows an auditor that you notice regressions.
What to report
Report counts by state, plus age of open items. A fall in open findings with a matching rise in fixed is real progress. A fall in open with a rise in suppressed deserves a question.
A worked example: one finding's life
Follow one finding through a few months.
| Day | Event | State | Recorded |
|---|---|---|---|
| 1 | Scan of digest A reports a library CVE | Open | Owner: payments team, due in 14 days |
| 6 | Team updates the dependency, builds digest B | Open | Link to pull request |
| 7 | Scan of digest B no longer reports it | Fixed | Digest B, scan date |
| 20 | A hotfix reverts the dependency, digest C | Reopened | Cause: revert, digest C |
| 21 | Team confirms the vulnerable function is never called and records why | Suppressed | Reason, approver, expiry in 90 days |
| 111 | Expiry arrives | Open | Needs review again |
Every transition has a reason and an artifact. If a tool only had open and closed, days 20 and 111 would vanish and the history would look like one clean fix.
Step by step: introduce the states
- List the states you will use and write one sentence for each.
- Decide which transitions need a human, for example open to suppressed.
- Require a reason and an expiry for suppression.
- Automate fixed and reopened, since only a scan can decide them.
- Add the digest and scan date to every transition record.
- Report counts by state and the age of open items every week.
Edge cases worth deciding in advance
- A finding disappears because the package was removed from the image. This is fixed, with the cause noted.
- A finding disappears because the scanner data changed, not because anything in the image did. Mark it as changed by data and keep the note. Do not claim a fix.
- A finding appears on an image you did not build. The state is open, and the owner is whoever approved the image.
- The same CVE appears in ten images. Track each image, but report them grouped by cause.
Mapping to what customers ask for
Customers who ask for a statement about a CVE in your product usually want one of a few answers: affected, not affected with a reason, fixed, or under investigation. If your internal states map cleanly to those words, you can answer without translation. The OpenVEX specification lists the not_affected justifications, among them component_not_present, vulnerable_code_not_present, vulnerable_code_not_in_execute_path, vulnerable_code_cannot_be_controlled_by_adversary and inline_mitigations_already_exist. Pick the one that matches your suppression reason, and be ready to show the evidence.
Common mistakes
- Counting suppressed findings as fixed in a progress chart.
- Letting suppression last forever.
- Closing a finding when a pull request merges, before the deploy.
- Using free text for reasons, which makes later reporting impossible.
Try it yourself
Click through it at your own pace, or open it full screen.
Follow a finding from detection to closure and note where a state change should record a reason.
Where Safeguard fits
Safeguard tracks findings through vulnerability management workflows and policies, and keeps evidence you can show later. You can try a free scan at gold.safeguard.sh/scan.
Quick check
Can a suppressed finding stay suppressed forever?
It should not. Give every suppression an expiry date so someone reviews it again.
Is a merged fix pull request the same as fixed?
No. Mark it fixed after a scan of the rebuilt, deployed digest no longer reports it.
Who should be allowed to suppress a finding?
Someone other than the person who owns the fix, such as a security reviewer or the service owner's lead. Record who approved it and when it expires.
Sources
Last reviewed: 19 October 2026.