Open source libraries constitute a significant portion of the world’s digital infrastructure. Securing the Open Source supply chain (OSSC) is therefore an increasing concern, with examples of sophisticated attacks against the ecosystem (e.g., the 2024xz utils backdoor) andmalware attacks on PyPI highlighting the need for supply chain security to be taken seriously.The Python Software Foundation (PSF) is also taking the importance of the OSSC seriously, as demonstrated by thecreation of the PSF Security Developer in Residence position in 2023.
With theSupply-chain Levels for Software Artifacts (SLSA) framework andOpenID Connect (OIDC) standard being widely adopted, several high level developer tools, maintained by professional security teams, have been created with clear recommendations on how to use them.
This SPEC outlines pragmatic recommendations for adopting these security tools and recommendations on how to publish release artifacts securely.Securelybuilding release artifacts will be covered in a later SPEC. This set of recommendations complements the recommendations fromSPEC 6 — Keys to the Castle.
While this SPEC is written with GitHub in mind, the same recommendations apply to other services, such as GitLab.
Projects can highlight their adoption of this SPEC by including a SPEC badge.
[](https://scientific-python.org/specs/spec-0008/)
|SPEC 8 — Securing the Release Process| .. |SPEC 8 — Securing the Release Process| image:: https://img.shields.io/badge/SPEC-8-green?labelColor=%23004811&color=%235CA038 :target: https://scientific-python.org/specs/spec-0008/
With a focus on securing the release artifact distribution process, the following processes and standards should be adopted.
The release process should be clearly and fully documented in the developer documentation and describe each step to make a release and the permissions required to do so.It is recommended that this is a dedicated page in the developer section of the documentation website, though providing instructions in aRELEASING.md in the top level of the repository is also a common approach.
Workflows that publish release artifacts should haverun triggers that require intentional actions by the release team (e.g.,workflow_dispatch in GitHub Actions) and require multiple release team members to approve the workflow to run (c.f. “Use GitHub Actions environments” section below).This is to safeguard the project from any one maintainer having the ability to commit to the default branch and make a release directly.
It is also strongly recommended that release managers usesigned commits, so that each release corresponds to a verified commit. Note that it can be difficult to enforce this via GitHub permissions without requiring all contributors to also sign their commits, which may be undesirable for many projects.
The branch from which the release is made should also be protected.
To restrict the attack surface area of arbitrary code execution in CI runners, thedefault runner permissions should be restricted to the minimum possible (read access). In the GitHub Action workflow, this is accomplished by defining the following workflow global permissions block before any jobs are defined.
permissions:contents:readElevating permissions beyond this should be done at the job level by redefining the permissions block in the job.
GitHub allows restricting the actions that workflows can use via the repository actions permissions settings athttps://github.com/$ORG/$PROJECT/settings/actions.A reasonable default is to select the
$ORG, and selectnon-$ORG, actions and reusable workflowsoption and the suboptions:
ConsultManaging GitHub Actions permissions for your repository for more details.
Use aGitHub Actions environment
environment:name:publish-packageand enforce additional review by at least one other release team maintainer to run a GitHub Actions workflow that publishes to PyPI.Additional reviewer requirements can be configured per GitHub Actions environment underhttps://github.com/$ORG/$PROJECT/settings/environments/ in the “Deployment protection rules” section.

GitHub actions must be pinned using full commit SHA corresponding to the release version being used.Using versions or small hashes is susceptible to attacks.
-uses:actions/some-action@1fe14e04876783b259436247a3898d2fe7d5548f#vX.Y.ZDependabot can be used to automatically update the hashes.It is important that this happens as part of a reviewed process.
# .github/dependabot.ymlversion: 2updates: # Maintain dependencies for GitHub Actions - package-ecosystem: "github-actions" directory: "/" schedule: interval: "monthly" groups: actions: patterns: - "*"
A component of SLSA issoftware attestation which allows for public validation of software artifacts and provenance.GitHub provides theactions/attest-build-provenance GitHub Action which implements SLSA to generate signed build provenance attestations for workflow artifacts.Attestations are published to the project GitHub underhttps://github.com/$ORG/$PROJECT/attestations/.
-uses:actions/attest-build-provenance@<full action commit SHA># vX.Y.Zwith:subject-path:"dist/<package name>-*"GitHub has also added thegh attestation verify command to the GitHub CLI utility, which verifies the integrity and provenance of an artifact using its associated cryptographically signed attestations.This can be used by individual users and also in GitHub Actions workflows where the GitHub CLI utility is installed by default.
-name:Verify artifact attestationenv:GH_TOKEN:${{ secrets.GITHUB_TOKEN }}shell:bashrun:| for artifact in dist/*; do echo "# ${artifact}" gh attestation verify "${artifact}" --repo ${{ github.repository }} doneTrusted Publishers provide a way to securely establish a short lived authentication token between a project repository and a distribution platform — such as PyPI.It replaces the need to use a long lived token to authenticate, reducing the security risks associated with authentication tokens (e.g., tokens being compromised, the need to rotate tokens).
Trusted Publishers can be used in GitHub Actions by using thepypa/gh-action-pypi-publish GitHub Action defaults in a GitHub Actions environment.
jobs:publish:name:Publish release to PyPIruns-on:ubuntu-latestenvironment:publish-packagepermissions:# IMPORTANT: this permission is mandatory for trusted publishingid-token:writesteps:# retrieve your distributions here# ...-name:Publish distribution to PyPIuses:pypa/gh-action-pypi-publish@<full action commit SHA># vX.Y.Zwith:print-hash:trueThe following is a complete example of a workflow which can be used as a starting point:
name:publish distributionson:workflow_dispatch:concurrency:group:${{ github.workflow }}-${{ github.ref }}cancel-in-progress:truepermissions:contents:readjobs:publish:name:Publish Python distribution to PyPIruns-on:ubuntu-latestpermissions:id-token:writeattestations:writeenvironment:name:publish-packagesteps:# - name: Collect built artifacts# ...-name:Generate artifact attestation for sdist and wheelsuses:actions/attest-build-provenance@<full action commit SHA># vX.Y.Zwith:subject-path:"dist/<package name>-*"-name:Verify artifact attestationenv:GH_TOKEN:${{ secrets.GITHUB_TOKEN }}shell:bashrun:| for artifact in dist/*; do echo "# ${artifact}" gh attestation verify "${artifact}" --repo ${{ github.repository }} done-name:Publish distribution to PyPIuses:pypa/gh-action-pypi-publish@<full action commit SHA># vX.Y.Zwith:print-hash:true