Repository navigation
feat(versions): compare each ecosystem with its own version scheme (PEP 440 for PyPI) - #1397
Open
alex-powell wants to merge 1 commit into
Open
alex-powell wants to merge 1 commit into
alex-powell wants to merge 1 commit into
Conversation
compareVersions, looksLikeVersion, isPreReleaseVersion, and normalizeAdvisoryVersion keep their names. Passing a PyPI ecosystem selects PEP 440. Other callers stay on semver, including sort and filter callbacks that pass an index. A PyPI advisory whose only fix is 2026.3.post1 is reported. scripts/check-pep440.ts judges a pinned requirements file against OSV and compares a PyPI sample with packaging.
cursor
Bot
force-pushed
the
feat/pep440-version-scheme
branch
from
October 25, 2026 03:14
c65cdc5 to
52d1792
Compare
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Order-dependent advisory evaluation can miss vulnerable versions, its reference check repeats the error, and scanner fix selection still discards invalid PyPI fixed boundaries.
3 open issues
What changed in this PR
Adds ecosystem-aware version comparison for experimental Python support, using PEP 440 for PyPI while preserving the default semver behavior.
Changes:
- Adds PEP 440 parsing, normalization, ordering, and version-scheme selection.
- Passes ecosystem information through scanner fix selection and offline advisory matching.
- Adds regression tests and a requirements/PyPI validation script without new runtime dependencies.
| File | Description |
|---|---|
tsconfig.test.json |
Includes the validation script in test compilation. |
tests/utils/pep440.test.ts |
Tests parsing, ordering, schemes, and advisory evaluation. |
tests/utils/pep440-helpers.test.ts |
Tests PyPI dispatch through existing version helpers. |
tests/scripts/check-pep440-requirements.test.ts |
Tests requirements parsing and advisory reports. |
tests/scanner/pep440-fix-version.test.ts |
Tests PyPI fix selection and npm compatibility. |
tests/advisory/pep440-local-db.test.ts |
Tests offline PyPI matching and invalid boundaries. |
src/utils/versions/scheme.ts |
Defines the version-scheme contract and parse error. |
src/utils/versions/registry.ts |
Selects the scheme by ecosystem. |
src/utils/versions/pep440.ts |
Implements PEP 440 parsing and comparison. |
src/utils/versions/advisory.ts |
Adds advisory-bound and PyPI event evaluation. |
src/utils/version.ts |
Adds ecosystem-aware dispatch to existing helpers. |
src/scanner.ts |
Uses ecosystem-aware comparison for fix selection. |
src/advisory/local-db.ts |
Applies ecosystem-aware offline range matching. |
scripts/check-pep440.ts |
Adds requirements reports and Python reference comparisons. |
Review effort: Balanced
| if (event.fixed !== undefined) { | ||
| const fixed = boundary(event.fixed); | ||
| if (!fixed) unparsed.push({ field: "fixed", value: event.fixed }); | ||
| else if (pep440Scheme.compare(version, fixed) >= 0) affected = false; |
Comment on lines
+354
to
+355
| elif ver >= parsed: | ||
| affected = False |
Comment on lines
+1131
to
+1132
| const fixed = normalizeAdvisoryVersion(event.fixed, ecosystem); | ||
| if (!fixed || isPreReleaseVersion(fixed, ecosystem)) continue; |
| process.exit(1); | ||
| } | ||
| const code = requirements !== undefined ? await runRequirements(requirements) : await runSample(); | ||
| process.exit(code); |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Fixes #1401
Refs #672
What changed and why
PyPI versions now order with PEP 440, through the helpers that already live in
src/utils/version.ts.compareVersions,looksLikeVersion,isPreReleaseVersion, andnormalizeAdvisoryVersionkeep their names. A caller that has an ecosystem passes it.PyPIandpypiselect PEP 440. A missing ecosystem, and every other ecosystem, stays on semver.npm callers are unchanged.
sortandfilterstill receive these functions directly. The extra argument ignores the numeric index those callbacks pass, so npm ordering stays the same.The scanner and the offline database pass the ecosystem they already store. A PyPI advisory whose only fix is
2026.3.post1reports that fix. An unreadable PyPI boundary throws, so a miss stays visible.This change is the comparison step. Lockfile parsing, package URLs, exit codes, SBOMs, and the offline database shape are later steps.
Done when
Every form in the table parses, and the two orderings come out right, with tests for each.
tests/utils/pep440-helpers.test.tscalls the helpers with ecosystemPyPI.2.9.0.post02026.3.post11!2.0.04.0.0.dev11.0.0rc11.0.0a12.0.0b21.0.0.post1.dev21.2.3+localafter1.2.31.0.0rc1<1.0.01!0.1.0>2.0.0tests/scanner/pep440-fix-version.test.ts, "reports 2026.3.post1 when that is the only fix", callsfindFirstFixedVersionfor pytz at2026.3and expects2026.3.post1.The full suite passes with no existing test changed.
git diff upstream/main -- tests/lists only new files.npm teston this commit passed 183 suites and 2513 tests. That run is the npm parity check.tests/helpers.test.ts,tests/utils/version-extensions.test.ts,tests/output/parent-update-display.test.ts, andtests/remediation/fix-commands-parent-update-merge.test.tsmatch upstreamc5a5180.No new runtime dependency.
git diff upstream/main -- package.jsonis empty.Checked against PyPI and OSV
Node v22.23.4, from a checkout of this branch.
--requirementsprints JSON for each exact pin. Comments, blank lines, extras, and environment markers are ignored. Each pin is queried on OSV and judged with PEP 440.--samplecompares PyPI release lists and OSV ranges for python-dateutil, pytz, numpy, django, requests, urllib3, jinja2, pillow, cryptography, setuptools, idna, and packaging with packaging 26.3.2.9.0,2.9.0.post0,2026.3,2026.3.post1,1.0rc1,1!2.0,1.22, and1.22.0are checked against the same oracle and are left out of the seen and parsed totals.Re-run on
52d1792.Both parsers reject the same 45 pytz legacy tags,
2004dthrough2013d. The sampled release lists contain no epoch. Epoch ordering is covered by1!2.0in the explicit check and by the unit tests.Verified against a production requirements.txt
I ran
--requirementsagainst the pinnedrequirements.txtof the production Python backend I mentioned in #672, and compared it with pip-audit 2.10.1 on the same file. That run used Node 20.19.2 atc65cdc5.Both flag
opentelemetry-api==1.45.0. This branch reports GHSA-xcq7-p3pf-p88h. pip-audit reports CVE-2026-114603, fixed in 1.45.1. OSV lists them as aliases of the same advisory, so the two results agree. The other 19 pins are unaffected in both.That file has no post, dev, pre-release, epoch, or two-part versions, so this run confirms parity on a real tree. The new forms are covered by the sample run above. The one unusual form is the single-segment date version
pdfminer.six==20260107, which parses.