You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
MavenVersion, NugetVersion and RubygemsVersion accept the empty string "" as a valid version, while SemverVersion, PypiVersion and others correctly raise InvalidVersion. Worse, the accepted empty version then sorts below every real version, so downstream range logic silently produces a confident but wrong answer rather than failing.
Three different behaviours across schemes, none of them an error for the first group:
MavenVersion("") <MavenVersion("0.0.1") # True -- sorts as minimumRubygemsVersion("") <RubygemsVersion("0.0.1") # True -- sorts as minimumNugetVersion("") <NugetVersion("0.0.1") # TypeError:# '<' not supported between instances of 'NoneType' and 'Version'
So NugetVersion accepts the value at construction and then raises on comparison, which is a third distinct outcome.
Why the silent ordering is the harmful part
Consider an OSV range built from a malformed advisory:
Because MavenVersion("") is accepted and sorts as the minimum, the interval [0, "") evaluates to the empty set. Code comparing this against the advisory's previous range concludes, with no error and no warning, that the affected set was reduced to nothing — i.e. that the range was retracted.
We hit this in a measurement study over the git history of the GitHub Advisory Database. GHSA emitted schema-invalid {"fixed": ""} into 2,107 advisory revisions during a single week in March 2023. Because three of the ecosystems we needed silently accepted it, our comparator produced roughly 1,000 spurious "affected range narrowed" events in Maven alone — a confidently wrong classification, not a crash we could catch. The npm/PyPI/Go paths were unaffected precisely because they raise InvalidVersion.
The general hazard: a caller cannot rely on "the version parsed successfully" as a validity check, and the failure is scheme-dependent, so it shows up only in some ecosystems and looks like real data.
Suggested resolution
Whatever the decision on leniency, the three schemes should agree with each other and with the rest of the library. Options, in our order of preference:
Raise InvalidVersion for "" in MavenVersion, NugetVersion and RubygemsVersion, matching SemverVersion/PypiVersion. An empty string is not a version in any of these ecosystems' specs.
If empty is to be accepted deliberately (see parse_version() should accept None and empty strings #10), make it consistent across all schemes and define its ordering explicitly rather than letting it fall out of the attribute tuple — the current NugetVersionTypeError on comparison shows the ordering is not well-defined today.
A test asserting identical "" behaviour across every Version subclass would prevent the schemes drifting apart again.
Related
parse_version() should accept None and empty strings #10parse_version() should accept None and empty strings — related but pointing the other way; this report is about the inconsistency between schemes and the silently wrong ordering, independent of which behaviour is chosen. The comment there that "None or empty would be the same... I am not sure this would make any sense" suggests option 1 is the intended direction.
Summary
MavenVersion,NugetVersionandRubygemsVersionaccept the empty string""as a valid version, whileSemverVersion,PypiVersionand others correctly raiseInvalidVersion. Worse, the accepted empty version then sorts below every real version, so downstream range logic silently produces a confident but wrong answer rather than failing.Tested with univers 32.0.1 on CPython 3.13.
Reproducer
Three different behaviours across schemes, none of them an error for the first group:
So
NugetVersionaccepts the value at construction and then raises on comparison, which is a third distinct outcome.Why the silent ordering is the harmful part
Consider an OSV range built from a malformed advisory:
{"type": "ECOSYSTEM", "events": [{"introduced": "0"}, {"fixed": ""}]}Because
MavenVersion("")is accepted and sorts as the minimum, the interval[0, "")evaluates to the empty set. Code comparing this against the advisory's previous range concludes, with no error and no warning, that the affected set was reduced to nothing — i.e. that the range was retracted.We hit this in a measurement study over the git history of the GitHub Advisory Database. GHSA emitted schema-invalid
{"fixed": ""}into 2,107 advisory revisions during a single week in March 2023. Because three of the ecosystems we needed silently accepted it, our comparator produced roughly 1,000 spurious "affected range narrowed" events in Maven alone — a confidently wrong classification, not a crash we could catch. The npm/PyPI/Go paths were unaffected precisely because they raiseInvalidVersion.The general hazard: a caller cannot rely on "the version parsed successfully" as a validity check, and the failure is scheme-dependent, so it shows up only in some ecosystems and looks like real data.
Suggested resolution
Whatever the decision on leniency, the three schemes should agree with each other and with the rest of the library. Options, in our order of preference:
InvalidVersionfor""inMavenVersion,NugetVersionandRubygemsVersion, matchingSemverVersion/PypiVersion. An empty string is not a version in any of these ecosystems' specs.NugetVersionTypeErroron comparison shows the ordering is not well-defined today.A test asserting identical
""behaviour across everyVersionsubclass would prevent the schemes drifting apart again.Related
parse_version() should accept None and empty strings— related but pointing the other way; this report is about the inconsistency between schemes and the silently wrong ordering, independent of which behaviour is chosen. The comment there that "None or empty would be the same... I am not sure this would make any sense" suggests option 1 is the intended direction.VersionRangewith empty constraint #203Do not allow creation of VersionRange with empty constraint— same family of problem one level up, at the range rather than the version.Happy to send a PR for option 1 plus the cross-scheme test if that is the direction you'd like.