Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions AUTHORS.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The following organizations or individuals have contributed to this repo:

- Shivam Sandbhor
- nexB Inc.
- Philippe Ombredanne
- Shivam Sandbhor @sbs2001
- Philippe Ombredanne @pombredanne
- Hritik Vijay @Hritik14

148 changes: 100 additions & 48 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,70 +5,116 @@ univers: mostly universal version and version ranges comparison and conversion

.. |Build Status| image:: https://api.travis-ci.com/sbs2001/univers.svg?branch=main&status=passed
.. |License| image:: https://img.shields.io/badge/License-Apache%202.0-blue.svg
:target: https://opensource.org/licenses/Apache-2.0
:target: https://scancode-licensedb.aboutcode.org/apache-2.0.html
.. |Python 3.6+| image:: https://img.shields.io/badge/python-3.6+-blue.svg
:target: https://www.python.org/downloads/release/python-380/



univers was born out of the need for a mostly univeral way to perform software
package version comparisons in VulnerableCode.
**univers** was born out of the need for a mostly univeral way to store version
ranges and to compare two software package versions in VulnerableCode.

Package version ranges and version constraints are useful and essential:

- When relating a known vulnerability or bug to a range of affected package
versions. For instance a statement such as "vulnerability 123 affects
package bar, version 3.1 and version 4.2 but not version 5" defines a
range of bar versions affected by a vulnerability.

- When resolving the dependencies of a package to express which subset of the
versions are supported. For instance a dependency requirement statement such
as "I require package foo, version 2.0 and later versions" defines a range of
acceptable foo versions.

- When relating a known vulnerability or bug to a range of affected package
versions. For instance a statement such as "vulnerability 123 affects
package bar, version 3.1 and version 4.2 but not version 5" also defines a
range of affected bar versions.
Version syntaxes and range notations are quite different across ecosystems,
making it is difficult to process versions and version ranges across ecosystems
in a consistent way.

Existing tools support typically a single algorithm to parse and compare
versions and this is not accurate across different ecosystems, since each
follow different versioning rules. For example there's no concept of 'epoch' in
semver versioning as used in package types and ecosystem such as npm or
rubygems, but epochs do exist in debian versions. A tool designed for semver or
dpkg versions processing would not be able to handle correctly the other version
scheme.
Existing tools and libraries typically support a single algorithms to parse and
compare versions with a single version range notation for a single package
ecosystem.

univers is different and considers the ecosystem-specific version scheme used.

**univers** is different:

How does univers work ?
=========================
- It tracks each ecosystem versionning scheme and how two versions are compared.

univers wraps, embeds or implements multiple version comparision libraries, each
focused on specific ecosystem version scheme.
- It support a growing number of package ecosystems versioning in a single
library.

It also implements an experimental unified syntax for version ranges specifier
and can parse and convert existing version range strings to this unified syntax.
- It can parse version range strings using their native notation (such as an npm
range) into the common "vers" notation and internal object model and can
return back a native version range string rebuilt from a "vers" range.

- It is designed to work with `Package URLs (purl) <https://github.com/package-url>`_.

The supported package ecosystems versioning schemes and underlying libraries are:

- semver: npm, golang, PHP composer, rubygems and others that follow the semver
spec, using `semantic_version <https://github.com/rbarrois/python-semanticversion>`_ library.
- debian: handled by the
`debian-inspector <https://github.com/sbs2001/univers/blob/main/src/univers/debian.py.ABOUT>`_
library.
How does **univers** work ?
============================

**univers** wraps, embeds and implements multiple version comparison libraries,
each focused on a specific ecosystem versionning scheme.

For each scheme, **univers** provides an implementation for:

- the version comparison procedure e.g, how to compare two versions,
- parsing and converting from a native version range notation to the
**univers** normalized and unified internal model,
- converting a range back to its scheme-native range syntax and to the
``vers`` syntax.

**univers** implements ``vers``, an experimental unified and mostly universal
version range syntax. It can parse and convert an existing native version range
strings to this unified syntax. For example, this means:

- converting ">1.2.3" as used in a Python package into ``vers:pypi/>1.2.3``,

- or converting "^1.0.2" as used in an npm package dependency declartion into
``vers:npm/>=1.0.2,<2.0.0``

The supported package ecosystems versioning schemes and underlying libraries
include:

- npm that use the "node-semver" ranges notation and the semver versions syntax
This is supported in part by the `semantic_version <https://github.com/rbarrois/python-semanticversion>`_ library.

