From 9901f20cd7ed876851acc49aabd9e05baf23bba0 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Sat, 29 Aug 2026 22:26:41 +0200 Subject: [PATCH 1/7] CI: pin the actions in the 32bit and bigendian workflows to commit SHAs Both workflows were added after 4dfcf29ad ("CI: pin all GitHub Actions to commit SHAs") and used floating tags, so they were the only two workflows not following that policy. zizmor flagged all eight uses as unpinned-uses. The SHAs for checkout, setup-python and cache are the ones already used by the other workflows. docker/setup-qemu-action was not pinned anywhere yet; v4 currently resolves to 96fe6ef7f3 (v4.2.0), so this is a no-op today. Co-Authored-By: Claude Opus 5 --- .github/workflows/32bit.yml | 8 ++++---- .github/workflows/bigendian.yml | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/32bit.yml b/.github/workflows/32bit.yml index 7462392dea..e33e915468 100644 --- a/.github/workflows/32bit.yml +++ b/.github/workflows/32bit.yml @@ -90,14 +90,14 @@ jobs: borg.testsuite.helpers.time_test steps: - - uses: actions/checkout@v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: # Just fetching one commit is not enough for setuptools-scm, so we fetch all. fetch-depth: 0 fetch-tags: true - name: Set up Python - uses: actions/setup-python@v7 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: # same python version as in the container, so both sides are comparable python-version: '3.13' @@ -106,7 +106,7 @@ jobs: # (msgpack, PyYAML, backports-zstd, borghash, borgstore) are the expensive # part of the container setup, so keep them from run to run. - name: Cache pip-built wheels (armv7) - uses: actions/cache@v6 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: .pip-cache-armv7 key: armv7-pip-${{ hashFiles('pyproject.toml') }} @@ -136,7 +136,7 @@ jobs: echo "BORG_VERSION=$("$RUNNER_TEMP/venv-native/bin/borg" --version | cut -d' ' -f2)" >> $GITHUB_ENV - name: Set up QEMU - uses: docker/setup-qemu-action@v4 + uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0 with: platforms: arm diff --git a/.github/workflows/bigendian.yml b/.github/workflows/bigendian.yml index 166fceedd5..4c116b758e 100644 --- a/.github/workflows/bigendian.yml +++ b/.github/workflows/bigendian.yml @@ -74,14 +74,14 @@ jobs: borg.testsuite.helpers.msgpack_test steps: - - uses: actions/checkout@v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: # Just fetching one commit is not enough for setuptools-scm, so we fetch all. fetch-depth: 0 fetch-tags: true - name: Set up Python - uses: actions/setup-python@v7 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: # same python version as in the container, so both sides are comparable python-version: '3.13' @@ -90,7 +90,7 @@ jobs: # (msgpack, borghash, borgstore) are the expensive # part of the container setup, so keep them from run to run. - name: Cache pip-built wheels (s390x) - uses: actions/cache@v6 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: .pip-cache-s390x key: s390x-pip-${{ hashFiles('pyproject.toml') }} @@ -120,7 +120,7 @@ jobs: echo "BORG_VERSION=$("$RUNNER_TEMP/venv-native/bin/borg" --version | cut -d' ' -f2)" >> $GITHUB_ENV - name: Set up QEMU - uses: docker/setup-qemu-action@v4 + uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0 with: platforms: s390x From 70fc87ec1b90841d270389c3cf87999f62ef6464 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Sat, 29 Aug 2026 22:37:28 +0200 Subject: [PATCH 2/7] CI: do not persist the checkout credentials where they are not needed actions/checkout leaves the job token behind in .git/config, so anything that later archives the workspace also archives the credential (zizmor's artipacked audit). None of these workflows push with it - release.yml authenticates gh via GH_TOKEN, and peter-evans/create-pull-request uses its own token input - so they can all opt out. backport.yml is the exception and keeps the credentials: korthout/backport-action pushes the backport branch with a plain `git push`, which only works with what actions/checkout left in .git/config. Marked accordingly. Co-Authored-By: Claude Opus 5 --- .github/workflows/32bit.yml | 1 + .github/workflows/backport.yml | 5 ++++- .github/workflows/bigendian.yml | 1 + .github/workflows/black.yaml | 2 ++ .github/workflows/canary.yml | 2 ++ .github/workflows/ci.yml | 8 ++++++++ .github/workflows/codeql-analysis.yml | 1 + .github/workflows/fame.yml | 1 + .github/workflows/pypy.yml | 1 + .github/workflows/release.yml | 1 + 10 files changed, 22 insertions(+), 1 deletion(-) diff --git a/.github/workflows/32bit.yml b/.github/workflows/32bit.yml index e33e915468..6d5da2e743 100644 --- a/.github/workflows/32bit.yml +++ b/.github/workflows/32bit.yml @@ -95,6 +95,7 @@ jobs: # Just fetching one commit is not enough for setuptools-scm, so we fetch all. fetch-depth: 0 fetch-tags: true + persist-credentials: false - name: Set up Python uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index 4d6775b6ae..90c5d3c7e2 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -31,7 +31,10 @@ jobs: startsWith(github.event.comment.body, '/backport') ) steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + # No persist-credentials: false here (unlike the other workflows): + # backport-action pushes the backport branch with a plain `git push`, + # so it needs the credentials that actions/checkout leaves behind. + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 # zizmor: ignore[artipacked] - name: Create backport pull requests uses: korthout/backport-action@2e830a1d0b8269505846ddd407a70876913ad1f8 # v4.6.0 with: diff --git a/.github/workflows/bigendian.yml b/.github/workflows/bigendian.yml index 4c116b758e..4100f4cc00 100644 --- a/.github/workflows/bigendian.yml +++ b/.github/workflows/bigendian.yml @@ -79,6 +79,7 @@ jobs: # Just fetching one commit is not enough for setuptools-scm, so we fetch all. fetch-depth: 0 fetch-tags: true + persist-credentials: false - name: Set up Python uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 diff --git a/.github/workflows/black.yaml b/.github/workflows/black.yaml index ae24faf502..b5d8efdc35 100644 --- a/.github/workflows/black.yaml +++ b/.github/workflows/black.yaml @@ -28,6 +28,8 @@ jobs: timeout-minutes: 5 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - uses: psf/black@87928e6d6761a4a6d22250e1fee5601b3998086e # 26.5.1 with: version: "~= 24.0" diff --git a/.github/workflows/canary.yml b/.github/workflows/canary.yml index ea8ba0244f..fabc8057e5 100644 --- a/.github/workflows/canary.yml +++ b/.github/workflows/canary.yml @@ -48,6 +48,7 @@ jobs: with: fetch-depth: 0 fetch-tags: true + persist-credentials: false - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 @@ -131,6 +132,7 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 + persist-credentials: false # Same as the "Cache pip-built wheels" step in ci.yml (MSYS2's mingw # Python cannot use PyPI's win_amd64 wheels). Own key, because the diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2d18ec53dc..382b668320 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,6 +43,8 @@ jobs: steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - uses: astral-sh/ruff-action@278981a28ce3188b1e39527901f38254bf3aac89 # v4.1.0 security: @@ -52,6 +54,8 @@ jobs: steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: Set up Python uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: @@ -76,6 +80,7 @@ jobs: # Just fetching one commit is not enough for setuptools-scm, so we fetch all. fetch-depth: 0 fetch-tags: true + persist-credentials: false - name: Set up Python uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 @@ -190,6 +195,7 @@ jobs: # Just fetching one commit is not enough for setuptools-scm, so we fetch all. fetch-depth: 0 fetch-tags: true + persist-credentials: false - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 @@ -457,6 +463,7 @@ jobs: with: fetch-depth: 0 fetch-tags: true + persist-credentials: false # .pip-cache lives inside the workspace, which cross-platform-actions # rsyncs into the VM and back, so wheels built from sdists in one run @@ -737,6 +744,7 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 + persist-credentials: false # MSYS2's mingw Python cannot use PyPI's win_amd64 wheels, so pip builds # all compiled deps from source - incl. building maturin via cargo, just diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 9985c05f54..a94f1cbc77 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -50,6 +50,7 @@ jobs: with: # Just fetching one commit is not enough for setuptools-scm, so we fetch all. fetch-depth: 0 + persist-credentials: false - name: Set up Python uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: diff --git a/.github/workflows/fame.yml b/.github/workflows/fame.yml index 1baf34394c..9586003c9b 100644 --- a/.github/workflows/fame.yml +++ b/.github/workflows/fame.yml @@ -46,6 +46,7 @@ jobs: with: ref: master fetch-depth: 0 # git blame needs the whole history + persist-credentials: false - name: Set up Python uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 diff --git a/.github/workflows/pypy.yml b/.github/workflows/pypy.yml index 8d027811ea..efdd743f97 100644 --- a/.github/workflows/pypy.yml +++ b/.github/workflows/pypy.yml @@ -44,6 +44,7 @@ jobs: with: fetch-depth: 0 fetch-tags: true + persist-credentials: false - name: Set up PyPy 3.11 (nightly) uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a79b740182..f8850626a6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -62,6 +62,7 @@ jobs: # Just fetching one commit is not enough for setuptools-scm, so we fetch all. fetch-depth: 0 fetch-tags: true + persist-credentials: false - name: Set up Python uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 From 4b46dea4d72d83560105ea8a27ba3dd3426700cc Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Sat, 29 Aug 2026 22:37:54 +0200 Subject: [PATCH 3/7] CI: build the release binaries without restoring caches native_tests, vm_tests and windows_tests all restore a pip/tox cache and, on tags, also build the release binaries, attest their provenance and upload them. A cache entry is writable by any job that can write to the cache scope, so this is the cache-poisoning path zizmor points at: a poisoned dependency could end up inside an attested release artifact. Skip the caches on tag pushes. Branch and pull request runs - where the speed actually matters - keep caching, and release builds start cold, which for a handful of tag pushes per year is a good trade. zizmor's own autofix for this sets lookup-only: true, which would disable cache restore on *every* run; that is why these are marked ignored instead. While here, drop the second pip restore-key. "--" is a prefix of the tox cache keys ("--tox-..."), so a pip cache miss could restore a tox cache archive. Co-Authored-By: Claude Opus 5 --- .github/workflows/ci.yml | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 382b668320..e9488b88e3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -203,16 +203,21 @@ jobs: python-version: ${{ matrix.python-version }} - name: Cache pip - uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + # Not on tags: that is when the release binaries are built and attested, + # and they should not be able to pick up a poisoned cache entry. + if: ${{ !startsWith(github.ref, 'refs/tags/') }} + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 # zizmor: ignore[cache-poisoning] with: path: ~/.cache/pip key: ${{ runner.os }}-${{ runner.arch }}-pip-${{ hashFiles('requirements.d/development.lock.txt') }} restore-keys: | ${{ runner.os }}-${{ runner.arch }}-pip- - ${{ runner.os }}-${{ runner.arch }}- - name: Cache tox environments - uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + # Not on tags: that is when the release binaries are built and attested, + # and they should not be able to pick up a poisoned cache entry. + if: ${{ !startsWith(github.ref, 'refs/tags/') }} + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 # zizmor: ignore[cache-poisoning] with: path: .tox key: ${{ runner.os }}-${{ runner.arch }}-tox-${{ matrix.toxenv }}-${{ hashFiles('requirements.d/development.lock.txt', 'pyproject.toml') }} @@ -469,7 +474,10 @@ jobs: # rsyncs into the VM and back, so wheels built from sdists in one run # (most of the VM setup time) are reused by the next one. - name: Cache pip-built wheels - uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + # Not on tags: that is when the release binaries are built and attested, + # and they should not be able to pick up a poisoned cache entry. + if: ${{ !startsWith(github.ref, 'refs/tags/') }} + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 # zizmor: ignore[cache-poisoning] with: path: .pip-cache key: ${{ matrix.os }}-${{ matrix.version }}-py${{ matrix.pyver }}-pip-${{ hashFiles('requirements.d/development.lock.txt') }} @@ -750,7 +758,10 @@ jobs: # all compiled deps from source - incl. building maturin via cargo, just # to build the blake3 wheel. Persist the wheels pip builds. - name: Cache pip-built wheels - uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + # Not on tags: that is when the release binaries are built and attested, + # and they should not be able to pick up a poisoned cache entry. + if: ${{ !startsWith(github.ref, 'refs/tags/') }} + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 # zizmor: ignore[cache-poisoning] with: path: .pip-cache key: windows-msys2-pip-${{ hashFiles('pyproject.toml', 'requirements.d/pyinstaller.txt') }} From 3e78810fab8408df520341b444dce09d713ddd8f Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Sat, 29 Aug 2026 22:38:06 +0200 Subject: [PATCH 4/7] CI: pass matrix.binary to the shell via env, not by template expansion ${{ ... }} is expanded into the script before the shell sees it, so the value becomes code rather than data. matrix.binary is a literal from the matrix in this very workflow, so nothing is exploitable here, but going through env is the form that stays correct if the value ever stops being a literal - and it silences zizmor's template-injection audit. Co-Authored-By: Claude Opus 5 --- .github/workflows/ci.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e9488b88e3..7c1b4dfa50 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -358,13 +358,15 @@ jobs: - name: Prepare binaries (${{ matrix.binary }}) if: ${{ matrix.binary && startsWith(github.ref, 'refs/tags/') }} + env: + BINARY: ${{ matrix.binary }} run: | mkdir -p artifacts if [ -f dist/binary/borg.exe ]; then - cp dist/binary/borg.exe artifacts/${{ matrix.binary }} + cp dist/binary/borg.exe "artifacts/$BINARY" fi if [ -f dist/binary/borg.tgz ]; then - cp dist/binary/borg.tgz artifacts/${{ matrix.binary }}.tgz + cp dist/binary/borg.tgz "artifacts/$BINARY.tgz" fi echo "binary files" ls -l artifacts/ From 7b69f51fd0085e0a65c9db90ae342446b717975b Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Sat, 29 Aug 2026 22:38:19 +0200 Subject: [PATCH 5/7] CI: document why the backport workflow uses pull_request_target pull_request_target is dangerous when the workflow checks out or runs code from the pull request, because it runs with a writable token. This one does not: the checkout takes the base branch (no `ref:`), and the only thing that looks at pull request content is backport-action cherry-picking commits. Say so, so the next reader does not have to re-derive it, and mark the finding as reviewed. Co-Authored-By: Claude Opus 5 --- .github/workflows/backport.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index 90c5d3c7e2..78399d1c06 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -1,6 +1,12 @@ name: Backport pull request -on: +# pull_request_target is needed because the backport has to run with a token +# that may write to this repository - a pull_request run from a fork does not +# get one. It is safe here because nothing from the pull request is checked +# out or executed: the checkout below takes the base branch (no `ref:`), and +# the only thing that touches pull request content is backport-action, which +# just cherry-picks commits. +on: # zizmor: ignore[dangerous-triggers] pull_request_target: types: [closed] issue_comment: From 417c6c7d9c1719e86d5b373043f79cc7e688e4eb Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Sat, 29 Aug 2026 22:56:04 +0200 Subject: [PATCH 6/7] CI: move the accepted zizmor findings into .github/zizmor.yml The inline "# zizmor: ignore[...]" comments had to go after the version comment on the `uses:` line, which left two comments there: uses: actions/cache@55cc8345... # v6.1.0 # zizmor: ignore[cache-poisoning] Dependabot maintains those version comments when it bumps a pin (see .github/dependabot.yml, the github-actions ecosystem is updated weekly), and zizmor's own ref-version-mismatch audit stopped recognising the version, so the line is better left in the plain "@sha # version" form. The explanations stay in the workflows next to what they explain; the new config file only records which findings were accepted. Co-Authored-By: Claude Opus 5 --- .github/workflows/backport.yml | 4 ++-- .github/workflows/ci.yml | 8 ++++---- .github/zizmor.yml | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 .github/zizmor.yml diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index 78399d1c06..920d22b150 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -6,7 +6,7 @@ name: Backport pull request # out or executed: the checkout below takes the base branch (no `ref:`), and # the only thing that touches pull request content is backport-action, which # just cherry-picks commits. -on: # zizmor: ignore[dangerous-triggers] +on: pull_request_target: types: [closed] issue_comment: @@ -40,7 +40,7 @@ jobs: # No persist-credentials: false here (unlike the other workflows): # backport-action pushes the backport branch with a plain `git push`, # so it needs the credentials that actions/checkout leaves behind. - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 # zizmor: ignore[artipacked] + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Create backport pull requests uses: korthout/backport-action@2e830a1d0b8269505846ddd407a70876913ad1f8 # v4.6.0 with: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7c1b4dfa50..be2052b8d0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -206,7 +206,7 @@ jobs: # Not on tags: that is when the release binaries are built and attested, # and they should not be able to pick up a poisoned cache entry. if: ${{ !startsWith(github.ref, 'refs/tags/') }} - uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 # zizmor: ignore[cache-poisoning] + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: ~/.cache/pip key: ${{ runner.os }}-${{ runner.arch }}-pip-${{ hashFiles('requirements.d/development.lock.txt') }} @@ -217,7 +217,7 @@ jobs: # Not on tags: that is when the release binaries are built and attested, # and they should not be able to pick up a poisoned cache entry. if: ${{ !startsWith(github.ref, 'refs/tags/') }} - uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 # zizmor: ignore[cache-poisoning] + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: .tox key: ${{ runner.os }}-${{ runner.arch }}-tox-${{ matrix.toxenv }}-${{ hashFiles('requirements.d/development.lock.txt', 'pyproject.toml') }} @@ -479,7 +479,7 @@ jobs: # Not on tags: that is when the release binaries are built and attested, # and they should not be able to pick up a poisoned cache entry. if: ${{ !startsWith(github.ref, 'refs/tags/') }} - uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 # zizmor: ignore[cache-poisoning] + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: .pip-cache key: ${{ matrix.os }}-${{ matrix.version }}-py${{ matrix.pyver }}-pip-${{ hashFiles('requirements.d/development.lock.txt') }} @@ -763,7 +763,7 @@ jobs: # Not on tags: that is when the release binaries are built and attested, # and they should not be able to pick up a poisoned cache entry. if: ${{ !startsWith(github.ref, 'refs/tags/') }} - uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 # zizmor: ignore[cache-poisoning] + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: .pip-cache key: windows-msys2-pip-${{ hashFiles('pyproject.toml', 'requirements.d/pyinstaller.txt') }} diff --git a/.github/zizmor.yml b/.github/zizmor.yml new file mode 100644 index 0000000000..f69f0d0d0a --- /dev/null +++ b/.github/zizmor.yml @@ -0,0 +1,33 @@ +# Configuration for zizmor (https://docs.zizmor.sh/), the GitHub Actions +# static analyser. Run it over this repository with: +# +# zizmor . +# +# The findings listed here are the ones that were looked at and accepted. The +# reasoning lives next to the code it is about, in the workflow files - this +# file only says "yes, we know". Ignores are deliberately kept as whole-file +# entries rather than file:line, so that they do not silently stop matching +# whenever the surrounding workflow shifts by a line. + +rules: + artipacked: + ignore: + # backport.yml is the one workflow that must keep the credentials + # actions/checkout leaves behind: korthout/backport-action pushes the + # backport branch with a plain `git push`. + - backport.yml + + dangerous-triggers: + ignore: + # backport.yml needs pull_request_target for a writable token, and never + # checks out or runs pull request code. See the comment in the workflow. + - backport.yml + + cache-poisoning: + ignore: + # The cache steps in ci.yml are skipped on tag pushes, which is when the + # release binaries are built and attested - see the `if:` on each of them. + # zizmor flags any actions/cache in a tag-triggered workflow regardless. + # A new cache step in ci.yml needs that same `if:`; this ignore will not + # tell you about it. + - ci.yml From 468cb70dd341b89635f3f6786b9c46ac1c52ca9e Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Sat, 29 Aug 2026 22:57:06 +0200 Subject: [PATCH 7/7] CI: scope the workflow token to the job that needs it backport.yml declared contents: write and pull-requests: write for the whole workflow. It has only one job, so nothing actually ran with more than it does now, but this is the workflow triggered by pull_request_target, which is where the scope should be tightest. The comments there were also the wrong way round: contents: write is what lets backport-action push the backport branch, not what lets it comment. codeql-analysis.yml had no workflow-level permissions at all. Its one job is scoped correctly, but a second job added later would silently fall through to the repository default - which is "write" for this repository. Co-Authored-By: Claude Opus 5 --- .github/workflows/backport.yml | 7 +++++-- .github/workflows/codeql-analysis.yml | 4 ++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index 920d22b150..bf60ee93af 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -13,8 +13,7 @@ on: types: [created] permissions: - contents: write # so it can comment - pull-requests: write # so it can create pull requests + contents: read jobs: backport: @@ -22,6 +21,10 @@ jobs: runs-on: ubuntu-26.04 timeout-minutes: 5 + permissions: + contents: write # backport-action pushes the backport branch + pull-requests: write # ... and opens the pull request and comments on it + # Only run when pull request is merged # or when a comment starting with `/backport` is created by someone other than the # https://github.com/backport-action bot user (user id: 97796249). Note that if you use your diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index a94f1cbc77..c3009520c0 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -27,6 +27,10 @@ concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} +# The analyze job below raises this to what CodeQL needs. +permissions: + contents: read + jobs: analyze: name: Analyze