@@ -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,24 @@ 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 }}
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 }}
308343
309344 .. _digital-attestations :
310345
311346Digital Attestations
312- --------------------
347+ ~~~~~~~~~~~~~~~~~~~~
313348
314349Since 14 November 2024, :term: `PyPI ` also supports :pep: `740 ` with `Digital
315350Attestations <https://docs.pypi.org/attestations/> `_. PyPI uses the
@@ -337,7 +372,7 @@ are used for publishing:
337372 id-token : write
338373 steps :
339374 - name : Publish package distributions to PyPI
340- uses : pypa/gh-action-pypi-publish@release/v1
375+ uses : pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
341376
342377 .. note ::
343378 Support for the automatic creation of digital attestations and publishing
@@ -346,3 +381,57 @@ are used for publishing:
346381.. seealso ::
347382 `PyPI now supports digital attestations
348383 <https://blog.pypi.org/posts/2024-11-14-pypi-now-supports-digital-attestations/> `_
384+
385+ .. _zizmorcore :
386+
387+ zizmor
388+ ~~~~~~
389+
390+ `zizmor <https://docs.zizmor.sh >`_ can detect and resolve many security issues
391+ in typical GitHub Actions CI/CD configurations. zizmor is designed to integrate
392+ with GitHub Actions. A typical GitHub Action we use for zizmor looks like this:
393+
394+ .. code-block :: yaml
395+ :caption : .github/workflows/zizmor.yml
396+
397+ # https://github.com/woodruffw/zizmor
398+ name : Zizmor
399+
400+ on :
401+ push :
402+ branches : ["main"]
403+ pull_request :
404+ branches : ["**"]
405+
406+ concurrency :
407+ group : ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
408+ cancel-in-progress : true
409+
410+ permissions : {}
411+
412+ jobs :
413+ zizmor :
414+ name : Run zizmor
415+ runs-on : ubuntu-latest
416+ permissions :
417+ security-events : write # Required for upload-sarif (used by zizmor-action) to upload SARIF files.
418+ steps :
419+ - name : Checkout repository
420+ uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
421+ with :
422+ persist-credentials : false
423+ - name : Run zizmor
424+ uses : zizmorcore/zizmor-action@71321a20a9ded102f6e9ce5718a2fcec2c4f70d8 # v0.5.2
425+ with :
426+ persona : pedantic
427+
428+ .. _add_2fa :
429+
430+ 2FA for all development accounts
431+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
432+
433+ You should use two-factor authentication for all your accounts related to
434+ development – not just for :term: `PyPI `. Remember your version control accounts
435+ (`GitHub <https://github.com/ >`_, `GitLab <https://about.gitlab.com/ >`_,
436+ `Codeberg <https://codeberg.org/ >`_, `Forgejo <https://forgejo.org/ >`_) and
437+ email.
0 commit comments