From 2de4ab1571c772d56a61a3d7d455ab1856a66437 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patryk=20B=C4=99za?= Date: Tue, 4 Aug 2026 19:00:18 +0200 Subject: [PATCH 1/6] ci: audit workflow definitions with zizmor Nothing checks the workflows themselves, which is the surface the implant arrived through. zizmor covers template injection, mutable action tags and actions with known advisories, and uploads its own SARIF so findings land alongside the other code scanning tools. Clean at the default persona today, so it can be required immediately and catches regressions from here. --- .github/workflows/zizmor.yml | 47 ++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 .github/workflows/zizmor.yml diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml new file mode 100644 index 0000000000..d7efb59c69 --- /dev/null +++ b/.github/workflows/zizmor.yml @@ -0,0 +1,47 @@ +name: Workflow Security Analysis + +# Audits the workflow definitions themselves for template injection, mutable +# action tags and known-vulnerable actions. Runs on every branch for the same +# reason the disguised-content scan does: a push to a branch with no open PR +# fires no `pull_request` event. +on: + pull_request: + push: + branches: + - '**' + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +# Granted per job instead, so a step only ever holds what it needs. +permissions: {} + +jobs: + zizmor: + name: "Audit workflows" + # Fork PRs produce no push event, so `pull_request` covers those; the guard + # keeps same-repo PRs from being audited twice. + if: >- + github.event_name != 'pull_request' || + github.event.pull_request.head.repo.full_name != github.repository + runs-on: ubuntu-latest + timeout-minutes: 10 + + permissions: + contents: read + security-events: write # zizmor uploads its own SARIF + + steps: + - name: Checkout repository + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - name: Run zizmor + uses: zizmorcore/zizmor-action@3dc1ecc9bcb9e94e9b2c709687979e1298497054 # v0.6.2 + with: + # Fork PRs cap security-events at read and so cannot upload; zizmor + # then prints its findings and fails the job instead. + advanced-security: ${{ github.event_name != 'pull_request' }} From 3e9526a4adcb71eb379cc797d250445fd9d2508b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patryk=20B=C4=99za?= Date: Tue, 4 Aug 2026 19:24:56 +0200 Subject: [PATCH 2/6] docs: clarify the zizmor workflow comments --- .github/workflows/zizmor.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index d7efb59c69..199b705b8d 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -1,11 +1,11 @@ +# Static analysis of this repo's own workflow files: template injection, +# unpinned actions, over-broad permissions. name: Workflow Security Analysis -# Audits the workflow definitions themselves for template injection, mutable -# action tags and known-vulnerable actions. Runs on every branch for the same -# reason the disguised-content scan does: a push to a branch with no open PR -# fires no `pull_request` event. on: pull_request: + # A push to a branch with no open PR fires no `pull_request` event, so pushes + # are covered separately rather than relying on PRs alone. push: branches: - '**' From a6cffa3e259beebae87957b0bb8bf1ca4d790a57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patryk=20B=C4=99za?= Date: Tue, 4 Aug 2026 19:26:16 +0200 Subject: [PATCH 3/6] docs: state the job guard as a single skip condition --- .github/workflows/zizmor.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index 199b705b8d..452850df56 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -21,11 +21,12 @@ permissions: {} jobs: zizmor: name: "Audit workflows" - # Fork PRs produce no push event, so `pull_request` covers those; the guard - # keeps same-repo PRs from being audited twice. + # Skip a same-repo PR: the push to its branch already audited these commits. + # Everything else runs, which leaves pushes and fork PRs, the latter having + # no push event of their own. if: >- - github.event_name != 'pull_request' || - github.event.pull_request.head.repo.full_name != github.repository + !(github.event_name == 'pull_request' && + github.event.pull_request.head.repo.full_name == github.repository) runs-on: ubuntu-latest timeout-minutes: 10 From 44abf4693ac1c338945b1da7713011a6aad42f7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patryk=20B=C4=99za?= Date: Tue, 4 Aug 2026 19:28:10 +0200 Subject: [PATCH 4/6] docs: correct the permissions comment to job scope --- .github/workflows/zizmor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index 452850df56..a4876c48f4 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -15,7 +15,7 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true -# Granted per job instead, so a step only ever holds what it needs. +# No token scopes by default; each job opts into only what it needs. permissions: {} jobs: From 6b663fd92763331909181a68198ecf3c367e1004 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patryk=20B=C4=99za?= Date: Tue, 4 Aug 2026 19:31:15 +0200 Subject: [PATCH 5/6] ci: align zizmor triggers and runner with convention Gate on pull requests rather than every push. Auditing a push to a feature branch does not help, since a malicious workflow runs on that push whatever the audit reports afterwards, so the useful point is before it reaches main. That also removes the double-run guard, which was the unclear part. Matches how sigstore/sigstore-rs, matrix-org/matrix-rust-sdk and apache/iceberg-rust configure the same tool, and switches to the runner label the other workflows here use. --- .github/workflows/zizmor.yml | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index a4876c48f4..1941e56a0d 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -2,17 +2,17 @@ # unpinned actions, over-broad permissions. name: Workflow Security Analysis +# Auditing a push to a feature branch would not help, because a malicious +# workflow runs on that push whatever this reports afterwards. What matters is +# catching it before it reaches main, so pull requests are the gate. on: - pull_request: - # A push to a branch with no open PR fires no `pull_request` event, so pushes - # are covered separately rather than relying on PRs alone. push: - branches: - - '**' + branches: [main] + pull_request: workflow_dispatch: concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true # No token scopes by default; each job opts into only what it needs. @@ -21,13 +21,7 @@ permissions: {} jobs: zizmor: name: "Audit workflows" - # Skip a same-repo PR: the push to its branch already audited these commits. - # Everything else runs, which leaves pushes and fork PRs, the latter having - # no push event of their own. - if: >- - !(github.event_name == 'pull_request' && - github.event.pull_request.head.repo.full_name == github.repository) - runs-on: ubuntu-latest + runs-on: warp-ubuntu-2404-x64-2x timeout-minutes: 10 permissions: @@ -43,6 +37,7 @@ jobs: - name: Run zizmor uses: zizmorcore/zizmor-action@3dc1ecc9bcb9e94e9b2c709687979e1298497054 # v0.6.2 with: - # Fork PRs cap security-events at read and so cannot upload; zizmor - # then prints its findings and fails the job instead. - advanced-security: ${{ github.event_name != 'pull_request' }} + # A fork's pull request gets a read-only token, so uploading SARIF + # would fail with a 403. Turned off for those, and zizmor then prints + # its findings and fails the job, which still surfaces them. + advanced-security: ${{ !github.event.pull_request.head.repo.fork }} From 64306e147659c4cf30ff9ff04d0a14f759c7a2d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patryk=20B=C4=99za?= Date: Tue, 4 Aug 2026 19:35:34 +0200 Subject: [PATCH 6/6] docs: drop the redundant trigger comment --- .github/workflows/zizmor.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index 1941e56a0d..954d94991e 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -2,9 +2,6 @@ # unpinned actions, over-broad permissions. name: Workflow Security Analysis -# Auditing a push to a feature branch would not help, because a malicious -# workflow runs on that push whatever this reports afterwards. What matters is -# catching it before it reaches main, so pull requests are the gate. on: push: branches: [main]