- pypi: handled by Python's packaging library and the standard ``packaging.version`` module.
- maven: handled by the embedded `rpm_vercmp <https://github.com/sbs2001/univers/blob/main/src/univers/rpm.py.ABOUT>`_ library.
- ebuild/gentoo: handled by the embedded `gentoo_vercmp <https://github.com/sbs2001/univers/blob/main/src/univers/gentoo.py.ABOUT>`_ module.

As we grow, new schemes will be implemented accordingly.
- Rubygems which use a semver-like but not-quite-semver scheme and there can be
commonly more than three version segments.
Gems also use a slightly different range notation from node-semver with
different operators and slightly different semantics: for instance it uses "~>"
as a pessimistic operator and supports exclusion with != and does not support
"OR" between constraints (that it call requirements).

- debian: handled by the `debian-inspector <https://github.com/nexB/univers/blob/main/src/univers/debian.py.ABOUT>`_
library.

- maven: handled by the embedded `pymaven <https://github.com/nexB/univers/blob/main/src/univers/pymaven.py.ABOUT>`_ library.

- rpm: handled by the embedded `rpm_vercmp <https://github.com/nexB/univers/blob/main/src/univers/rpm.py.ABOUT>`_ library.

- golang (using semver)

- PHP composer

- ebuild/gentoo: handled by the embedded `gentoo_vercmp <https://github.com/nexB/univers/blob/main/src/univers/gentoo.py.ABOUT>`_ module.

- arch linux : handled by the embedded `arch utility borrowed from msys2 <https://github.com/nexB/univers/blob/main/src/univers/arch.py.ABOUT>`_ module.

The level of support for each ecosystem may not be even for now and new schemes
and support for more package types are implemented on a continuous basis.


Alternative
============

Rather than using ecosystem-specific version schemes and code, another approach
is to use a single procedure for all the versions as implemented in `libversion
<https://github.com/repology/libversion>`_. This works in the most common case
but may not work correctly for specific tasks that demand accurate version
comparison such as for dependency resolution and vulnerabilities checks.
<https://github.com/repology/libversion>`_. ``libversion`` works in the most
common case but may not work correctly when a task that demand precise version
comparisons such as for dependency resolution and vulnerability lookup where
a "good enough" comparison accuracy is not acceptable. ``libversion`` does not
handle version range notations.


Installation
Expand All @@ -80,35 +126,40 @@ Installation
Examples
========

Compare two versions using the Python comparison operators:
Compare two native Python versions:

.. code:: python

from univers.version import PypiVersion
assert PypiVersion("1.2.3") < PypiVersion("1.2.4")


Normalize a version range from an npm:

.. code:: python

from univers.version import PYPIVersion
v1 = PYPIVersion("1.2.3")
v2 = PYPIVersion("1.2.4")
assert v1 < v2 == True
from univers.version_range import NpmVersionRange
range = NpmVersionRange.from_native("^1.0.2")
assert str(range) == "vers:npm/>=1.0.2,<2.0.0"


Test if a version is within or outside of a version range:
Test if a version is within or outside a version range:

.. code:: python

from univers.version import PYPIVersion
from univers.version_specifier import VersionSpecifier
from univers.version import PypiVersion
from univers.version_range import VersionRange

vs = VersionSpecifier.from_scheme_version_spec_string("pypi", ">=1.2.4")
v1 = PYPIVersion("1.2.4")
v2 = PYPIVersion("1.2.3")
range = VersionRange.from_string("vers:pypi/>=1.2.4")

assert (v1 in vs ) == True
assert (v2 in vs ) == False
assert PypiVersion("1.2.4") in range
assert PypiVersion("1.2.3") not in range


Development
============

Starting from a git clone of https://github.com/nexB/univers run these::
Run these commands, starting from a git clone of https://github.com/nexB/univers ::

$ configure --dev
$ source venv/bin/active
Expand All @@ -121,5 +172,6 @@ Visit https://github.com/nexB/univers and
https://gitter.im/aboutcode-org/vulnerablecode and
https://gitter.im/aboutcode-org/aboutcode for support and chat.


Primary license: Apache-2.0
SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause AND MIT
14 changes: 1 addition & 13 deletions conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,6 @@
# Copyright (c) nexB Inc. and others.
# SPDX-License-Identifier: Apache-2.0
#
# Visit https://aboutcode.org and https://github.com/nexB/ for support and download.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Visit https://aboutcode.org and https://github.com/nexB/univers for support and download.

