@@ -183,16 +183,16 @@ PyPI at every time a release is created. Such a
183183 needs : [test]
184184 steps :
185185 - name : Checkout
186- uses : actions/checkout@v4
186+ uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
187187 with :
188188 fetch-depth : 0
189189 - name : Set up Python
190- uses : actions/setup-python@v5
190+ uses : actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
191191 with :
192192 python-version-file : .python-version
193193 cache-dependency-path : ' **/pyproject.toml'
194194 - name : Setup cached uv
195- uses : hynek/setup-cached-uv@v2
195+ uses : hynek/setup-cached-uv@4300ec2180bc77d705e626a34e381b81a4772c51 # v2.5.0
196196 - name : Create venv
197197 run : |
198198 uv venv
@@ -203,9 +203,9 @@ PyPI at every time a release is created. Such a
203203 - name : Retrieve and publish
204204 steps :
205205 - name : Retrieve release distributions
206- uses : actions/download-artifact@v4
206+ uses : actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
207207 - name : Publish package distributions to PyPI
208- uses : pypa/gh-action-pypi-publish@release/v1
208+ uses : pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
209209 with :
210210 username : __token__
211211 password : ${{ secrets.PYPI_TOKEN }}
@@ -227,17 +227,52 @@ Lines 38–41
227227.. seealso ::
228228
229229 * `GitHub Actions <https://docs.github.com/en/actions >`_
230+ * :doc: `cibuildwheel `
231+
232+ Securing the release workflow
233+ -----------------------------
234+
235+ Continuous deployment systems used to publish Python packages are a popular
236+ target for attacks. You can avoid many of these risks by following a few
237+ security recommendations:
238+
239+ Avoid insecure triggers
240+ Workflows that can be triggered by an attacker, particularly those that rely
241+ on inputs controlled by the attacker (such as :ref: `pull request
242+ <merge-pull-requests>` or :doc: `branch
243+ <Python4DataScience:productive/git/branch>` titles), have been used in the
244+ past to inject commands. In particular, the ``pull_request_target `` trigger
245+ in :ref: `github-actions ` should be avoided.
246+ Sanitise parameters and inputs
247+ Any workflow parameter or input that can be expanded into an executable
248+ command has the potential to be exploited in attacks. Sanitise values by
249+ passing them to commands as environment variables to prevent :abbr: `SSTI
250+ ( Server Side Template Injection ) ` attacks.
251+ Avoid mutable references
252+ Fix your dependencies in workflows.
253+
254+ * Prefer Git commit `SHA
255+ <https://en.wikipedia.org/wiki/Secure_Hash_Algorithms> `_ values over
256+ :doc: `Git tags <Python4DataScience:productive/git/tag >`, as tags are
257+ mutable.
258+ * Use a :ref: `uv_lock ` file for PyPI dependencies used in workflows.
259+
260+ Use verifiable deployments
261+ With :ref: `trusted_publishers `, you can use verifiable GitHub environments
262+ to build your Python packages. If you use GitHub Actions for continuous
263+ delivery, you should use :ref: `zizmorcore ` to detect and fix insecure
264+ workflows.
230265
231266.. _trusted_publishers :
232267
233268Trusted Publishers
234- ------------------
269+ ~~~~~~~~~~~~~~~~~~
235270
236271`Trusted Publishers <https://docs.pypi.org/trusted-publishers/ >`_ is a procedure
237272for publishing packages on the :term: `PyPI `. It is based on OpenID Connect and
238273requires neither a password nor a token. Only the following steps are required:
239274
240- #. Add a *Trusted Publishers * on PyPI
275+ #. Add a *Trusted Publisher * on PyPI
241276
242277 Depending on whether you want to publish a new package or update an existing
243278 one, the process is slightly different:
@@ -276,7 +311,7 @@ requires neither a password nor a token. Only the following steps are required:
276311 .. code-block :: diff
277312 :caption: .github/workflows/pypi.yml
278313 :lineno-start: 10
279- :emphasize-lines: 3, 4 -5
314+ :emphasize-lines: 3-5
280315
281316 package-and-deploy:
282317 runs-on: ubuntu-latest
@@ -292,24 +327,19 @@ requires neither a password nor a token. Only the following steps are required:
292327 Lines 13–14
293328 The ``write `` authorisation is required for *Trusted Publishing *.
294329
295- Zeilen 42 –44
330+ Zeilen 40 –44
296331 ``username `` and ``password `` are no longer required for the GitHub
297332 action ``pypa/gh-action-pypi-publish ``.
298333
299- .. code-block :: diff
334+ .. code-block :: yaml
300335 :lineno-start : 40
301336 :emphasize-lines : 3-
302337
303- - name: Publish package distributions to PyPI
304- uses: pypa/gh-action-pypi-publish@release/v1
305- - with:
306- - username: __token__
307- - password: ${{ secrets.PYPI_TOKEN }}
308-
309- .. _digital-attestations :
310-
311- Digital Attestations
312- --------------------
338+ - name : Publish package distributions to PyPI
339+ uses : pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
340+ with :
341+ username : __token__
342+ password : ${{ secrets.PYPI_TOKEN }}
313343
314344 Since 14 November 2024, :term: `PyPI ` also supports :pep: `740 ` with `Digital
315345Attestations <https://docs.pypi.org/attestations/> `_. PyPI uses the
@@ -337,7 +367,7 @@ are used for publishing:
337367 id-token : write
338368 steps :
339369 - name : Publish package distributions to PyPI
340- uses : pypa/gh-action-pypi-publish@release/v1
370+ uses : pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
341371
342372 .. note ::
343373 Support for the automatic creation of digital attestations and publishing
@@ -346,3 +376,57 @@ are used for publishing:
346376.. seealso ::
347377 `PyPI now supports digital attestations
348378 <https://blog.pypi.org/posts/2024-11-14-pypi-now-supports-digital-attestations/> `_
379+
380+ .. _zizmorcore :
381+
382+ zizmor
383+ ~~~~~~
384+
385+ `zizmor <https://docs.zizmor.sh >`_ can detect and resolve many security issues
386+ in typical GitHub Actions CI/CD configurations. zizmor is designed to integrate
387+ with GitHub Actions. A typical GitHub Action we use for zizmor looks like this:
388+
389+ .. code-block :: yaml
390+ :caption : .github/workflows/zizmor.yml
391+
392+ # https://github.com/woodruffw/zizmor
393+ name : Zizmor
394+
395+ on :
396+ push :
397+ branches : ["main"]
398+ pull_request :
399+ branches : ["**"]
400+
401+ concurrency :
402+ group : ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
403+ cancel-in-progress : true
404+
405+ permissions : {}
406+
407+ jobs :
408+ zizmor :
409+ name : Run zizmor
410+ runs-on : ubuntu-latest
411+ permissions :
412+ security-events : write # Required for upload-sarif (used by zizmor-action) to upload SARIF files.
413+ steps :
414+ - name : Checkout repository
415+ uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
416+ with :
417+ persist-credentials : false
418+ - name : Run zizmor
419+ uses : zizmorcore/zizmor-action@71321a20a9ded102f6e9ce5718a2fcec2c4f70d8 # v0.5.2
420+ with :
421+ persona : pedantic
422+
423+ .. _add_2fa :
424+
425+ 2FA for all development accounts
426+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
427+
428+ You should use two-factor authentication for all your accounts related to
429+ development – not just for :term: `PyPI `. Remember your version control accounts
430+ (`GitHub <https://github.com/ >`_, `GitLab <https://about.gitlab.com/ >`_,
431+ `Codeberg <https://codeberg.org/ >`_, `Forgejo <https://forgejo.org/ >`_) and
432+ email.
0 commit comments