diff --git a/PURL-SPECIFICATION.rst b/PURL-SPECIFICATION.rst index 0d043ed..ef28d4c 100644 --- a/PURL-SPECIFICATION.rst +++ b/PURL-SPECIFICATION.rst @@ -14,8 +14,7 @@ packaging conventions, tools, APIs and databases. Such a package URL is useful to reliably reference the same software package using a simple and expressive syntax and conventions based on familiar URLs. -See https://github.com/package-url/purl-spec for the Package URL specification -and ``_ for known type definitions. +See _ for known type definitions. Check also this short ``purl`` presentation (with video) at FOSDEM 2018 https://fosdem.org/2018/schedule/event/purl/ for an overview. @@ -105,7 +104,7 @@ A ``purl`` is a URL - Version control system (VCS) URLs such ``git://``, ``svn://``, ``hg://`` or as defined in Python pip or SPDX download locations are NOT valid ``purl`` types. They are valid URL or URI schemes but they are not ``purl``. - They are a closely related, compact and uniform way to reference vcs URLs. + They are a closely related, compact and uniform way to reference VCS URLs. They may be used as references in separate attributes outside of a ``purl`` or in a ``purl`` qualifier. @@ -151,7 +150,7 @@ The rules for each component are: - The package ``type`` is composed only of ASCII letters and numbers, '.', '+' and '-' (period, plus, and dash) - The ``type`` cannot start with a number - - The ``type`` cannot contains spaces + - The ``type`` cannot contain spaces - The ``type`` must NOT be percent-encoded - The ``type`` is case insensitive. The canonical form is lowercase @@ -187,7 +186,7 @@ The rules for each component are: - A ``version`` must be a percent-encoded string - A ``version`` is a plain and opaque string. Some package ``types`` use versioning - conventions such as semver for NPMs or nevra conventions for RPMS. A ``type`` + conventions such as SemVer for NPMs or NEVRA conventions for RPMS. A ``type`` may define a procedure to compare and sort versions, but there is no reliable and uniform way to do such comparison consistently. @@ -210,7 +209,7 @@ The rules for each component are: - A ``key`` cannot start with a number - A ``key`` must NOT be percent-encoded - A ``key`` is case insensitive. The canonical form is lowercase - - A ``key`` cannot contains spaces + - A ``key`` cannot contain spaces - A ``value`` must be a percent-encoded string - The '=' separator is neither part of the ``key`` nor of the ``value`` @@ -282,7 +281,7 @@ To build a ``purl`` string from its components: - Start a ``purl`` string with the "pkg:" ``scheme`` as a lowercase ASCII string -- Append the ``type`` string to the ``purl`` as a lowercase ASCII string +- Append the ``type`` string to the ``purl`` as a lowercase ASCII string - Append '/' to the ``purl`` @@ -319,15 +318,15 @@ To build a ``purl`` string from its components: - Append '?' to the ``purl`` - Build a list from all key/value pair: - - discard any pair where the ``value`` is empty. + - Discard any pair where the ``value`` is empty. - UTF-8-encode each ``value`` if needed in your programming language - If the ``key`` is ``checksums`` and this is a list of ``checksums`` join this list with a ',' to create this qualifier ``value`` - - create a string by joining the lowercased ``key``, the equal '=' sign and + - Create a string by joining the lowercased ``key``, the equal '=' sign and the percent-encoded ``value`` to create a qualifier - - sort this list of qualifier strings lexicographically - - join this list of qualifier strings with a '&' ampersand + - Sort this list of qualifier strings lexicographically + - Join this list of qualifier strings with a '&' ampersand - Append this string to the ``purl`` - If the ``subpath`` is not empty and not composed only of empty, '.' and '..' @@ -360,7 +359,7 @@ To parse a ``purl`` string in its components: - Strip the right side from leading and trailing '/' - Split this on '/' - Discard any empty string segment from that split - - Discard any '.' or '..' segment from that split + - Discard any '.' or '..' segment from that split - Percent-decode each segment - UTF-8-decode each segment if needed in your programming language - Join segments back with a '/' @@ -412,8 +411,7 @@ To parse a ``purl`` string in its components: - Discard any empty segment from that split - Percent-decode each segment - - UTF-8-decode the each segment if needed in your programming - language + - UTF-8-decode each segment if needed in your programming language - Apply type-specific normalization to each segment if needed - Join segments back with a '/' - This is the ``namespace`` @@ -434,14 +432,14 @@ identification to ensure that a ``purl`` stays compact and readable in most case Additional, separate external attributes stored outside of a ``purl`` are the preferred mechanism to convey extra long and optional information such as a -download URL, vcs URL or checksums in an API, database or web form. +download URL, VCS URL or checksums in an API, database or web form. With this warning, the known ``key`` and ``value`` defined here are valid for use in all package types: - ``repository_url`` is an extra URL for an alternative, non-default package - repository or registry. When a package does not come from the default public + repository or registry. When a package does not come from the default public package repository for its ``type`` a ``purl`` may be qualified with this extra URL. The default repository or registry of a ``type`` is documented in the "Known ``purl`` types" section. diff --git a/PURL-TYPES.rst b/PURL-TYPES.rst index 52a7ee8..ed6c460 100644 --- a/PURL-TYPES.rst +++ b/PURL-TYPES.rst @@ -198,9 +198,9 @@ cpan ---- ``cpan`` for CPAN Perl packages: -- The default respository is ``https://www.cpan.org/``. +- The default repository is ``https://www.cpan.org/``. - The ``namespace``: - - To refer to a CPAN distribution name, the ``namespace`` MUST be present. In this case, the namespace is the CPAN id of the author/publisher. It MUST be written uppercase, followed by the distribution name in the ``name`` component. A distribution name may NEVER contain the string ``::``. + - To refer to a CPAN distribution name, the ``namespace`` MUST be present. In this case, the namespace is the CPAN id of the author/publisher. It MUST be written uppercase, followed by the distribution name in the ``name`` component. A distribution name MUST NOT contain the string ``::``. - To refer to a CPAN module, the ``namespace`` MUST be absent. The module name MAY contain zero or more ``::`` strings, and the module name MUST NOT contain a ``-`` - The ``name`` is the module or distribution name and is case sensitive. @@ -208,7 +208,7 @@ cpan - Optional qualifiers may include: - ``repository_url``: CPAN/MetaCPAN/BackPAN/DarkPAN repository base URL (default is ``https://www.cpan.org``) - - ``download_url``: URL of package or distibution + - ``download_url``: URL of package or distribution - ``vcs_url``: extra URL for a package version control system - ``ext``: file extension (default is ``tar.gz``) @@ -278,7 +278,7 @@ docker gem --- -``gem`` for Rubygems: +``gem`` for RubyGems: - The default repository is ``https://rubygems.org``. - The ``platform`` qualifiers key is used to specify an alternative platform. @@ -400,7 +400,7 @@ luarocks The full version number is required to uniquely identify a version. - Qualifier ``repository_url``: The LuaRocks rocks server to be used; useful in case a private server is used (optional). - If ommitted, ``https://luarocks.org`` as default server is assumed. + If omitted, ``https://luarocks.org`` as default server is assumed. Examples:: @@ -545,9 +545,16 @@ pypi - PyPI treats ``-`` and ``_`` as the same character and is not case sensitive. Therefore a PyPI package ``name`` must be lowercased and underscore ``_`` replaced with a dash ``-``. +- The ``file_name`` qualifier selects a particular distribution file + (case-sensitive). For naming convention, see the Python Packaging User Guide on + `source distributions `_, + `binary distributions `_, + and `platform compatibility tags `_. - Examples:: pkg:pypi/django@1.11.1 + pkg:pypi/django@1.11.1?filename=Django-1.11.1.tar.gz + pkg:pypi/django@1.11.1?filename=Django-1.11.1-py2.py3-none-any.whl pkg:pypi/django-allauth@12.23 rpm diff --git a/README.rst b/README.rst index 2781828..222a0f2 100644 --- a/README.rst +++ b/README.rst @@ -56,7 +56,7 @@ A `purl` or package URL is an attempt to standardize existing approaches to reliably identify and locate software packages. A `purl` is a URL string used to identify and locate a software package in a -mostly universal and uniform way across programing languages, package managers, +mostly universal and uniform way across programming languages, package managers, packaging conventions, tools, APIs and databases. Such a package URL is useful to reliably reference the same software package @@ -124,7 +124,7 @@ Some `purl` examples pkg:golang/google.golang.org/genproto#googleapis/api/annotations pkg:maven/org.apache.xmlgraphics/batik-anim@1.9.1?packaging=sources - pkg:maven/org.apache.xmlgraphics/batik-anim@1.9.1?repository_url=repo.spring.io%2Frelease + pkg:maven/org.apache.xmlgraphics/batik-anim@1.9.1?repository_url=repo.spring.io/release pkg:npm/%40angular/animation@12.3.1 pkg:npm/foobar@12.3.1 @@ -156,35 +156,39 @@ type definitions: Known implementations ~~~~~~~~~~~~~~~~~~~~~ -- in Golang: https://github.com/package-url/packageurl-go -- for .NET: https://github.com/package-url/packageurl-dotnet -- for the JVM: https://github.com/package-url/packageurl-java, +- .NET: https://github.com/package-url/packageurl-dotnet +- Elixir: https://github.com/maennchen/purl +- Go: https://github.com/package-url/packageurl-go +- Java: https://github.com/package-url/packageurl-java, https://github.com/sonatype/package-url-java -- in Python: https://github.com/package-url/packageurl-python -- in Rust: https://github.com/package-url/packageurl.rs -- in JS: https://github.com/package-url/packageurl-js -- in Elixir: https://github.com/jshmrtn/purl -- in Perl: https://github.com/giterlizzi/perl-URI-PackageURL +- JavaScript: https://github.com/package-url/packageurl-js +- Perl: https://github.com/giterlizzi/perl-URI-PackageURL +- PHP: https://github.com/package-url/packageurl-php +- Python: https://github.com/package-url/packageurl-python +- Ruby: https://github.com/package-url/packageurl-ruby +- Rust: https://github.com/package-url/packageurl.rs +- Swift: https://github.com/package-url/packageurl-swift -Users, adopters and links -~~~~~~~~~~~~~~~~~~~~~~~~~ +Users, adopters and links (alphabetical order) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- `CycloneDX `_: A lightweight software + bill-of-material (SBOM) specification - `GitHub Dependency Submission API `_: allows third-party tools to submit dependency data to GitHub for inclusion in a repository's dependency graph. -- `Scancode Toolkit `_: Reports - `purl` from parsed package manifests using https://github.com/package-url/packageurl-python -- `OWASP Dependency-Track `_: +- `OWASP Dependency-Track `_: Open source component analysis platform -- `CycloneDX `_: A lightweight software - bill-of-material (SBOM) specification -- `SPDX `_: A data exchange standard for human-readable and - machine-processable software bill-of-materials (SBOM) - `OSS Index `_: A free catalog of Open Source Components and scanning tools to help developers identify vulnerable components -- `Sonatype Nexus Lifecycle `_: - Enterprise grade Open Source component management - `OSV Schema `_ and `OSV.dev `_: Open Source Vulnerability Schema and distributed vulnerability database +- `Scancode Toolkit `_: Reports + `purl` from parsed package manifests using https://github.com/package-url/packageurl-python +- `Sonatype Nexus Lifecycle `_: + Enterprise grade Open Source component management +- `SPDX `_: A data exchange standard for human-readable and + machine-processable software bill-of-materials (SBOM) License ~~~~~~~ diff --git a/VERSION-RANGE-SPEC.rst b/VERSION-RANGE-SPEC.rst index 945fd43..b53ed5c 100644 --- a/VERSION-RANGE-SPEC.rst +++ b/VERSION-RANGE-SPEC.rst @@ -59,9 +59,9 @@ conventions in use: - ``semver`` https://semver.org/ is a popular specification to structure version strings, but does not provide a way to express version ranges. -- Rubygems strongly suggest using ``semver`` for version but does not enforce it. +- RubyGems strongly suggest using ``semver`` for version but does not enforce it. As a result some gem use semver while several popular package do not use - strict semver. Rubygems use their own notation for version ranges which + strict semver. RubyGems use their own notation for version ranges which looks like the ``node-semver`` notation with some subtle differences. See https://guides.rubygems.org/patterns/#semantic-versioning @@ -115,7 +115,7 @@ conventions in use: version. And also provides a concrete enumeration of the available ranges as a daily feed. -- The version 5 of the NVD CVE JSON data format at +- The version 5 of the CVE JSON data format at https://github.com/CVEProject/cve-schema/blob/master/schema/v5.0/CVE_JSON_5.0.schema#L303 defines version ranges with a starting version, a versionType, and an upper limit for the version range as lessThan or lessThanOrEqual; or an enumeration @@ -142,7 +142,7 @@ related topic: - For instance, ``semver`` is a prominent specification in this domain but this is just one of the many ways to structure a version string. -- Debian, RPM, PyPI, Rubygems, and Composer have their own subtly different +- Debian, RPM, PyPI, RubyGems, and Composer have their own subtly different approach on how to determine how two versions are compared as equal, greater or lesser. @@ -260,7 +260,7 @@ Note how the constraints are sorted: - ``vers:tomee/>=7.1.0|<=7.1.2`` - ``vers:tomee/>=8.0.0-M1|<=8.0.1`` -Conversing Rubygems custom syntax for dependency on gem. Note how the +Conversing RubyGems custom syntax for dependency on gem. Note how the pessimistic version constraint is expanded: - ``'library', '~> 2.2.0', '!= 2.2.1'`` @@ -603,9 +603,9 @@ These are a few known versioning schemes for some common Package URL Debian uses these comparators: <<, <=, =, >= and >>. - **rpm**: RPM distros https://rpm-software-management.github.io/rpm/manual/dependencies.html - The a simplified rmpvercmp version comparison routine is used by archlinux Pacman. + The a simplified rmpvercmp version comparison routine is used by Arch Linux Pacman. -- **gem**: Rubygems https://guides.rubygems.org/patterns/#semantic-versioning +- **gem**: RubyGems https://guides.rubygems.org/patterns/#semantic-versioning which is similar to ``node-semver`` for its syntax, but does not use semver versions. @@ -692,7 +692,7 @@ Why not reuse existing version range notations? 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 +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. @@ -752,7 +752,7 @@ most vulnerable ranges yet: and vulnerable ranges when a version must be excluded and the set of existing versions is not yet known, -- this make some ranges more verbose such as with the NVD CVE v5 API ranges +- this make some ranges more verbose such as with the CVE v5 API ranges notation that can include their upper limit and would need two constraints. Another high level difference between the two specifications are the @@ -761,7 +761,7 @@ 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. -Why not use the NVD CVE v5 API Ranges? +Why not use the CVE v5 API Ranges? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ See: @@ -769,7 +769,7 @@ See: - https://github.com/CVEProject/cve-schema/blob/master/schema/v5.0/CVE_JSON_5.0_schema.json#L303 - https://github.com/CVEProject/cve-schema/blob/master/schema/v5.0/CVE_JSON_5.0_schema.json#L123 -The version 5 of the NVD CVE JSON data format defines version ranges with a +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 @@ -778,16 +778,16 @@ of this specification and both seem strictly equivalent to the Package URL 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 NVD v5 range to its notation and further +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 NVD CVE v5 API schema spec and its authors. +informed by the CVE v5 API schema spec and its authors. -When NVD v5 becomes active, this spec will provide a strict mapping between the -NVD versionType and the ``vers`` versioning schemes values. Furthermore, this +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 NVD collectionURL that will be effectively used. +a mapping with the upcoming CVE ``collectionURL`` that will be effectively used. -There is one issue with NVD v5: it introduces a new trailing "*" notation that +There is one issue with CVE v5: it introduces a new trailing "*" notation that does not exists in most version ranges notations and may not be computable easily in many cases. The description of the "lessThan" property is: @@ -806,7 +806,7 @@ The conversion to ``vers`` range should be: computed for ``semver`` versions as ``>=1.0|<2`` but is not accurate unless as 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 NVD spec means: + of the v2. branch. It is not clear if with "2.*" the CVE v5 spec means: - ``<2`` - or something that excludes any version string that starts with ``2.`` @@ -900,14 +900,14 @@ aspects specific to the versions used only in the Python ecosystem. difficult to express without an "OR" logic. -Why not use Rubygems requirements notation? +Why not use RubyGems requirements notation? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ See: - https://guides.rubygems.org/patterns/#declaring-dependencies -The Rubygems specification suggests but does not enforce using semver. It uses +The RubyGems specification suggests but does not enforce using semver. It uses operators similar to the ``node-semver`` spec with the different of the "~>" aka. pessimistic operator vs. a plain "~" tilde used in node-semver. This operator implies some semver-like versioning, yet gem version are not strictly diff --git a/test-suite-data.json b/test-suite-data.json index 25276e7..d2498ad 100644 --- a/test-suite-data.json +++ b/test-suite-data.json @@ -658,5 +658,77 @@ "qualifiers": null, "subpath": null, "is_invalid": true + }, + { + "description": "cpan distribution name are case sensitive", + "purl": "pkg:cpan/DROLSKY/DateTime@1.55", + "canonical_purl": "pkg:cpan/DROLSKY/DateTime@1.55", + "type": "cpan", + "namespace": "DROLSKY", + "name": "DateTime", + "version": "1.55", + "qualifiers": null, + "subpath": null, + "is_invalid": false + }, + { + "description": "cpan module name are case sensitive", + "purl": "pkg:cpan/URI::PackageURL@2.11", + "canonical_purl": "pkg:cpan/URI::PackageURL@2.11", + "type": "cpan", + "namespace": null, + "name": "URI::PackageURL", + "version": "2.11", + "qualifiers": null, + "subpath": null, + "is_invalid": false + }, + { + "description": "cpan module name like distribution name", + "purl": "pkg:cpan/Perl-Version@1.013", + "canonical_purl": "pkg:cpan/Perl-Version@1.013", + "type": "cpan", + "namespace": null, + "name": "Perl-Version", + "version": "1.013", + "qualifiers": null, + "subpath": null, + "is_invalid": true + }, + { + "description": "cpan distribution name like module name", + "purl": "pkg:cpan/GDT/URI::PackageURL@2.11", + "canonical_purl": "pkg:cpan/GDT/URI::PackageURL", + "type": "cpan", + "namespace": "GDT", + "name": "URI::PackageURL", + "version": null, + "qualifiers": null, + "subpath": null, + "is_invalid": true + }, + { + "description": "cpan valid module name", + "purl": "pkg:cpan/DateTime@1.55", + "canonical_purl": "pkg:cpan/DateTime@1.55", + "type": "cpan", + "namespace": null, + "name": "DateTime", + "version": "1.55", + "qualifiers": null, + "subpath": null, + "is_invalid": false + }, + { + "description": "cpan valid module name without version", + "purl": "pkg:cpan/URI", + "canonical_purl": "pkg:cpan/URI", + "type": "cpan", + "namespace": null, + "name": "URI", + "version": null, + "qualifiers": null, + "subpath": null, + "is_invalid": false } ]