collect_ignore = ["setup.py"]
7 changes: 5 additions & 2 deletions src/univers/arch.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
#
# Copyright 2016-2020 Christoph Reiter
# Copyright (c) Christoph Reiter
# SPDX-License-Identifier: MIT
# Version comparision utility extracted from msys2 and further stripped down.
# Version utility extracted from msys2 https://github.com/msys2/msys2-web/
# and further stripped down.
#
# Visit https://aboutcode.org and https://github.com/nexB/univers for support and download.

import re
from itertools import zip_longest
Expand Down
2 changes: 1 addition & 1 deletion src/univers/arch.py.ABOUT
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ license_expression: MIT
homepage_url: https://github.com/msys2/msys2-web/

notes: |
The version comparision utility is extracted from msys2 and further stripped down.
The version comparison utility is extracted from msys2 and further stripped down.

notice_file: arch.py.NOTICE
4 changes: 3 additions & 1 deletion src/univers/debian.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
#
# Copyright (c) nexB Inc. and others.
# Exatrcted from http://nexb.com and https://github.com/nexB/debian_inspector/
# Extracted from http://nexb.com and https://github.com/nexB/debian_inspector/
# Copyright (c) Peter Odding
# Author: Peter Odding <peter@peterodding.com>
# URL: https://github.com/xolox/python-deb-pkg-tools
# SPDX-License-Identifier: MIT
#
# Visit https://aboutcode.org and https://github.com/nexB/univers for support and download.

import logging
import operator as operator_module
Expand Down
13 changes: 12 additions & 1 deletion src/univers/gentoo.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
#
# Copyright (c) 2006-2019, pkgcore contributors
# SPDX-License-Identifier: BSD-3-Clause
# Version comparision utility extracted from pkgcore and further stripped down.
# Version comparison utility extracted from pkgcore and further stripped down.
#
# Visit https://aboutcode.org and https://github.com/nexB/univers for support and download.

import re

from univers.utils import cmp
from univers.utils import remove_spaces

_is_gentoo_version = re.compile(
r"^(?:\d+)(?:\.\d+)*[a-zA-Z]?(?:_(p(?:re)?|beta|alpha|rc)\d*)*$"
).match

suffix_regexp = re.compile("^(alpha|beta|rc|pre|p)(\\d*)$")

Expand All @@ -19,6 +25,11 @@
"""


def is_valid(string):
version, _ = parse_version_and_revision(remove_spaces(string))
return _is_gentoo_version(version)


def parse_version_and_revision(version_string):
"""
Return a tuple of (version string, revision int) given a ``version_string``.
Expand Down
2 changes: 1 addition & 1 deletion src/univers/gentoo.py.ABOUT
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ copyright: |
license_expression: BSD-3-Clause
homepage_url: https://github.com/pkgcore/pkgcore/blob/master/src/pkgcore/ebuild/cpv.py

notes: The version comparision utility is extracted from pkgcore and further stripped down.
notes: The version comparison utility is extracted from pkgcore and further stripped down.

notice_file: gentoo.py.NOTICE
4 changes: 3 additions & 1 deletion src/univers/maven.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
#
# Copyright (c) SAS Institute Inc.
# SPDX-License-Identifier: Apache-2.0
# Version comparision utility extracted from pymaven and further stripped down
# Version comparison utility extracted from pymaven and further stripped down
# and significantly modified from the original at pymaven
#
# Visit https://aboutcode.org and https://github.com/nexB/univers for support and download.

import functools
from itertools import zip_longest
Expand Down
6 changes: 4 additions & 2 deletions src/univers/rpm.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
#
# Copyright (c) SAS Institute Inc.
# SPDX-License-Identifier: Apache-2.0
# Version comparision utility extracted from python-rpm-vercmp and further
# Version comparison utility extracted from python-rpm-vercmp and further
# stripped down and significantly modified from the original at python-rpm-vercmp
#
# Visit https://aboutcode.org and https://github.com/nexB/univers for support and download.

import re


class Vercmp(object):
class Vercmp:
R_NONALNUMTILDE = re.compile(br"^([^a-zA-Z0-9~]*)(.*)$")
R_NUM = re.compile(br"^([\d]+)(.*)$")
R_ALPHA = re.compile(br"^([a-zA-Z]+)(.*)$")
Expand Down
2 changes: 1 addition & 1 deletion src/univers/rpm.py.README
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pure Python implementation of rpmvercmp.

The RPM Package Manager (http://rpm.org) has a version comparision algorithm,
The RPM Package Manager (http://rpm.org) has a version comparison algorithm,
implemented in its C library, which performs the comparison in a certain way.

In certain circumstances, where the C library is not installable (for example,
Expand Down
Loading