A scanner decides a version is affected by running five checks: the ecosystem matches, the package name matches, the version falls inside an affected range, the advisory has not been withdrawn, and the finding is not a duplicate of another alias. Fail any check and the finding should not appear.
Check 1: ecosystem
The same name can exist in several registries. A package called "request" in one ecosystem is unrelated to one with that name in another. Advisory records therefore name the ecosystem alongside the package. The OSV schema puts both in the package object, along with an optional purl. A scanner that ignores ecosystem will produce false matches.
Check 2: package name
Names need normalising. Some ecosystems treat case or separators as equivalent, others do not. Scoped names, group IDs and namespaces add detail. A scanner must apply each ecosystem's rules, or it either misses real matches or invents false ones.
Check 3: version range
This is the core decision. OSV expresses ranges as ordered events: introduced, fixed, last_affected and limit. A version is affected if it is at or above an introduced event and below the matching fixed event. Comparison must use the ecosystem's own ordering. Semantic versions, Debian-style versions and date-based versions all sort differently, so a generic string comparison gives wrong answers.
Here is a plain example:
introduced: 1.2.0
fixed: 1.4.3
1.1.9 -> not affected (before introduced)
1.3.0 -> affected
1.4.3 -> not affected (fixed here)
Some records also list explicit versions instead of ranges. A careful matcher uses whichever the record provides.
Check 4: withdrawn records
Advisories sometimes get withdrawn. The OSV withdrawn field carries a timestamp, and a missing field means the entry has not been withdrawn. A scanner that keeps matching withdrawn records will report findings that the source itself no longer stands behind.
Check 5: aliases and duplicates
One vulnerability can appear in several databases under different IDs. The aliases field lets a record claim it describes the same issue as others. Matching against two databases without merging aliases doubles the count.
Source quality changes the result
Not all advisory data is checked equally. GitHub documents two kinds: reviewed advisories, which are mapped to packages in supported ecosystems and checked for validity and completeness, and unreviewed advisories, which are published automatically from the NVD feed and may lack package information. Where a source lacks ecosystem and package data, a scanner has less to match on.
A debugging checklist
When a finding looks wrong, ask in order:
- Is the ecosystem right?
- Is the package name the one the advisory means?
- What exact version did the scanner detect, and from which file?
- Does that version really fall in the range?
- Is the record current, or withdrawn?
- Is it the same issue as another finding?
Most disputed findings fail at step 3, because the detected version was a declared range rather than the installed one.
Where Safeguard fits
Safeguard's dependency scanning matches an SBOM inventory against vulnerability data, and reachability analysis then shows whether the affected code is actually called. You can try the free scan and inspect why a component was flagged.
Quick check
Why not just compare version strings?
Ecosystems order versions differently, so a plain string comparison can call 1.10 older than 1.9. Matchers must use each ecosystem's own rules.
What if a finding names a version I do not run?
Check which file the scanner read. If it used a manifest range instead of a lockfile or built artifact, it may have guessed the version.
Sources
Last reviewed: 19 October 2026.