?um/p1-90`This specification is a new syntax for dependency and vulnerable version ranges.
A version range specifier (aka. "vers") is a URI string using the vers URI-scheme with this syntax:
vers:<versioning-scheme>/<version-constraint>|<version-constraint>|...
For example, to define a set of versions that contains either version 1.2.3, or any versions greater than or equal to 2.0.0 but less than 5.0.0 using the node-semver versioning scheme used with the npm Package-URL type, the version range specifier will be:
vers:npm@1.2.3|≥=2.0.0|<5.0.0
vers is the URI-scheme and is an acronym for "VErsion Range Specifier". It has been selected because it is short, obviously about version and available for a future formal URI-scheme registration at IANA. The pipe "|" is used as a simple separator between `<version-constraint>`. Each `<version-constraint>` in this pipe-separated list contains a comparator and a version:
<comparator:version>
This list of <version-constraint> are signposts in the version timeline of a package that specify version intervals.
A <version> satisfies a version range specifier if it is contained within any of the intervals defined by these <version-constraint>.
vers primary usage is to test if a version is within a range.
A version is within a version range if falls in any of the intervals defined by a range. Otherwise, the version is outside of the version range.
Some important usages derived from this include:
vers describing the vulnerable versions of a package, this process is used to determine if an existing package version is vulnerable.vers could be used in tandem with purl to provide an input to this dependencies resolution process.A single version in an npm package dependency:
vers:npm/1.2.3A list of versions, enumerated:
vers:pypi/0.0.0|0.0.1|0.0.2|0.0.3|1.0|2.0pre1A complex statement about a vulnerability in a "maven" package that affects multiple branches each with their own fixed versions at https://repo1.maven.org/maven2/org/apache/tomee/apache-tomee/ Note how the constraints are sorted:
vers:maven/>=1.0.0-beta1|<=1.7.5|>=7.0.0-M1|<=7.0.7|>=7.1.0|<=7.1.2|>=8.0.0-M1|<=8.0.1vers express the same range, using one vers for each vulnerable "branches":
vers:tomee/>=1.0.0-beta1|<=1.7.5vers:tomee/>=7.0.0-M1|<=7.0.7vers:tomee/>=7.1.0|<=7.1.2vers:tomee/>=8.0.0-M1|<=8.0.1Converting RubyGems custom syntax for dependency on gem. Note how the pessimistic version constraint is expanded:
'library', '~> 2.2.0', '!= 2.2.1'vers:gem/>=2.2.0|!= 2.2.1|<2.3.0The vers URI scheme is an acronym for "VErsion Range Specifier". It has been selected because it is short, obviously about version and available for a future formal registration for this URI-scheme at the IANA registry.
The URI scheme is followed by a colon ":".
<versioning-scheme>The <versioning-scheme> (such as npm, deb, etc.) determines:
vers simplified notation defined here.By convention the versioning scheme should be the same as the Package-URL package type for a given package ecosystem. It is OK to have other schemes beyond the purl type. A scheme could be specific to a single package name.
The <versioning-scheme> is followed by a slash "/".
<version-constraint>After the <versioning-scheme> and "/" there are one or more <version-constraint> separated by a pipe "|". The pipe "|" has no special meaning beside being a separator.
Each <version-constraint> of this list is either a single <version> as in 1.2.3 for example or the combination of a <comparator> and a <version> as in >=2.0.0 using this syntax:
<comparator><version>
A single version means that a version equal to this version satisfies the range spec. Equality is based on the equality of two normalized version strings according to their versioning scheme. For most schemes, this is a simple string equality. But schemes can specify normalization and rules for equality such as pypi with PEP440.
The special star "*" comparator matches any version. It must be used alone exclusive of any other constraint and must not be followed by a version. For example "vers:deb/*" represents all the versions of a Debian package. This includes past, current and possible future versions.
Otherwise, the <comparator> is one of these comparison operators:
The <versioning-scheme> defines:
semver specification for version numbers defines a version as composed primarily of three dot-separated numeric segments named major, minor and patch.The construction and validation rules are designed such that a vers is easier to read and understand by humans and straightforward to process by tools, attempting to avoid the creation of empty or impossible version ranges.
vers:npm.version in a <version-constraint> contains separator or comparator characters (i.e. ><=!*|), it must be quoted using the URL quoting rules. This should be rare in practice.The list of <version-constraint>s of a range are signposts in the version timeline of a package. With these few and simple validation rules, we can avoid the creation of most empty or impossible version ranges:
<version-constraint> is not significant otherwise but this sort order is needed when checking if a version is contained in a range.version must be unique in a range and can occur only once in any <version-constraint> of a range specifier, irrespective of its comparators. Tools must report an error for duplicated versions.Starting from a de-duplicated and sorted list of constraints, these extra rules apply to the comparators of any two contiguous constraints to be valid:
Ignoring all constraints with "!=" comparators:
And ignoring all constraints with "=" or "!=" comparators, the sequence of constraint comparators must be an alternation of greater and lesser comparators:
Tools must report an error for such invalid ranges.
To parse a version range specifier string:
vers.<versioning-scheme> that must be lowercase. Tools should validate that the <versioning-scheme> is a known scheme.<version-constraint> is "*". Parsing is done and no further processing is needed for this vers. A tool should report an error if there are extra characters beyond "*".<version-constraint>. Consecutive pipes must be treated as one and leading and trailing pipes ignored.<version-constraint>:
<version-constraint> starts with one of the two comparators:
<version-constraint> string start. The remaining string is the version.<version-constraint> string (which implies an equality comparator of "=")Finally:
<versioning-scheme> and the list of <comparator, version> constraints.Tools should optionally validate and simplify the list of <comparator, version> constraints once parsing is complete:
Tools can simplify a list of <version-constraint> using this approach:
These pairs of contiguous constraints with these comparators are valid:
These pairs of contiguous constraints with these comparators are redundant and invalid (ignoring any != since they can show up anywhere):
A procedure to remove redundant constraints can be:
To check if a "tested version" is contained within a version range:
semver version of an npm and the deb version of its Debian packaging, the way versions are compared specific to each versioning scheme and may be different. Tools should report an error in this case.These are a few known versioning schemes for some common Package-URL types (aka. ecosystem).
node-semver for its syntax, but does not use semver versions.node-semver-like ranges. But most of these related schemes are not strictly the same as what is implemented in node-semver. For instance PHP composer may need its own scheme as this is not strictly node-semver.semver versions with a specific minimum version resolution algorithm.These are generic schemes, to use sparingly for special cases:
vers:none/* is the only valid vers form for this scheme.vers:all/* is the only valid vers form for this scheme.10.234.5.12. Versions specified in this scheme consist of ASCII digits only, formatted with only non-negative integers, and ignoring leading zeros. Interpretation of the version should stop at the first character that is not a digit or a dot.semver. It follows the MAJOR.MINOR.PATCH format and is defined in the Semantic Versioning Specification 2.0.0, see https://semver.org/spec/v2.0.0.html.A separate document will provide details for each versioning scheme and:
vers notation and back.This versioning schemes document will also explain how to convert CVE and OSV ranges to vers.
Most existing version range notations are tied to a specific version string syntax and are therefore not readily applicable to other contexts. For example, the use of elements such as tilde and caret ranges in RubyGems, npm or Dart notations implies that a certain structure exists in the version string (semver or semver- like). The inclusion of these additional comparators is a result of the history and evolution in a given package ecosystem to address specific needs.
In practice, the unified and reduced set of comparators and syntax defined for vers has been designed such that all these notations can be converted to a vers and back from a vers to the original notation.
In contrast, this would not be possible with existing notations. For instance, the Python notation may not work with npm semver versions and reciprocally.
There are likely to be a few rare cases where round tripping from and to vers may not be possible, and in any case round tripping to and from vers should produce equivalent results and even if not strictly the same original strings.
Another issue with existing version range notations is that they are primarily designed for dependencies and not for vulnerable ranges. In particular, a vulnerability may exist for multiple "version branches" of a given package such as with Django 2.x and 3.x. Several version range notations have difficulties to communicate these as typically all the version constraints must be satisfied. In contrast, a vulnerability can affect multiple disjoint version ranges of a package and any version satisfying these constraints would be vulnerable: it may not be possible to express this with a notation designed exclusively for dependent versions resolution.
Finally, one of the goals of this spec is to be a compact yet obvious Package-URL companion for version ranges. Several existing and closely related notations designed for vulnerable ranges are verbose specifications designed for use in API with larger JSON documents.
See:
vers and the OSSF OSV schema vulnerable ranges are equivalent and vers provides a compact range notation while OSV provides a more verbose JSON notation.
vers borrows the design from and was informed by the OSV schema spec and its authors.
OSV uses a minimalist set of only three comparators:
OSV Ranges support neither ">" nor "!=" comparators making it difficult to express some ranges that must exclude a version. This may not be an issue for most vulnerable ranges yet:
Another high level difference between the two specifications is the codes used to qualify a range package "ecosystem" value that resembles closely the Package-URL package "type" used in vers. This spec will provide a strict mapping between the OSV ecosystem and the vers versioning schemes values.
See:
The version 5 of the CVE JSON data format defines version ranges with a starting version, a versionType, and an upper limit for the version range as lessThan or lessThanOrEqual or as an enumeration of versions. The versionType and the package collectionURL possible values are only indicative and left out of this specification and both seem strictly equivalent to the Package-URL "type" on the one hand and thevers versioning scheme on the other hand.
The semantics and expressiveness of each range are similar and vers provides a compact notation rather than a more verbose JSON notation. vers supports strictly the conversion of any CVE v5 range to its notation and further provides a concrete list of well known versioning schemes. vers design was informed by the CVE v5 API schema spec and its authors.
When CVE v5 becomes active, this spec will provide a strict mapping between the CVE versionType and the vers versioning schemes values. Furthermore, this spec and the Package-URL "types" should be updated accordingly to provide a mapping with the upcoming CVE collectionURL that will be effectively used.
There is one issue with CVE v5: it introduces a new trailing "*" notation that does not exist in most version ranges notations and may not be computable easily in many cases. The description of the "lessThan" property is:
The non-inclusive upper limit of the range. This is the least version NOT in the range. The usual version syntax is expanded to allow a pattern to end in an asterisk (
), indicating an arbitrarily large number in the version ordering. For example, {version: 1.0 lessThan: 1. } would describe the entire 1.X branch for most range kinds, and {version: 2.0, lessThan: *} describes all versions starting at 2.0, including 3.0, 5.1, and so on.
The conversion to vers range should be:
vers equivalent is: >=1.0.vers equivalent can be computed for semver versions as >=1.0|<2 but this is not accurate because the versioning schemes have different rules. For instance, pre-release may be treated in some case as part of the v1. branch and in some other cases as part of the v2. branch. It is not clear if with "2.*" the CVE v5 spec means:
<22.And in this case, with the expression "lessThan": "2.*" using a semver version, it is not clear if 2.0.0-alpha is "lessThan"; semver sorts it before 2.0 and after 1.0, e.g., in semver 2.0.0-alpha is "less than" 2.
See:
The version ranges notation defined in the JSON schema of the CVE API payload uses these four fields: versionStartIncluding, versionStartExcluding, versionEndIncluding and versionEndExcluding. For example:
"versionStartIncluding": "7.3.0",
"versionEndExcluding": "7.3.31",
"versionStartExcluding" : "9.0.0",
"versionEndIncluding" : "9.0.46",
In addition to these ranges, the NVD publishes a list of concrete CPEs with versions resolved for a range with daily updates at https://nvd.nist.gov/vuln/data-feeds#cpeMatch
Note that the NVD CVE configuration is a complex specification that goes well beyond version ranges and is used to match comprehensive configurations across multiple products and version ranges. vers focus is exclusively versions.
In contrast with vers compact notation, the NVD JSON notation is more verbose, yet vers supports strictly the conversion of any CPE range.
See:
The node-semver spec is similar to but much more complex than this spec. This is an AND of ORs constraints with a few practical issues:
vers avoids the ambiguity of spaces by ignoring them.Notations that are directly derived from node-semver as used in Rust and PHP Composer have the same issues.
See:
The Python pep-0440 "Version Identification and Dependency Specification" provides a comprehensive specification for Python package versioning and a notation for "version specifiers" to express the version constraints of dependencies.
This specification is similar to this vers spec, with more operators and aspects specific to the versions used only in the Python ecosystem.
See:
The RubyGems specification suggests but does not enforce using semver. It uses operators similar to the node-semver spec with the difference of the "
For instance, the OSV schema adopts a reduced set of only three comparators:
This approach is simpler and works well for most vulnerable ranges but it faces limitations when converting from other notations:
Some existing notations such as used with npm, gem, python, or composer provide syntactic shorthand such as:
Most of these notations can be converted without loss to the vers notation. Furthermore these notations typically assume a well defined version string structure specific to their package ecosystem and are not reusable in another ecosystem that would not use the exact same version conventions.
For instance, the tilde and caret notations demand that you can reliably infer the next version (aka. "bump") from a given version; this is possible only if the versioning scheme supports this operation reliably for all its accepted versions.
Apache Maven and NuGet use a mathematical interval notation with comma-separated "[", "]", "(" and ")" to declare version ranges.
All other known range notations use the more common ">", "<", and "=" as comparators. vers adopts this familiar approach.
Here are some of the discussions that led to the creation of this specification:
This document is licensed under the MIT license