Skip to content

fix: treat a pre-release of the introduced version as in range - #1098

Merged
sonukapoor merged 1 commit into
mainfrom
bugfix/prerelease-introduced-bound
Sep 7, 2026
Merged

fix: treat a pre-release of the introduced version as in range#1098
sonukapoor merged 1 commit into
mainfrom
bugfix/prerelease-introduced-bound

Conversation

@sonukapoor

Copy link
Copy Markdown
Collaborator

Making compareVersions semver-correct in #1084 fixed the upper bound of an affected range but broke the lower one. A pre-release sorts below its release, so an install of 1.2.0-beta.1 fell outside a range introduced at 1.2.0 and stopped being reported. That build is a pre-release of 1.2.0 and carries whatever the advisory says was introduced there, so excluding it is a false negative in the opposite direction from the one #1084 fixed.

versionMatchesRange now treats a version as at or above the introduced bound when it is a pre-release whose release core equals that bound. compareVersions is untouched: it is used for sorting throughout the codebase and must stay spec-correct.

Why the relaxation stops where it does

The issue originally proposed comparing the lower bound against the release core. Checking real OSV data first ruled that out. Across 15 sampled npm packages, 64 of 352 introduced events are themselves pre-releases, and Next.js does it routinely:

GHSA-4342-x723-ch2f  next  introduced=15.0.0-canary.0
GHSA-5f7q-jpqc-wp7h  next  introduced=15.0.1-canary.0  fixed=15.6.0-canary.61

Widening those to their release core would pull in earlier canaries the advisory explicitly excluded, trading this false negative for a false positive on the highest-profile package in our advisory set. So the relaxation applies only when the bound is a stable release. No explicit guard is needed: a release core never contains a hyphen, so it can never equal a pre-release bound.

Verification

Before and after, through the real CLI against a real offline advisory database seeded with demo-pkg [1.2.0, 1.3.0), scanning an install of demo-pkg@1.2.0-beta.1:

BEFORE (main):  Scan complete. No known vulnerabilities found.
AFTER:          demo-pkg@1.2.0-beta.1 flagged, > npm install demo-pkg@1.3.0

19 tests in local-advisory-source.test.ts, covering the stable-boundary case, the last_affected interaction, and four Next.js canary shapes that must keep comparing exactly. Removing the relaxation fails two of them.

Scope

Offline only. versionMatchesRange lives in local-db.ts; the online path goes through the OSV API, which does its own range matching. Affects --offline and --offline-db.

Like #1084 and #1085, this makes offline scans report more findings for pre-release installs. Anyone on a canary or rc with a --ratchet baseline may see new findings and need to regenerate. Already noted in the release tracker.

Closes #1086

…ange

Making compareVersions semver-correct in #1084 fixed the upper bound of an
affected range but broke the lower one. A pre-release sorts below its
release, so an install of 1.2.0-beta.1 fell outside a range introduced at
1.2.0 and stopped being reported. In practice that build is a pre-release
of 1.2.0 and carries whatever the advisory says was introduced there, so
excluding it is a false negative in the opposite direction from the one
#1084 fixed.

versionMatchesRange now treats a version as at or above the introduced
bound when it is a pre-release whose release core equals that bound.
compareVersions is untouched: it is used for sorting throughout the
codebase and must stay spec-correct.

The relaxation deliberately cannot widen a pre-release lower bound. OSV
declares plenty of those: 64 of 352 introduced events across 15 sampled
packages, with Next.js using values like 15.0.1-canary.0 routinely. Those
are precise canary boundaries, and widening them would pull in earlier
canaries the advisory excluded, trading this false negative for a false
positive. No guard is needed because a release core never contains a
hyphen, so it can never equal a pre-release bound.

Verified end to end against a real offline advisory database: main
reports "No known vulnerabilities found" for a vulnerable pre-release
install, this branch flags it and emits a working fix command.

Closes #1086
@sonukapoor
sonukapoor merged commit a2716ca into main Sep 7, 2026
6 checks passed
@sonukapoor
sonukapoor deleted the bugfix/prerelease-introduced-bound branch September 7, 2026 14:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(detection): pre-release of the introduced version falls outside its own affected range

1 participant