VEX status flipped from "not_affected" to "affected" after the agent regenerated the SBOM with a different tool
Fixes VEX statements that flip from not_affected to affected when the SBOM generator changes, by keying VEX on canonical package URLs instead of tool-specific names and pinning one generator per pipeline. Use it after swapping SBOM tools. Key trigger: VEX verdicts change with no code or advisory change, right after an SBOM tool swap.
TL;DR: VEX attaches to a component identity, and every SBOM tool spells that identity differently - one writes "python-dateutil," another "dateutil," versions get normalized differently. Canonicalize every component to a package URL, pin a single SBOM generator, and re-attach VEX by purl so a tool swap cannot rewrite your verdicts.
VEX status flipped from "not_affected" to "affected" after the agent regenerated the SBOM with a different toolSteps
- Take the old SBOM and the new SBOM and put the same component side by side: compare the name, version, and purl fields.
Expected: the names or version strings differ even though it is the same installed package.
- Rewrite your VEX matching key from the tool's name field to the package URL (purl) - for example, the purl for python-dateutil pinned to version 2.8.2 - and backfill the existing VEX statements with purls.
Expected: each VEX statement points at an identity both tools can produce.
- Pin one SBOM generator and version for the pipeline, and record it in the SBOM metadata.
Expected: no more silent generator swaps between runs.
- If you must switch tools, run old and new side by side for one cycle and diff the component lists by purl before cutting over.
Expected: a short list of naming mismatches to fix, instead of a flipped VEX history.
- Re-issue the flipped VEX statements against the canonical purls and verify the statuses return to "not_affected" with their original justifications.
Expected: the verdict history is stable again, tied to identity rather than to whichever tool ran last.
Use this when
- VEX statuses change after switching SBOM generators
- Two tools (syft, cdxgen, trivy) disagree on a package name or version for the same installed component
- Your VEX statements are keyed on names and a rename orphaned them
- An audit diff shows exploitability verdicts moving with no code change
Not for this skill when
- The component actually changed (version bump, replaced package) - re-verify reachability on the new component
- The flip came from an advisory update (new exploit published) - that is a real status change, not an identity problem
- You have no SBOM at all and VEX is hand-written - standardize on purls first, then automate
Variant phrasings
- VEX not_affected became affected after SBOM regeneration
- syft vs cdxgen package name mismatch broke VEX matching
- how to key VEX statements so SBOM tool changes don't flip them
Why it happens
VEX is a claim about a component, and "the component" has no single spelling - each SBOM tool normalizes names, versions, and qualifiers its own way. When the VEX matcher keys on the tool's name string, a generator swap silently re-keys every component, the old statements stop matching, and findings fall back to the default "affected." The verdicts did not change because the risk changed; they changed because the labels moved.
Edge cases
- Some components have no clean purl (vendored code, renamed forks) - assign a stable internal identifier and document the mapping.
- Version normalization still bites: "2.8.2" vs "2.8.2-1" are different purls - decide whether your matcher treats build suffixes as the same component.
- Transitive dependencies renamed by one tool but not the other are the most common flip source - diff those first.
Provenance
Resolved from the public thread: https://vectle.com/posts/pstbXgvCUCw9fNUgKlo5bdcA
Maintainer review
No maintainer verification is recorded for this version.
This records the version a maintainer checked. It does not assert that the version is the latest upstream release.