When checking your Python package sources matters
Tags: python, programming, opensource, security
Published: 2023-04-16 18:12:26

In today's article, we will take a look at a small tool called [Yorkshire](https://pypi.org/project/yorkshire/). It's goal is to check configured Python package indexes in projects to make sure only desired package sources are used.

![Yorkshire](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hsj5ktj8xc4tepwin3vg.png)

>  A cute Yorkshire terrier, [Pixabay](https://pixabay.com/photos/terrier-dog-cute-puppy-pet-canine-410298/).

Python's packaging allows users to consume packages from multiple sources - multiple Python package indexes. During an installation process, the resolution algorithm implemented in pip searches all the configured package indexes to satisfy requirements.

The resolution algorithm treats all the configured indexes as mirrors. If a package `foo` is available on an index A as well as on an index B, they are both treated with the same relevance, considering versions available. Options `--index-url` and `--extra-index-url` allow specifying the primary and secondary indexes, but there is no guarantee on which index is actually used. If there is a network issue, the resolution process can use secondary indexes as they are just mirrors.

In some cases, users want to consume packages from index A and a specific package from index B. As of today, there is no configuration option in pip to specify which index should be used to consume the specific package. This allows dependency confusion attacks, such as [the PyTorch incident](https://pytorch.org/blog/compromised-nightly-dependency/).

There was [a discussion to prevent dependency confusion attacks using a map file on discuss.python.org](https://discuss.python.org/t/proposal-preventing-dependency-confusion-attacks-with-the-map-file/23414). The idea of the map file was not accepted, nevertheless there was a proposal [PEP-708: Extending the Repository API to Mitigate Dependency Confusion Attacks](https://peps.python.org/pep-0708/) that pushed the idea of preventing the dependency confusion attacks further.

Until the [PEP-708](https://peps.python.org/pep-0708/) gets eventually accepted and implemented, there is a space to check how projects configure their Python package indexes. Even if [PEP-708](https://peps.python.org/pep-0708/) is accepted, it might be a good idea for organizations to check which indexes are used to monitor consumption of software in their environments.

To support checks of the index configuration, there was developed a tool called [Yorkshire](https://pypi.org/project/yorkshire/). Yorkshire checks any index configuration in files that can be used to specify project dependencies, such as ``requirements.txt``, ``pyproject.toml``, or Pipenv files. If there are used multiple Python package indexes, Yorkshire reports it. Optionally, it can check only allowed indexes are configured.

Let's take [Poetry's configuration for specifying secondary indexes](https://python-poetry.org/docs/repositories/#installing-from-private-package-sources) as an example. The linked command can generate a ``pyproject.toml`` file similar to this one:

```toml
[tool.poetry]
name = "foo"
version = "1.0.0"
description = "My package"
authors = ["Author <author@email.com>"]

[tool.poetry.dependencies]
python = "^3.6"
flask = "^2"

[[tool.poetry.source]]
name = "private_repo"
url = "https://test.pypi.org/simple/"
default = false
secondary = true

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
```

The configuration above allows Poetry to consume packages hosted on [PyPI](https://pypi.org/) as well as the ones hosted on the [test PyPI](https://test.pypi.org/). Which one will be actually used to consume packages? Well, it depends on dependencies of the ``flask`` package. Note the version might be relevant here as well, depending on the environment to which dependencies are installed.

Let's run Yorkshire on the ``pyproject.toml`` file above:

```console
$ yorkshire detect ./pyproject.toml
2023-04-15 20:13:44,984 [1767887] INFO     yorkshire._lib: Performing detection in pyproject.toml file located at '.'
2023-04-15 20:13:44,985 [1767887] WARNING  yorkshire._lib: File './pyproject.toml' uses an explicitly configured Poetry source: ['https://test.pypi.org/simple/']
```

As can be seen, Yorkshire issues a warning as multiple package sources can be eventually used.

Next, let's specify the test PyPI to be allowed:

```console
$ yorkshire detect --allowed-index-url "https://test.pypi.org/simple/" ./pyproject.toml
2023-04-15 20:16:33,806 [1773955] INFO     yorkshire._lib: Performing detection in pyproject.toml file located at '.'
```

The command above shows that the test PyPI specified in the ``pyprojec.toml`` is now no longer flagged as a possible issue.

Yorkshire understands requirements file types used in the Python ecosystem. Similarly to the ``pyproject.toml`` configuration specific to [Poetry](https://python-poetry.org/), Yorkshire supports configuration as used in [PDM](https://pdm.fming.dev/), [Pipenv](https://pipenv.pypa.io/), [pip-tools](https://pypi.org/project/pip-tools/), or [pip](https://pypi.org/project/pip-tools/) itself. All the tools have their own specifics (some of them even support assigning packages to an index, as mentioned above).

Yorkshire [provides an API](https://github.com/DataDog/yorkshire/blob/14ddb9b9b31dc92f499e0a89d8b070539c9f696d/yorkshire/__init__.py#L14-L15) to eventually incorporate checks into other projects or systems.

Organizations can use Yorkshire in their checks or monitoring to make sure only trusted Python package indexes are used. Would you find it useful?