diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 62107d36..49ec5198 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,7 +24,7 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/tox-dev/pyproject-fmt - rev: 3632ea90e7641b574983ddf3a11ccedefff3c881 # frozen: v2.25.3 + rev: d600c142bb19f521ae6a6a345f94a2efd7937759 # frozen: v2.26.0 hooks: - id: pyproject-fmt - repo: https://github.com/abravalheri/validate-pyproject @@ -32,7 +32,7 @@ repos: hooks: - id: validate-pyproject - repo: https://github.com/astral-sh/ruff-pre-commit - rev: 01a675ea018f2fb714478a5ffb83fcea8374bb06 # frozen: v0.15.21 + rev: 39d9ac5938dadb73df0564a45f163e25ff9fa6e2 # frozen: v0.16.1 hooks: - id: ruff-check args: [--fix, --exit-non-zero-on-fix] @@ -43,14 +43,14 @@ repos: - id: sphinx-lint types: [rst] - repo: https://github.com/adamchainz/blacken-docs - rev: fda77690955e9b63c6687d8806bafd56a526e45f # frozen: 1.20.0 + rev: dda8db18cfc68df532abf33b185ecd12d5b7b326 # frozen: 1.20.0 hooks: - id: blacken-docs args: [--line-length=79] additional_dependencies: - black - repo: https://github.com/codespell-project/codespell - rev: 2ccb47ff45ad361a21071a7eedda4c37e6ae8c5a # frozen: v2.4.2 + rev: 57b21406f092110c18776e39b0bda50d37c945c8 # frozen: v2.4.3 hooks: - id: codespell args: [--toml pyproject.toml] diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 2dff0e4c..199169f5 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -28,6 +28,7 @@ Added Changed ~~~~~~~ +* 📝 Extend seccurity section * 👷🔧📝 Switch to prek * Remove pre-commit diff --git a/docs/productive/qa/pysa.rst b/docs/productive/qa/pysa.rst index d3cb55ef..712d7a7b 100644 --- a/docs/productive/qa/pysa.rst +++ b/docs/productive/qa/pysa.rst @@ -13,10 +13,10 @@ and identifies vulnerable code. .. seealso:: * `What Is Taint Analysis and Why Should I Care? `_ - * `How Pysa works `_ + * `How Pysa works `_ * `Running Pysa `_ * `Pysa Tutorial - `_ + `_ Configuration ------------- diff --git a/docs/productive/security/dependencies.rst b/docs/productive/security/dependencies.rst new file mode 100644 index 00000000..ef292636 --- /dev/null +++ b/docs/productive/security/dependencies.rst @@ -0,0 +1,602 @@ +.. SPDX-FileCopyrightText: 2023 cusy GmbH +.. +.. SPDX-License-Identifier: BSD-3-Clause + +Dependencies +============ + +This is precisely where attacks on the software supply chain take place. The +`OpenSSF Secure Supply Chain Consumption Framework (S2C2F) +`_ provides a structured maturity model for how +organisations should use open-source software. Unfortunately, however, the +:abbr:`S2C2F (Secure Supply Chain Consumption Framework)` is limited to GitHub +projects. We therefore sought comparable solutions for our Python projects that +do not rely on GitHub. + +.. seealso:: + * `OpenSSF Scorecard `_ + * :ref:`open_chain` + * `CNCF Software Supply Chain Security Whitepaper + `_ + +Choose your dependencies carefully +---------------------------------- + +Before adding a dependency, you should check whether you actually need it, as +every dependency increases your attack surface. Fewer or smaller dependencies +mean fewer potential points of attack. When you add a dependency, you can assess +the security situation using the `OpenSSF Scorecard +`_: + +A low score gives you an indication of how much trust you should place in a +project with limited security practices. + +Is there a security policy? +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Ideally, a :ref:`python-basics:security` file or similar should have been +published alongside the dependency. This file should contain information on + +* how a security vulnerability can be reported without it becoming publicly + visible, +* the procedure and timeline for disclosing the vulnerability, +* and links, such as URLs and email addresses, where support can be requested. + +.. seealso:: + * `Guide to implementing a coordinated vulnerability disclosure process for + open source projects + `_ + * `Adding a security policy to your repository + `_ + * `Runbook + `_ + +Are CI tests carried out? +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Before code is merged into pull or merge requests, tests should be carried out +to help identify errors at an early stage and reduce the number of +vulnerabilities in a project. + +.. seealso:: + * :ref:`coverage-github-actions` + +Are fuzzing tools used? +~~~~~~~~~~~~~~~~~~~~~~~ + +Fuzzing, or fuzz testing, feeds unexpected or random data into your programme to +uncover bugs. Regular fuzzing is important for identifying vulnerabilities that +could be exploited by others, particularly as fuzzing can also be used during an +attack to find the same vulnerabilities. + +* Does your project use `fuzzing `_? +* Is the repository name included in the `OSS-Fuzz + `_ project list? +* Is `ClusterFuzzLite `_ used in the + repository? +* Are there any custom language-specific fuzzing functions in the repository, + for example using `atheris `_? + +Are static code analysis tools used? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:term:`Static test procedures` test the source code before the application is +run. This can prevent known classes of errors from being inadvertently +introduced into the codebase. + +Is the source code free of checked-in binary files? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Generated executable files in the source code repository (such as Python +:file:`.pyc` files) increase the risk, as they are difficult to verify and may +therefore be out of date or have been maliciously manipulated. These issues can +be addressed with verified, reproducible builds; however, the resulting +executable files should not be placed back into the source code repository. + +.. seealso:: + * `Reproducible Builds `_ + * `Python 3.12.0 from a supply chain security perspective + `_ + * `Defending against the PyTorch supply chain attack PoC + `_ + +Can malicious code be injected? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:ref:`Protected Git branches ` allow rules to be defined for +merging changes into the main and release branches, such as automated `static +code analysis `_ using +:doc:`../qa/ruff`, :doc:`../qa/pysa`, :doc:`../qa/wily`, and :ref:`code reviews +` via so-called :doc:`merge requests +<../git/advanced/gitlab/merge-requests>`. + +.. _code_reviews: + +Are code reviews carried out? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Code reviews help to identify unintended vulnerabilities or the potential +injection of malicious code. Where applicable, this can help detect attacks in +which a team member’s account has been compromised. + +Are people from multiple organisations involved? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This is seen as an indication of a smaller number of trusted code reviewers. To +check this, you can search the profiles for different entries in the *Company* +field. It is desirable to have at least three different companies represented in +the last 30 commits, with each of these team members having made at least five +commits. + +.. _lock-dependencies: + +Are dependencies declared and pinned? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In your project, dependencies used during the build and release process should +be pinned. A pinned dependency should be explicitly set to a specific hash, +rather than just a variable version or a version range. + +:doc:`../envs/spack/index` records these hashes for the respective environment +in the :ref:`spack_lock` and :doc:`../envs/uv/index` in :ref:`uv_lock` files. + +.. tip:: + However, I usually only manage these files in :doc:`Git <../git/index>` for + :doc:`apps `. For libraries, I typically just + restrict the version range of the dependencies in the :file:`pyproject.toml` + file. + +For :doc:`apps `, this can help reduce the following +security risks: + +* Testing and deployment are carried out using the same software, which reduces + deployment risks, simplifies debugging and enables reproducibility. +* Compromised dependencies do not undermine the security of the project. +* Substitution attacks – that is, attacks aimed at confusing dependencies – can + thus be countered. + +However, locking down dependencies should not prevent software updates. You can +reduce this risk by + +* using automated tools that notify you when dependencies in your project are + out of date +* updating applications that lock down dependencies promptly. + +Specify the dependencies +------------------------ + +.. warning:: + When publishing a library on :term:`PyPI`, you should use as broad version + ranges as possible in the ``dependencies`` section of your + :file:`pyproject.toml` file to avoid conflicts when others wish to install + your library alongside other libraries. The guidance in this section applies + exclusively to the deployment of applications. + +Consider the following scenario: ``uv add`` writes the approximate version of +your dependency to your :file:`pyproject.toml` file, for example, +:samp:`"{MYDEP}>=3.0.5"`. If the project is set up from scratch, running ``uv +sync`` may result in version ``3.0.6`` of :samp:`{MYDEP}` being installed. This +could lead to a malicious version being downloaded unnoticed, without a single +line of code having been changed. + +A fixed version :samp:`"{MYDEP}==3.0.5"` is better, as this ensures we do not +receive a version newer than the one tested in the project. However, this still +does not provide an integrity check: if, during an attack, the maintainer’s +account were compromised and a new release—containing a backdoor—were published +for the same version but for a different platform, this too could be installed +unwittingly. To mitigate this attack scenario, in future `only releases for a +single version within a 14-day window will be permitted +`_ +on :term:`PyPI`. + +Hash pinning is more secure – it creates a cryptographic fingerprint of the +package file, which is fixed in the :file:`uv.lock` file using :term:`uv`. +Alternatively, you can use ``pip-compile --generate-hashes`` from the +`pip-tools `_: + +.. code-block:: console + + $ python -m pip install pip-tools + $ pip-compile --generate-hashes pyproject.toml -o requirements.txt + +.. seealso:: + * `Secure installs `_ + +However, you should not only use hash pinning for your Python dependencies, but +also, for example, for your :doc:`pre-commit checks +<../git/advanced/hooks/checks>` and :ref:`GitHub Actions `. + +Hash pinning does not, however, protect against installing a malicious package +for the first time; in that case, you would simply be pinning the hash of the +malicious package. You should therefore combine hash pinning with vulnerability +scans and delayed deployment. + +.. seealso:: + `The lockfile + `_ + +.. _automatic-update: + +Automatically update dependencies +--------------------------------- + +Dependencies should be updated regularly to avoid vulnerabilities, minimise +incompatibilities between dependencies and prevent complex upgrades when +updating from an outdated version. A variety of tools can help you stay up to +date. + +Out-of-date dependencies make a project vulnerable to attacks exploiting known +vulnerabilities. Therefore, updating dependencies should be automated by +checking for out-of-date dependencies and updating them where necessary. With +:doc:`../git/advanced/hooks/prek`, you can regularly update your :file:`uv.lock` +file: + +.. code-block:: yaml + :caption: .pre-commit-config.yaml + + - repo: https://github.com/astral-sh/uv-pre-commit + rev: 6a280ba12b7901e47757c868c8c13c6a624c9ecb # 0.11.7 + hooks: + - id: uv-lock + args: ["--exclude-newer = 'P3D'", "--quiet"] + +``--exclude-newer`` + *Dependency cooldown*, which excludes packages that have only been published + on :term:`PyPI` for a few days – or, in the case of ``P3D``, for just three + days. This gives PyPI administrators the opportunity to respond to malware + during this period. + +.. seealso:: + * :ref:`Update uv.lock ` + +Alternatively, you can also use :doc:`../envs/uv/dependency-bot` for assistance. + +.. _vulnerability_scans: + +Vulnerability scans +------------------- + +*Dependency pinning* prevents unauthorised changes – but what happens if you’ve +pinned a version that contains a known security vulnerability? Researchers are +constantly discovering new :abbr:`CVEs (Common Vulnerabilities and Exposures)` +in packages. A package that was fine yesterday could already have a critical +security vulnerability today. Unpatched security vulnerabilities in your +dependencies can easily be exploited and should therefore be fixed as soon as +possible. To do this, you can use ``uv audit`` to check whether your project has +any known security vulnerabilities in its dependencies: + +.. code-block:: console + + $ uv audit + warning: `uv audit` is experimental and may change without warning. Pass `--preview-features audit-command` to disable this warning. + Resolved 115 packages in 16ms + Found 12 known vulnerabilities and no adverse project statuses in 114 packages + + Vulnerabilities: + + idna 3.12 has 1 known vulnerability: + - GHSA-65pc-fj4g-8rjx: Internationalized Domain Names in Applications (IDNA): Specially crafted inputs to idna.encode() can bypass CVE-2024-3651 fix + Fixed in: 3.15 + Advisory information: https://github.com/kjd/idna/security/advisories/GHSA-65pc-fj4g-8rjx + … + +``uv add``, ``uv sync``, :abbr:`etc. (et cetera)` can now scan for previously +identified malware during every synchronisation process. This feature is not +enabled by default, but can be easily enabled by setting ``UV_MALWARE_CHECK=1`` +in the shell. + +.. seealso:: + * `uv audit `_ + * `uv audit settings `_ + +If a vulnerability is found in a dependency, you should update to a +non-vulnerable version; if no update is available, you should consider removing +the dependency. + +If you believe that the vulnerability does not affect your project, you can +define exceptions for ``uv audit`` in the :file:`pyproject.toml` file, for +example + +.. code-block:: toml + :caption: pyproject.toml + + [tool.uv.audit] + ignore = ["PYSEC-2022-43017", "GHSA-5239-wwwm-4pmq"] + +or, better still, + +.. code-block:: toml + :caption: pyproject.toml + + [tool.uv.audit] + ignore-until-fixed = ["PYSEC-2022-43017"] + +.. seealso:: + * `ignore `_ + * `ignore-until-fixed + `_ + +You can also incorporate the vulnerability analysis carried out using +``uv-audit`` into your :doc:`prek <../git/advanced/hooks/prek>` checks: + +.. code-block:: yaml + + - repo: https://github.com/astral-sh/uv-pre-commit + rev: d9fca3320346514799461a80b0753eb45d707d46 # 0.11.28 + hooks: + - id: uv-audit + files: ^(uv\.lock|pyproject\.toml)$ + +Security checks should be carried out automatically. To do this, you can use +``uv audit``, for example, in a GitHub Action: + +.. code-block:: yaml + + name: Security Scan + jobs: + security: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 + - run: uv audit + +or in a GitLab CI/CD pipeline: + +.. code-block:: yaml + + security-scan: + image: ghcr.io/astral-sh/uv:python3.14 + script: + - uv audit + +As an alternative to ``uv audit``, you can also use `osv +`_ or `pip-audit +`_ for this. There is also a corresponding +GitHub Action: `pypa/gh-action-pip-audit +`_. + +Avoid dependency conflicts +-------------------------- + +Dependency conflicts can arise from the way package managers resolve names when +both public and private package repositories are used. A malicious package +published on :term:`PyPI` that shares the same name as your internal package may +be installed by the build system instead. With :term:`pip`, the attack works as +follows: + +#. When running :samp:`python -m pip install --extra-index-url + {https://EXAPMPLE.COM/simple MYPACKAGE}` or similar, you would probably + expect :samp:`{MYPACKAGE}` to be fetched from your index at + :samp:`https://{EXAPMPLE.COM}/simple`. +#. However, ``pip`` checks all indexes and selects the highest version. +#. So if there is a higher version of :samp:`{MYPACKAGE}` on :term:`PyPI` + containing malicious code, that version will be installed. + +You can work around this problem by using ``--index-url`` to specify a single +index. In this case, ``pip`` assumes that your internal index acts as a proxy +for the public :term:`PyPI`; however, if it only hosts internal packages, you +can first configure it as a PyPI proxy: + +.. code-block:: ini + :caption: pip.conf + + [install] + index-url = https://EXAPMPLE.COM/simple + trusted-host = EXAPMPLE.COM + +:doc:`SBOMs ` can help to identify potential naming conflicts by providing +an inventory for verification; however, these are retrospective checks – so they +only show you, after the fact, what you have installed. + +:term:`uv`, on the other hand, usually uses the ``first-index`` strategy, +meaning it takes the first index in which a package is found. This avoids the +dependency conflicts described above: + +.. code-block:: toml + :caption: pyoroject.toml + :linenos: + + [[tool.uv.index]] + name = "internal" + url = "https://EXAPMPLE.COM/simple" + explicit = true + + [tool.uv.sources] + mypackage = { index = "internal" } + +Line 4: + This index is used only for explicitly pinned packages. + +.. seealso:: + `Searching across multiple indexes + `_ + +Verify package attestations +--------------------------- + +:term:`PyPI` :ref:`package attestations ` use `Sigstore +`_ to provide cryptographic proof of a package’s +provenance in accordance with :pep:`740`. Since `gh-action-pypi-publish v1.11.0 +`_, these +attestations have also been generated automatically. By the end of 2025, more +than 50,000 projects were using *Trusted Publishing*, and 17 per cent of uploads +included attestations. *Trusted Publishing* has also been extended to +organisations and self-managed GitLab instances. + +.. seealso:: + * `PyPI in 2025: A Year in Review + `_ + * `Are we PEP 740 yet? 🔏 + `_ + +In addition to package attestations, :pep:`740` also defines :abbr:`SLSA +(Supply-chain Levels for Software Artifacts)` provenance attestations. For use +cases outside :term:`PyPI`, `actions/attest +`_ can generate these SLSA provenance and +:doc:`SBOM ` attestations for each artefact. + +In the case of the attack on :ref:`Ultralytics `, these +attestations could have been used to identify which versions originated from a +compromised workflow and which were legitimate – without the need for any manual +forensic analysis. Sigstore’s transparency logs provide an independent audit +trail with precise timestamps and details of the provenance of every published +artefact. + +Add time-based defences +----------------------- + +When a malicious package is published on :term:`PyPI`, it is immediately +available worldwide. Detection times vary – some attacks are detected within a +few hours, whilst others go unnoticed for weeks or months. In 2025, there were +over 2,000 malware reports, 66 per cent of which were processed within four +hours. + +Whilst waiting before using newly published packages does not provide a +guarantee, it does reduce the risk, as the community is likely to uncover +obvious threats within a short time. + +Modern package managers support time-based filtering. :term:`uv` has the +``--exclude-newer`` option, and pip ≥ v26 has introduced the +``--uploaded-prior-to`` option for the same purpose, both of which rely on +metadata regarding the upload time in accordance with :pep:`700`. + +Use internal package repositories within your organisations +----------------------------------------------------------- + +In smaller organisations, a simple mirror of :term:`PyPI` that makes packages +available with a one-week delay can already reduce the security risk to the +organisation. However, you should ensure that you can override this delay for +critical security patches. If you use internal package repositories within your +organisation, you can also implement further security measures: + +#. Automated security scans of packages +#. Automated building of packages with *Trusted Publishing* and SLSA provenance + +Respond quickly if you discover a malicious package +--------------------------------------------------- + +If you discover a compromised package on your system, acting quickly can often +prevent major damage. + +#. Isolate the package immediately + + Stop all deployments that use this dependency and block the package version + on your internal mirror, if you operate one. The aim is to prevent further + installations whilst you investigate the cause further. + +#. Assess the damage + + Check logs and process data to determine whether the malicious code was + executed. Identify which sensitive data the package may have accessed: + environment variables, login credentials, cloud tokens, :abbr:`etc. (et + cetera)` Use your :doc:`SBOM ` to identify all affected projects within + your organisation. + +#. Mitigate the damage + + Change all login credentials that the package may have accessed: API keys, + database passwords, cloud credentials. Scan systems for signs of compromise + and check outgoing network connections for signs of data exfiltration. + +#. Remove the dependency completely + + Update to a known bug-free version and remove the dependency completely. Run + ``pip-audit`` to ensure that no further vulnerabilities have been introduced. + Then update your lock files with the corrected version. + +#. Report the malicious package + + You can report the malicious package via `PyPI’s security reporting system + `_. Also notify the relevant people in your + organisation and any customers who may be affected. Document the incident: + What happened? How was the package discovered? What changes have you made to + prevent a recurrence? + +Check whether your dependencies are still being maintained? +----------------------------------------------------------- + +You should regularly check whether a dependency has been archived. However, the +OSSF Scorecard checks are only successful if the project is older than 90 days. +A lack of active maintenance is not necessarily always a problem, though: +smaller utilities in particular usually require maintenance only very rarely. A +lack of active maintenance therefore simply indicates that you should +investigate the situation more closely. + +`pypi-changes `_ is a CLI tool that +checks the packages installed for a Python interpreter and compares them with +the latest versions on :term:`PyPI`. It shows which packages are out of date, +how long ago the respective version was released, and highlights significant +version jumps so that you can make informed decisions regarding upgrades, for +example: + +.. figure:: pypi-changes.png + :alt: Kommandozeilenaufruf uvx pypi-changes mit der Auflistung aller in einem + Projekt verwendeten Python-Bibliotheken, deren Version und + Veröffentlichungsdatum + +.. + $ uvx pypi-changes + Installed 26 packages in 18ms + 🐍 Distributions within + /Users/veit/.cache/uv/archive-v0/soguBMAn2UOVYxDU/bin/python + ├── annotated-types 0.8.0 7 days + ├── certifi 2026.7.22 9 days + ├── soupsieve 2.9.1 9 days + ├── pypi-changes 1.6.0 9 days + ├── platformdirs 4.11.0 9 days + ├── charset-normalizer 3.4.9 a month + ├── requests-cache 1.3.3 a month + ├── typing_extensions 4.16.0 a month + ├── humanize 4.16.0 a month + ├── beautifulsoup4 4.15.0 2 months + ├── idna 3.18 2 months + ├── pydantic_core 2.46.4 3 months remote 2.47.0 2 months + ├── requests 2.34.2 3 months + ├── urllib3 2.7.0 3 months + ├── markdown-it-py 4.2.0 3 months + ├── pydantic 2.13.4 3 months + ├── url-normalize 3.0.0 3 months + ├── packaging 26.2 3 months + ├── rich 15.0.0 4 months + ├── Pygments 2.20.0 4 months + ├── attrs 26.1.0 4 months + ├── cattrs 26.1.0 5 months + ├── mailbits 0.2.3 8 months + ├── typing-inspection 0.4.2 10 months + ├── pypi-simple 1.8.0 11 months + └── mdurl 0.1.2 3 years + +Alternatively, you can also view the PyPI versions of a project with badges, for +example: + ++---------------+-------------------------------------------------------+ +| Package name | Current PyPI version | ++===============+=======================================================+ +| pypi-simple | .. image:: https://img.shields.io/pypi/v/pypi-simple | +| | :alt: PyPI Version | +| | :target: https://pypi.org/project/pypi-simple | ++---------------+-------------------------------------------------------+ +| mdurl | .. image:: https://img.shields.io/pypi/v/mdurl | +| | :alt: PyPI Version | +| | :target: https://pypi.org/project/mdurl | ++---------------+-------------------------------------------------------+ + +.. tab:: reST + + .. code-block:: rst + + +---------------+-------------------------------------------------------+ + | Package name | Current PyPI version | + +===============+=======================================================+ + | pypi-simple | .. image:: https://img.shields.io/pypi/v/pypi-simple | + | | :alt: PyPI Version | + | | :target: https://pypi.org/project/pypi-simple | + +---------------+-------------------------------------------------------+ + | mdurl | .. image:: https://img.shields.io/pypi/v/mdurl | + | | :alt: PyPI Version | + | | :target: https://pypi.org/project/mdurl | + +---------------+-------------------------------------------------------+ + +.. seealso:: + * `Is it maintained? `_ diff --git a/docs/productive/security/environments.rst b/docs/productive/security/environments.rst new file mode 100644 index 00000000..b2230581 --- /dev/null +++ b/docs/productive/security/environments.rst @@ -0,0 +1,90 @@ +.. SPDX-FileCopyrightText: 2026 cusy GmbH +.. +.. SPDX-License-Identifier: BSD-3-Clause + +Separating environments +======================= + +Current best practice is to separate different environments depending on what is +to be done in each one. For example, in our `cusy.tasks +`_ application, a distinction is made +between the following environments: + +.. code-block:: toml + :caption: pyproject.toml + + [project] + + dependencies = [ + "rich", + "tinydb", + "typer", + ] + + [dependency-groups] + dev = [ + "pre-commit", + "reuse", + "tox-uv", + "watchgha", + { include-group = "docs" }, + { include-group = "tests" }, + ] + docs = [ + "furo", + "interrogate", + "matplotlib", + "sphinx-copybutton", + "sphinx-inline-tabs", + "sphinxcontrib-napoleon", + "sphinxext-opengraph", + ] + tests = [ + "coverage[toml]", + "faker", + "pytest", + "pytest-cov", + ] + +This means that when the application is running, only the dependencies required +for that purpose are installed; additional dependencies are only installed when +testing or deploying the documentation. Only the development environment +contains all dependencies. + +Just as your Python environment should be kept up to date with immutable +references, your :doc:`../git/advanced/hooks/checks` and GitHub Actions should +also be updated regularly. + +In the :file:`.pre-commit-config.yaml` file, the versions of the checks and +their hashes should be updated regularly, for example using: + +.. code-block:: console + + $ uv run prek update --freeze --cooldown-days 7 + https://github.com/pre-commit/pre-commit-hooks + updating rev `v6.0.0` -> `3e8a8703264a2f4a69428a0aa4dcb512790b2c8c` (frozen: v6.0.0) + +.. seealso:: + :doc:`../git/advanced/hooks/prek` + +.. _pinact: + +Check your GitHub Actions +------------------------- + +For GitHub Actions, you can use `pinact +`_, for example with: + +.. code-block:: console + + $ pinact run -u --min-age 7 + +`zizmor `_ is a static analysis tool that detects +security vulnerabilities in GitHub Actions workflows – including template +injection, unpinned actions, excessive permissions, credential exposure and +`more than 30 other checks `_. zizmor identifies +vulnerabilities such as those exploited through :ref:`token exfiltration +`. + +.. seealso:: + * :ref:`zizmorcore` diff --git a/docs/productive/security/ghsas-by-month.webp b/docs/productive/security/ghsas-by-month.webp new file mode 100644 index 00000000..7e0ba811 Binary files /dev/null and b/docs/productive/security/ghsas-by-month.webp differ diff --git a/docs/productive/security/index.rst b/docs/productive/security/index.rst index 0732d7e3..4005d94b 100644 --- a/docs/productive/security/index.rst +++ b/docs/productive/security/index.rst @@ -5,377 +5,137 @@ Security ======== -In previous chapters, we have already provided some tips designed to help ensure -safer operation. - -.. seealso:: - * :ref:`secure-release-workflow` - * :ref:`zizmorcore` - * :ref:`add_2fa` - -Here, we would like to summarise and expand on the individual elements once -again. We will be using the `OpenSSF Scorecard -`_ as our guide. Alternatively, you can also -refer to :ref:`open_chain`. - -.. _check-vulnerabilities: - -Check vulnerabilities ---------------------- - -Risk: High - -This check determines whether the project has open, unfixed vulnerabilities in -its own code base or in its dependencies. An open vulnerability can be easily -exploited and should be closed as soon as possible. - -For such a check, you can use for example ``uv audit`` Alternatively, you can -use `osv `_ or `pip-audit -`_. - -``uv audit`` is a new command introduced in uv≥0.11.19 that checks the -dependencies in your project for known vulnerabilities in the `OSV -`_ database and ‘undesirable’ project statuses, such as -*deprecated*: +As of July 2026, :term:`PyPI` hosts over 750,000 packages, and this number is +growing daily. An average Python project includes dozens of additional +dependencies – packages that you have never explicitly chosen, but on which you +nevertheless rely because your dependencies require them. When you install +pandas in your application, you get more than just pandas. The complete +dependency tree looks like this: .. code-block:: console - $ uv audit - warning: `uv audit` is experimental and may change without warning. Pass `--preview-features audit-command` to disable this warning. - Resolved 115 packages in 16ms - Found 12 known vulnerabilities and no adverse project statuses in 114 packages - - Vulnerabilities: - - idna 3.12 has 1 known vulnerability: - - GHSA-65pc-fj4g-8rjx: Internationalized Domain Names in Applications (IDNA): Specially crafted inputs to idna.encode() can bypass CVE-2024-3651 fix - Fixed in: 3.15 - Advisory information: https://github.com/kjd/idna/security/advisories/GHSA-65pc-fj4g-8rjx - … - -``uv add``, ``uv sync``, and so on can now be run during every synchronisation -process to check for previously identified malware. This feature is not enabled -by default, but it can be easily enabled by setting ``UV_MALWARE_CHECK=1`` in -the shell. - -.. seealso:: - * `uv audit `_ - * `uv audit settings `_ - -If a vulnerability is found in a dependency, you should update to a -non-vulnerable version; if no update is available, you should consider removing -the dependency. - -If you believe that the security vulnerability does not affect your project, you -can define exceptions for ``uv audit`` in the :file:`pyproject.toml` file, for -example: - -.. code-block:: toml - :caption: pyproject.toml - - [tool.uv.audit] - ignore = ["PYSEC-2022-43017", "GHSA-5239-wwwm-4pmq"] - -or better still: - -.. code-block:: toml - :caption: pyproject.toml - - [tool.uv.audit] - ignore-until-fixed = ["PYSEC-2022-43017"] - -.. seealso:: - * `ignore `_ - * `ignore-until-fixed - `_ - -You can also add the vulnerability analysis using ``uv-audit`` to your -:doc:`prek <../git/advanced/hooks/prek>` checks: - -.. code-block:: yaml - - - repo: https://github.com/astral-sh/uv-pre-commit - rev: d9fca3320346514799461a80b0753eb45d707d46 # 0.11.28 - hooks: - - id: uv-audit - files: ^(uv\.lock|pyproject\.toml)$ - -Maintenance ------------ - -.. _automatic-update: - -Are the dependencies updated automatically? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Risk: High - -Outdated dependencies make a project vulnerable to attacks on known -vulnerabilities. Therefore, the process of updating dependencies should be -automated by checking for outdated or insecure requirements and updating them if -necessary. You can use `dependabot `_ or `Safety -`_ for this purpose. - -You can also update your :doc:`/productive/envs/uv/index` environments -automatically. - -.. seealso:: - * :ref:`Update uv.lock ` - -Are the dependencies still maintained? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Risk: High - -This indicates possible unpatched security vulnerabilities. Therefore, it should -be checked regularly whether a project has been archived. Conversely, the -OpenSSF scorecard assumes that with at least one commit a week for 90 days, the -project is very actively maintained. However, a lack of active maintenance is -not necessarily always a problem: smaller utilities in particular usually do not -need to be maintained, or only very rarely. So a lack of active maintenance only -tells you that you should investigate the situation more closely. - -You can also display the activities of a project with badges, for example: - -.. image:: https://img.shields.io/github/commit-activity/y/veit/python4datascience - :alt: Annual commit activity -.. image:: https://img.shields.io/github/commit-activity/m/veit/python4datascience - :alt: Monthly commit activity -.. image:: https://img.shields.io/github/commit-activity/w/veit/python4datascience - :alt: Weekly commit activity - -Is there a safety concept for the project? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Risk: Medium - -Ideally, a :ref:`python-basics:security` or similar file should have been -published with the project. This file should contain information - -* how a security vulnerability can be reported without it becoming publicly - visible, -* on the procedure and schedule for disclosing the vulnerability, -* to links, for example URLs and emails, where support can be requested. - -.. seealso:: - * `Guide to implementing a coordinated vulnerability disclosure process for - open source projects - `_ - * `Adding a security policy to your repository - `_ - * `Runbook - `_ - -Does the project contain a usable licence? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Risk: Low - -A :doc:`license ` indicates how the source code may or -may not be used. The absence of a licence complicates any kind of security -review or audit and poses a legal risk for potential use. - -OSSF-Scorecard uses the `GitHub License API -`_ -for projects hosted on GitHub, otherwise it uses its own heuristics to detect a -published license file. Files in a :file:`LICENSES` directory should be named -with their :ref:`SPDX ` licence identifier followed -by an appropriate file extension as described in the :ref:`REUSE ` -specification. - -OpenSSF Best Practices Badge -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Risk: Low - -You can also get a corresponding badge with the `OpenSSF Best Practices Badge -Program `_. - -Continuous testing ------------------- - -Are CI tests carried out in the project? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Risk: Low - -Before code is merged into pull or merge requests, tests should be performed to -help detect errors early and reduce the number of vulnerabilities in a project. - -.. seealso:: - * :ref:`coverage-github-actions` - -Does the project use fuzzing tools? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -risk: Medium - -Fuzzing or fuzz testing passes unexpected or random data to your programme to -detect bugs. Regular fuzzing is important to detect vulnerabilities that can be -exploited by others, especially since fuzzing can also be used in an attack to -find the same vulnerabilities. - -* Does your project use `fuzzing `_? -* Is the name of the repository included in the `OSS fuzz - `_ project list? -* Is `ClusterFuzzLite `_ used in the - repository? -* Are custom language-specific fuzzing features present in the repository, for - example with `atheris `_? - -Does your project use static code analysis tools? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Risk: Medium - -:term:`Static test procedures` test the source code before the application is -run. This can prevent known types of errors from being inadvertently introduced -into the codebase. - -.. _bandit: - -`Bandit `__, which you can use with -:doc:`../qa/ruff`, allows you to check for the following vulnerabilities, -amongst others: - -+--------+-----------------------------------------------------------------------+ -| Rule | Description | -+--------+-----------------------------------------------------------------------+ -| `S105`_| Hard-coded secrets | -+--------+-----------------------------------------------------------------------+ -| `S301`_| :doc:`/data-processing/serialisation-formats/pickle/index` and other | -| | insecure deserialisation | -+--------+-----------------------------------------------------------------------+ -| `S307`_| Use of :func:`eval` with untrusted input | -+--------+-----------------------------------------------------------------------+ -| `S113`_| Missing timeouts | -+--------+-----------------------------------------------------------------------+ -| `S324`_| Weak cryptography, such as MD5 collisions | -+--------+-----------------------------------------------------------------------+ -| `S608`_| SQL injection via string formatting | -+--------+-----------------------------------------------------------------------+ - -.. seealso: - `flake8-bandit `_ - -You can also integrate Bandit into Jupyter Notebooks, IDEs and prek. - -In addition, you can use :doc:`../qa/pysa` for `taint -`_ analyses. - -For GitHub repositories you can also use `CodeQL `_; -see `codeql-action `_. - -Risk assessment of the source code ----------------------------------- - -Is the project free of checked-in binaries? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Risk: High - -Generated executables in the source code repository (for example Java -:file:`.class` files, Python :file:`.pyc` files) increase risk because they are -difficult to verify, so they may be out of date or maliciously tampered with. -These problems can be countered with verified, reproducible builds, but their -executables should not end up back in the source code repository. - -Is the development process vulnerable to the introduction of malicious code? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Risk: High - -With :ref:`protected Git branches `, rules can be defined -for the adoption of changes in standard and release branches, for example -automated `static code analyses -`_ with -:doc:`../qa/flake8`, :doc:`../qa/pysa`, :doc:`../qa/wily` and :ref:`code reviews -` via :doc:`merge requests -<../git/advanced/gitlab/merge-requests>`. - -.. seealso:: - * `Reproducible Builds `_ - * `Python 3.12.0 from a supply chain security perspective - `_ - * `Defending against the PyTorch supply chain attack PoC - `_ - -.. _code_reviews: - -Are code reviews performed? -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Risk: High - -Code reviews can detect unintentional vulnerabilities or possible introduction -of malicious code. Possible attacks can be detected in which the account of a -team member has been infiltrated. - -Does the project involve people from several organisations? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Risk: Low - -This is taken as an indication of a lower number of trustworthy code reviewers. -For this purpose, you can search for different entries in the * Company* field -in the profiles. At least three different companies in the last 30 commits are -desirable, whereby each of these team members should have made at least five -commits. - -Risk assessment of the builds ------------------------------ - -.. _lock-dependencies: - -Are dependencies declared and fixed in the project? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Risk: Medium - -In your project, dependencies used during the build and release process should -be pinned. A pinned dependency should be explicitly set to a specific hash and -not just to a mutable version or version range. - -:doc:`../envs/spack/index` writes these hashes for the respective environment in -:ref:`spack_lock`, :doc:`../envs/uv/index` in :ref:`uv_lock`. - -.. tip:: - Üblicherweise verwalte ich diese Dateien jedoch nur bei - :doc:`python-basics:packs/apps` in :doc:`../git/index`. Bei - :doc:`python-basics:libs/index` schränke ich üblicherweise lediglich den - Versionsbereich der Abhängigkeiten in der :file:`pyproject.toml`-Datei ein. - -:doc:`../envs/spack/index` writes these hashes for the respective environment in -:ref:`spack_lock`, :doc:`../envs/uv/index` in :ref:`uv_lock`. These files should -therefore also be checked in with the source code. - -This can reduce the following security risks for -:doc:`python-basics:packs/apps`: - -* Testing and deployment are done with the same software, which reduces - deployment risks, simplifies debugging and enables reproducibility. -* Compromised dependencies do not undermine the security of the project. -* Substitution attacks, :abbr:`i.e. (id est)` attacks that aim to confuse - dependencies, can thus be countered. - -However, fixing dependencies should not prevent software updates. You can -reduce this risk by - -* automated tools that notify you when dependencies in your project are out of - date -* update applications that lock dependencies quickly. - -.. _S105: https://docs.astral.sh/ruff/rules/hardcoded-password-string/ -.. _S301: https://docs.astral.sh/ruff/rules/suspicious-pickle-usage/ -.. _S307: https://docs.astral.sh/ruff/rules/suspicious-eval-usage/ -.. _S113: https://docs.astral.sh/ruff/rules/request-without-timeout/ -.. _S324: https://docs.astral.sh/ruff/rules/hashlib-insecure-hash-function/ -.. _S608: https://docs.astral.sh/ruff/rules/hardcoded-sql-expression/ -.. _S608: https://docs.astral.sh/ruff/rules/hardcoded-sql-expression/ + $ uv add pandas + $ uv pip tree + myapp v0.1.0 + └── pandas v3.0.5 + ├── numpy v2.5.1 + └── python-dateutil v2.9.0.post0 + └── six v1.17.0 + +Although you only wanted to add one package (``pandas``), you ended up with four +without being asked. Even if just one of these transitive packages – which you +never explicitly installed – were to contain a security vulnerability, your +entire application would be at risk. This greatly increases the attack surface +compared to what you yourself specified in ``dependencies``. + +Here are just a few recent attacks on the software supply chain: + +LiteLLM/Telnyx + In March this year, following the disclosure of an API token due to an + `exploited trivy dependency + `_, + versions of the `litellm `_ and `telnyx + `_ packages were published on :term:`PyPI` + that contained malware designed to steal login credentials. The malware was + executed upon installation, collected sensitive login credentials and files, + and forwarded them to a remote API. + + .. seealso:: + `Incident Report: LiteLLM/Telnyx supply-chain attacks, with guidance + `_ + +Email phishing attack targeting PyPI users + In April 2026, the wave of phishing attacks – which exploit domain name + confusion and involve the sending of emails that appear legitimate – + continues. This is the same attack that occurred in June 2025 and targets + many other open-source repositories, albeit with a different domain name. + + .. seealso:: + `PyPI Users Email Phishing Attack + `_ + +Shai-Hulud + In November 2025, an attack on the `npm `_ ecosystem + escalated, exploiting compromised accounts to publish malicious packages. + This campaign, known as Shai-Hulud, targeted a large number of JavaScript + packages and stole credentials to spread further. Although :term:`PyPI` + itself was not exploited, some PyPI login credentials were exposed in + compromised repositories. + + .. seealso:: + `PyPI and Shai-Hulud: Staying Secure Amid Emerging Threats + `_ + +.. _token_exfiltration: + +Token Exfiltration + In September 2025, code was injected into GitHub Actions workflows in over + 570 repositories, resulting in the theft of more than 3,300 secrets, + including :term:`PyPI` and npm tokens as well as AWS access keys. PyPI + blocked all the stolen tokens and urged all users to switch to + :ref:`trusted_publishers`. + + .. seealso:: + `Token Exfiltration Campaign via GitHub Actions Workflows + `_ + +ZIP parser confusion attacks + In August 2025, :term:`PyPI` introduced restrictions designed to prevent + confusion arising from different implementations of the ZIP parser in + installation and verification programmes for Python packages. :term:`uv` + exhibited different extraction behaviour to many Python-based installation + programmes that use :mod:`zipfile`. + + .. seealso:: + `uv security advisory: ZIP payload obfuscation + `_ + +.. _ultralytics: + +Ultralytics + In December 2024, `ultralytics `_ + fell victim to a supply-chain attack in which the project’s GitHub Actions + workflows were first compromised, followed by its PyPI API tokens. No + vulnerability in :term:`PyPI` was exploited to carry out this attack. + + .. seealso:: + `Supply-chain attack analysis: Ultralytics + `_ + +These are not theoretical attacks. They have occurred in real-world projects +with millions of users. If you discover a malicious package on PyPI, you can +report it via `PyPI’s security reporting system `_. + +In June 2026, Seth Larson, a member of the `Python Security Response Team +`_, published a chart showing the +annual trend in security vulnerabilities published by Python, which indicates +that the number is expected to triple in 2026: + +.. figure:: python-cve-per-year.png + :alt: Number of CVEs published annually by Python. It is expected that around + 65 CVEs will be published in 2026. + + Source: https://mastodon.social/@sethmlarson/116680832573268456 + +However, this merely reflects the results and does not provide an overview of +the reports received. Many of these are closed and treated instead as +non-security-related error reports; others are closed as neither security nor +error reports. Here is the number of reports created since July 2024 relating to +GitHub security advisories: + +.. figure:: ghsas-by-month.webp + :alt: Chart showing new security reports. From 2024, single-digit numbers or + zero per month, rising to around 40 in 2026. + + Source: Hugo van Kemenade: `Security: line goes up + `_ .. toctree:: :hidden: :titlesonly: :maxdepth: 0 + own-code + dependencies + environments sbom diff --git a/docs/productive/security/own-code.rst b/docs/productive/security/own-code.rst new file mode 100644 index 00000000..ffa46488 --- /dev/null +++ b/docs/productive/security/own-code.rst @@ -0,0 +1,159 @@ +.. SPDX-FileCopyrightText: 2023 cusy GmbH +.. +.. SPDX-License-Identifier: BSD-3-Clause + +Your own code +============= + +Attacks on the supply chain do not only stem from :doc:`dependencies +`; your own code can also provide points of entry. A PyPI token +hard-coded into the source code, once uploaded to a public repository, provides +everything needed to launch an attack, compromise your account and publish +malicious packages under your name. Apart from secrets, common security flaws +are often hidden in everyday coding patterns that may initially appear harmless +during a code review and can be overlooked by humans. Detecting these using a +linter is the first line of defence. + +The eternal secret +------------------ + +Leaked credentials are the starting point for many security breaches in the +supply chain. An exposed :term:`PyPI` token makes it possible to publish +versions of your packages containing backdoors. An exposed database URL makes it +possible to steal data. And yet, this pattern is all too common. It is better to +use environment variables: + +.. code-block:: python + + import os + + DATABASE_KEY = os.environ["DB_KEY"] + DATABASE_URL = os.environ["DB_URL"] + +.. warning:: + Git never forgets: once you’ve managed a secret via Git, it remains in your + repository’s history forever. Simply deleting it in a later commit doesn’t + really help. Anyone with access to the repository can extract these + credentials. In attacks, the Git history is often scoured for secrets first, + and a PyPI token or cloud credentials that have been exposed are often the + first step in a supply chain compromise. + +Cryptographic vulnerabilities +----------------------------- + +Other common security vulnerabilities include cryptographic weaknesses such as +`MD5 `_ and `SHA-1 +`_. MD5 collisions were first demonstrated +in 2004 and SHA-1 collisions in 2017. This means that collisions can be +generated using different inputs that produce the same hash value. This enables +the forgery of certificates, the manipulation of downloads or the circumvention +of integrity checks. Therefore, do not use either of these algorithms for +security purposes; instead, use `SHA-256 `_ +or better: + +.. code-block:: python + + import hashlib + + digest = hashlib.sha256(payload).hexdigest() + +Stalled connections +------------------- + +This may be subtle, but it is nonetheless dangerous, as a slow server can bring +your process to a standstill indefinitely. An attack via such a server, with +which your application communicates, can bring every request to a standstill, +exhaust your thread pool and trigger a denial-of-service attack. Your entire +application will then grind to a halt because you forgot to specify a parameter. +You should therefore always specify a timeout: + +.. code-block:: pycon + + >>> import httpx + >>> r = httpx.get("https://httpbin.org/get", timeout=30) + httpx.ReadTimeout: The read operation timed out + +.. _bandit: + +Detect security vulnerabilities with Ruff +----------------------------------------- + +:doc:`../qa/ruff` is a fast Python linter that includes comprehensive security +rules from :ref:`Bandit `: + +.. code-block:: console + + $ uvx ruff check --select S . + +.. seealso:: + Further information can be found in the `Ruff security rules documentation + `_. + +For future checks, you can configure ``ruff`` in the :file:`pyproject.toml` +file: + +.. code-block:: toml + + [tool.ruff] + lint.select = ["S"] + +The ``["S"]`` security rules, which use Bandit checks, detect hard-coded +secrets, weak encryption and insecure deserialisation. Ruff runs in less than a +second, so you can run it whilst typing in your IDE and before every commit. All +three vulnerabilities mentioned above are detected, along with many more, +including: + ++--------+-----------------------------------------------------------------------+ +| Rule | Description | ++--------+-----------------------------------------------------------------------+ +| `S105`_| Hard-coded secrets | ++--------+-----------------------------------------------------------------------+ +| `S301`_| :doc:`/data-processing/serialisation-formats/pickle/index` and other | +| | insecure deserialisation | ++--------+-----------------------------------------------------------------------+ +| `S307`_| Use of :func:`eval` with untrusted input | ++--------+-----------------------------------------------------------------------+ +| `S113`_| Missing timeouts | ++--------+-----------------------------------------------------------------------+ +| `S324`_| Weak cryptography, such as MD5 collisions | ++--------+-----------------------------------------------------------------------+ +| `S608`_| SQL injection via string formatting | ++--------+-----------------------------------------------------------------------+ + +.. seealso:: + * `flake8-bandit (S) `_ + * `lint.flake8-bandit + `_ + +You can also integrate Bandit into Jupyter Notebooks, :abbr:`IDEs (Integrated +Development Wnvironments)` and :doc:`../git/advanced/hooks/prek`. + +You can also use :doc:`../qa/pysa` for `taint analysis +`_. + +For GitHub repositories, you can alternatively use `CodeQL +`_; see also `codeql-action +`_. + +Trusted publishing +------------------ + +In an earlier section, we’ve already provided some guidance on how to secure the +publication of Python packages on :term:`PyPI`: + +.. seealso:: + * :ref:`secure-release-workflow` + * :ref:`add_2fa` + +.. seealso:: + * `Publishing package distribution releases using GitHub Actions CI/CD + workflows + `_ + +.. _S105: https://docs.astral.sh/ruff/rules/hardcoded-password-string/ +.. _S301: https://docs.astral.sh/ruff/rules/suspicious-pickle-usage/ +.. _S307: https://docs.astral.sh/ruff/rules/suspicious-eval-usage/ +.. _S113: https://docs.astral.sh/ruff/rules/request-without-timeout/ +.. _S324: https://docs.astral.sh/ruff/rules/hashlib-insecure-hash-function/ +.. _S608: https://docs.astral.sh/ruff/rules/hardcoded-sql-expression/ +.. _S608: https://docs.astral.sh/ruff/rules/hardcoded-sql-expression/ diff --git a/docs/productive/security/pypi-changes.png b/docs/productive/security/pypi-changes.png new file mode 100644 index 00000000..7034fab2 Binary files /dev/null and b/docs/productive/security/pypi-changes.png differ diff --git a/docs/productive/security/python-cve-per-year.png b/docs/productive/security/python-cve-per-year.png new file mode 100644 index 00000000..175a3cd6 Binary files /dev/null and b/docs/productive/security/python-cve-per-year.png differ diff --git a/docs/productive/security/sbom.rst b/docs/productive/security/sbom.rst index c227574a..a281bc20 100644 --- a/docs/productive/security/sbom.rst +++ b/docs/productive/security/sbom.rst @@ -7,30 +7,39 @@ Software Bill of Materials (SBOM) A Software Bill of Materials (SBOM) is a document used to exchange information about software and its composition. This format is primarily used in the -security field to check software and its dependencies for vulnerabilities using +security sector to check software and its dependencies for vulnerabilities using vulnerability databases such as `CVE `_ and `OSV -`_. The SBOM format used by the CPython project is `SPDX +`_. + +The SBOM format used by the CPython project is `SPDX `_, which -can be converted to other formats as needed. The SBOM file for the dependencies -included in CPython is maintained at `Misc/sbom.spdx.json +can be converted to other formats if required. The SBOM file for the +dependencies included in CPython is maintained at `Misc/sbom.spdx.json `_. The file is generated using `Tools/build/generate_sbom.py `_. +You can retrieve the SBOM file for any Python version at :samp:`https://www.python.org/ftp/python/{MAJOR.MINOR.PATCH}/Python-{MAJOR.MINOR.PATCH}.tgz.spdx.json`, +for example at +https://www.python.org/ftp/python/3.14.6/Python-3.14.6.tgz.spdx.json. + +.. seealso:: + * `Python Software Bill-of-Materials Information + `_ -Generating an SBOM File ------------------------ +Creating an SBOM file +--------------------- -… with uv -~~~~~~~~~ +… using uv +~~~~~~~~~~ -:term:`uv` provides an easy way to generate an SBOM file in the CycloneDX v1.5 -format using: +:term:`uv` offers a simple way to create an SBOM file in CycloneDX v1.5 format +using: .. code-block:: console $ uv export --format='cyclonedx1.5' > sbom.cdx.json -However, the file contains only very basic information; for example, for +However, the file contains only very basic information, for example, for `cusy.tasks `_: .. code-block:: json @@ -48,11 +57,11 @@ However, the file contains only very basic information; for example, for ] } -Using ``uv export --all-groups --format='cyclonedx1.5' > sbom.cdx.json``, you +Using ``uv export --all-groups --format="cyclonedx1.5" > sbom.cdx.json``, you can also include all dependency groups in the SBOM file. -… using CycloneDX Python -~~~~~~~~~~~~~~~~~~~~~~~~ +… with CycloneDX Python +~~~~~~~~~~~~~~~~~~~~~~~ The output from `CycloneDX Python `_ is @@ -98,7 +107,7 @@ considerably more comprehensive: "version": "26.2.0" } -The command to generate the file is: +The command-line command to generate the file is: .. code-block:: console @@ -184,17 +193,18 @@ CycloneDX SBOM file can be checked into the repository: } In the :term:`CI` workflow, the placeholder SBOM file is then checked out along -with the code. sbomify then enriches it with the latest information. Hatchling -subsequently builds the wheel using the latest SBOM file, and finally the :term:`wheel` is published to :term:`PyPI`. +with the code. sbomify then populates it with the latest information. Hatchling +then builds the :term:`wheel ` using the latest SBOM file, and finally +the wheel is published to PyPI. Analysis -------- There are numerous tools available for security and licence checks, each -focusing on different problem areas. Two open-source tools for SBOM analysis are -`Dependency Track `_ and `GUAC `_. -With ``sbomify-action``, you can upload SBOMs directly from the :term:`CI` -pipeline to your Dependency Track instance using: +focusing on different areas of concern. Two open-source tools for SBOM analysis +are `Dependency Track `_ and `GUAC +`_. With ``sbomify-action``, you can upload SBOMs from the CI +pipeline directly to your Dependency Track instance using: .. code-block:: yaml