From 73415cd2dc81960ee9a7d59b74b5ebee18acba38 Mon Sep 17 00:00:00 2001 From: kingchenc Date: Tue, 2 Jun 2026 02:08:40 +0200 Subject: [PATCH] ci: zizmor security hardening (#133) * ci: pass ref context through env in release tag step zizmor flagged the "Resolve target tag" step in release.yml for template-injection: github.event_name / github.ref / github.ref_name were interpolated directly into the shell script. On a tag push the tag name is attacker-influenceable, so a crafted tag could inject commands. Move all three context values into the step env and reference them as shell variables instead. Verified with zizmor 1.16.3: template-injection findings on release.yml drop from 2 to 0. * ci: accept release.yml build caches via zizmor config The release pipeline restores Swatinem/rust-cache and actions/setup-node caches as a deliberate optimisation. zizmor flags all eight under cache-poisoning because release.yml publishes to crates.io / PyPI / npm. The caches are maintainer-controlled and the restore speedup is kept on purpose, so accept the finding via a zizmor config ignore for release.yml rather than running cache-free release builds. (Six of the eight are actions/setup-node, reported at Low confidence.) Adds .github/zizmor.yml; release.yml now reports 0 high findings. * ci: drop persisted checkout credentials on read-only jobs zizmor's artipacked audit flags every actions/checkout that keeps the default persisted credential: the token is written to the runner's .git/config, where it can leak if a later step packs .git into an uploaded artifact, or be read by another step in the same job. Set persist-credentials: false on the 20 checkouts whose jobs never push or authenticate to git (build/test/clippy/msrv/coverage/supply-chain/ fuzz/python/wasm/node in ci.yml, plus bench.yml, codeql.yml, the seven release.yml build/publish jobs, and sync-metadata.yml). The publish and release jobs authenticate to crates.io / npm / PyPI / the GitHub API with their own tokens, not persisted git credentials, so this is safe. sync-about.yml genuinely pushes the indicator-count fix-up to the PR branch, so it keeps its credential and is accepted via .github/zizmor.yml. zizmor artipacked for the repo drops to 0 (0 high, 0 medium remaining). --- .github/workflows/bench.yml | 2 ++ .github/workflows/ci.yml | 20 ++++++++++++++++++++ .github/workflows/codeql.yml | 2 ++ .github/workflows/release.yml | 25 +++++++++++++++++++++++-- .github/workflows/sync-metadata.yml | 2 ++ .github/zizmor.yml | 24 ++++++++++++++++++++++++ 6 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 .github/zizmor.yml diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index 05dd25f1..3a031f79 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -49,6 +49,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable branch, 2026-03-27 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ec1cd59a..54838916 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,6 +40,8 @@ jobs: os: [ubuntu-latest, macos-latest, windows-latest] steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Install Rust toolchain uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable branch, 2026-03-27 @@ -88,6 +90,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Set up Node id: setup_node @@ -175,6 +179,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Install Rust toolchain uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable branch, 2026-03-27 @@ -253,6 +259,8 @@ jobs: packages: "-p wickra-node" steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Install Rust ${{ matrix.toolchain }} uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable branch, 2026-03-27 @@ -276,6 +284,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Install Rust toolchain uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable branch, 2026-03-27 @@ -315,6 +325,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: cargo-deny uses: EmbarkStudios/cargo-deny-action@bb137d7af7e4fb67e5f82a49c4fce4fad40782fe # v2.0.20 @@ -331,6 +343,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Install nightly Rust uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable branch, 2026-03-27 @@ -387,6 +401,8 @@ jobs: python-version: ["3.9", "3.11", "3.12", "3.13"] steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Install Rust toolchain uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable branch, 2026-03-27 @@ -461,6 +477,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Install Rust toolchain (with wasm target) uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable branch, 2026-03-27 @@ -507,6 +525,8 @@ jobs: node-version: ["18", "20"] steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Install Rust toolchain uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable branch, 2026-03-27 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index b3454d73..9a2f803e 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -41,6 +41,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Initialize CodeQL uses: github/codeql-action/init@03e4368ac7daa2bd82b3e85262f3bf87ee112f57 # v3.36.0 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 34a34c4f..1b7bb2bd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -46,6 +46,8 @@ jobs: environment: release steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable branch, 2026-03-27 - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 continue-on-error: true # cache is an optimisation; never block on a stuck/slow restore @@ -156,6 +158,8 @@ jobs: runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Set up Python id: setup_python continue-on-error: true @@ -194,6 +198,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Sync root README into bindings/python so it ships in the sdist run: cp README.md bindings/python/README.md - uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0 @@ -244,6 +250,8 @@ jobs: runs-on: ${{ matrix.host }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Set up Node id: setup_node @@ -303,6 +311,8 @@ jobs: id-token: write steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Set up Node id: setup_node @@ -470,6 +480,8 @@ jobs: id-token: write steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Set up Node id: setup_node @@ -570,13 +582,22 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: + persist-credentials: false fetch-depth: 0 - name: Resolve target tag id: tag + # Pass the (potentially attacker-influenceable on a tag push) ref context + # through the environment instead of interpolating it into the shell + # script, so a crafted tag name cannot inject commands (zizmor: + # template-injection). + env: + EVENT_NAME: ${{ github.event_name }} + REF: ${{ github.ref }} + REF_NAME: ${{ github.ref_name }} run: | - if [ "${{ github.event_name }}" = "push" ] && [[ "${{ github.ref }}" == refs/tags/* ]]; then - tag="${{ github.ref_name }}" + if [ "$EVENT_NAME" = "push" ] && [[ "$REF" == refs/tags/* ]]; then + tag="$REF_NAME" else # workflow_dispatch / non-tag push: attach to the latest v* tag. tag=$(git tag --list 'v*' --sort=-v:refname | head -n1) diff --git a/.github/workflows/sync-metadata.yml b/.github/workflows/sync-metadata.yml index 52e08da9..bce12abe 100644 --- a/.github/workflows/sync-metadata.yml +++ b/.github/workflows/sync-metadata.yml @@ -15,6 +15,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: "3.12" diff --git a/.github/zizmor.yml b/.github/zizmor.yml new file mode 100644 index 00000000..4bf390f4 --- /dev/null +++ b/.github/zizmor.yml @@ -0,0 +1,24 @@ +# zizmor configuration — https://docs.zizmor.sh/configuration/ +# +# cache-poisoning (release.yml): +# The release pipeline restores build caches (Swatinem/rust-cache for the Rust +# compilation, actions/setup-node) as a deliberate, accepted optimisation. +# zizmor flags these under cache-poisoning because release.yml publishes +# artifacts to crates.io / PyPI / npm, so a poisoned cache could in theory +# reach a released build. Our caches are maintainer-controlled and the +# restore speedup is kept on purpose; we accept this risk rather than running +# cache-free release builds. (Six of the eight hits are actions/setup-node, +# which zizmor reports at "Low" confidence.) +# +# artipacked (sync-about.yml): +# The sync-about job checks out with persisted credentials on purpose: it +# pushes the indicator-count fix-up back to the PR head branch (git commit + +# git push), which needs the token in the runner's git config. It uploads no +# artifacts, so the persisted token is never packaged or leaked; accept it. +rules: + cache-poisoning: + ignore: + - release.yml + artipacked: + ignore: + - sync-about.yml