From 603c2528a3b7bac4ebc2f9c2c249345119078c9e Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 16:27:30 +0000 Subject: [PATCH] ci: scope workflow tokens and pin actions to commit SHAs Workflows without a `permissions:` block inherited the repository's default token, so test, validation, build and benchmark jobs running on push to main or merge_group held a token that could write releases, tags and branches, while executing third-party actions by mutable reference (dfinity/setup-dfx@main, @vN tags). - tests, validation, builds, benchmarks: `permissions: contents: read` - release: `permissions: contents: write`, the only scope that `orbit-cli release publish` needs (push tags, create releases) - pin every `uses:` in .github/, including the setup-node and setup-python composites, to a full commit SHA with a version comment - `persist-credentials: false` on every checkout that does not push; release.yaml keeps it explicitly for `git push origin --tags` Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01KLiTf9UsrvvHjNxzpyjV45 --- .github/actions/setup-node/action.yml | 6 +- .github/actions/setup-python/action.yaml | 2 +- .github/workflows/benchmarks.yaml | 9 ++- .github/workflows/builds.yaml | 9 ++- .github/workflows/release.yaml | 10 +++- .github/workflows/tests.yaml | 73 +++++++++++++++--------- .github/workflows/validation.yaml | 20 +++++-- 7 files changed, 89 insertions(+), 40 deletions(-) diff --git a/.github/actions/setup-node/action.yml b/.github/actions/setup-node/action.yml index aa1a16059..d9f1c5e37 100644 --- a/.github/actions/setup-node/action.yml +++ b/.github/actions/setup-node/action.yml @@ -5,13 +5,13 @@ runs: using: 'composite' steps: - name: 'Install Node.js' - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version-file: .nvmrc registry-url: 'https://registry.npmjs.org' - name: 'Install pnpm' id: pnpm-install - uses: pnpm/action-setup@v4 + uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4 with: run_install: false - name: 'Get pnpm store directory' @@ -20,7 +20,7 @@ runs: run: | echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV - name: 'Setup pnpm cache' - uses: actions/cache@v4 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: ${{ env.STORE_PATH }} # To evict the cache, bump the key prefix from v1 to v2 diff --git a/.github/actions/setup-python/action.yaml b/.github/actions/setup-python/action.yaml index 19c4dee25..aba61293f 100644 --- a/.github/actions/setup-python/action.yaml +++ b/.github/actions/setup-python/action.yaml @@ -5,7 +5,7 @@ runs: using: 'composite' steps: - name: 'Install Python' - uses: actions/setup-python@v4 + uses: actions/setup-python@7f4fc3e22c37d6ff65e88745f38bd3157c663f7c # v4 with: python-version: '3.10' - name: 'Install PIP' diff --git a/.github/workflows/benchmarks.yaml b/.github/workflows/benchmarks.yaml index 30c283964..dee7c9b94 100644 --- a/.github/workflows/benchmarks.yaml +++ b/.github/workflows/benchmarks.yaml @@ -26,14 +26,19 @@ env: SCCACHE_GHA_ENABLED: 'true' RUSTC_WRAPPER: 'sccache' +permissions: + contents: read + jobs: bench-station: name: 'bench-station:required' runs-on: ubuntu-latest steps: - name: 'Checkout' - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + persist-credentials: false - name: Run sccache-cache - uses: mozilla-actions/sccache-action@v0.0.8 + uses: mozilla-actions/sccache-action@65101d47ea8028ed0c98a1cdea8dd9182e9b5133 # v0.0.8 - name: 'Run Station Benchmarks' run: ./scripts/benchmark-canister.sh ./core/station/impl diff --git a/.github/workflows/builds.yaml b/.github/workflows/builds.yaml index daf38dc6a..34c8672ee 100644 --- a/.github/workflows/builds.yaml +++ b/.github/workflows/builds.yaml @@ -19,6 +19,9 @@ on: - '**/build**' - main +permissions: + contents: read + jobs: deterministic-build: name: 'deterministic-build:optional' @@ -36,9 +39,11 @@ jobs: artifact: wallet-dapp.tar.gz steps: - name: 'Checkout' - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + persist-credentials: false - name: 'Set up Docker' - uses: docker/setup-docker-action@v4 + uses: docker/setup-docker-action@e43656e248c0bd0647d3f5c195d116aacf6fcaf4 # v4 timeout-minutes: 12 - name: 'Deterministic build' run: | diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index fe610abe4..64ce42659 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -8,6 +8,11 @@ on: paths: - '.release.json' +# `orbit-cli release publish` pushes the release tags and creates the GitHub +# releases, which needs contents: write and nothing else. +permissions: + contents: write + jobs: release_projects: name: 'release' @@ -20,10 +25,13 @@ jobs: - /var/run/docker.sock:/var/run/docker.sock:ro steps: - name: 'Checkout' - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 with: fetch-depth: 0 fetch-tags: true + # Kept (the default): `git push origin --tags` authenticates with the + # token checkout leaves in .git/config. + persist-credentials: true # Helps with debugging - name: Show versions run: | diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index a21d2b15e..167999331 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -26,15 +26,20 @@ env: SCCACHE_GHA_ENABLED: 'true' RUSTC_WRAPPER: 'sccache' +permissions: + contents: read + jobs: test-rust: name: 'test-rust:required' runs-on: ubuntu-latest steps: - name: 'Checkout' - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + persist-credentials: false - name: Run sccache-cache - uses: mozilla-actions/sccache-action@v0.0.8 + uses: mozilla-actions/sccache-action@65101d47ea8028ed0c98a1cdea8dd9182e9b5133 # v0.0.8 - name: 'Test cargo crates' run: cargo test --locked --workspace --exclude integration-tests test-node: @@ -42,7 +47,9 @@ jobs: runs-on: ubuntu-latest steps: - name: 'Checkout' - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + persist-credentials: false - name: 'Setup Node' uses: ./.github/actions/setup-node - name: 'Install Dependencies' @@ -68,9 +75,11 @@ jobs: canister: station steps: - name: 'Checkout' - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + persist-credentials: false - name: Run sccache-cache - uses: mozilla-actions/sccache-action@v0.0.8 + uses: mozilla-actions/sccache-action@65101d47ea8028ed0c98a1cdea8dd9182e9b5133 # v0.0.8 - name: 'Build Package' run: ./scripts/generate-wasm.sh ${{ matrix.crate }} - name: 'Prepare artifacts' @@ -78,7 +87,7 @@ jobs: mkdir -p artifacts mv tests/integration/wasms/* artifacts - name: 'Upload Artifacts' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: integration-tests-${{ matrix.canister }} path: artifacts/${{ matrix.canister }}.wasm.gz @@ -100,7 +109,9 @@ jobs: name: 'ic-icrc1-ledger' steps: - name: 'Checkout' - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + persist-credentials: false - name: 'Download Canister' run: ./scripts/download-nns-canister-wasm.sh ${{ matrix.canister }} ${{ matrix.name }} - name: 'Prepare artifacts' @@ -108,7 +119,7 @@ jobs: mkdir -p artifacts mv tests/integration/wasms/* artifacts - name: 'Upload Artifacts' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: integration-tests-${{ matrix.canister }} path: artifacts/${{ matrix.canister }}.wasm.gz @@ -119,7 +130,7 @@ jobs: needs: [build-wasms, download-canisters] steps: - name: Merge Artifacts - uses: actions/upload-artifact/merge@v4 + uses: actions/upload-artifact/merge@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: integration-tests pattern: integration-tests-* @@ -132,11 +143,13 @@ jobs: DOWNLOAD_NNS_CANISTERS: 'false' steps: - name: 'Checkout' - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + persist-credentials: false - name: Run sccache-cache - uses: mozilla-actions/sccache-action@v0.0.8 + uses: mozilla-actions/sccache-action@65101d47ea8028ed0c98a1cdea8dd9182e9b5133 # v0.0.8 - name: 'Download Artifacts' - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: integration-tests path: tests/integration/wasms @@ -154,16 +167,18 @@ jobs: runs-on: ubuntu-latest steps: - name: 'Checkout' - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + persist-credentials: false - name: Run sccache-cache - uses: mozilla-actions/sccache-action@v0.0.8 + uses: mozilla-actions/sccache-action@65101d47ea8028ed0c98a1cdea8dd9182e9b5133 # v0.0.8 - name: 'Download Artifacts' - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: integration-tests path: wasms - name: 'Install dfx' - uses: dfinity/setup-dfx@main + uses: dfinity/setup-dfx@e50c04f104ee4285ec010f10609483cf41e4d365 # main (no tags/releases published) - name: 'Setup Node' uses: ./.github/actions/setup-node - name: 'Install Dependencies' @@ -177,7 +192,7 @@ jobs: pnpm run test:e2e - name: 'Upload e2e test results' if: failure() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: e2e-test-results path: | @@ -190,13 +205,15 @@ jobs: runs-on: ubuntu-latest steps: - name: 'Checkout' - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + persist-credentials: false - name: 'Setup Node' uses: ./.github/actions/setup-node - name: 'Run sccache-cache' - uses: mozilla-actions/sccache-action@v0.0.8 + uses: mozilla-actions/sccache-action@65101d47ea8028ed0c98a1cdea8dd9182e9b5133 # v0.0.8 - name: 'Install dfx' - uses: dfinity/setup-dfx@main + uses: dfinity/setup-dfx@e50c04f104ee4285ec010f10609483cf41e4d365 # main (no tags/releases published) - name: 'Start PocketIC' run: dfx start --clean --system-canisters --host 127.0.0.1:4943 --background - name: 'Perform local deployment' @@ -209,13 +226,15 @@ jobs: runs-on: ubuntu-latest steps: - name: 'Checkout' - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + persist-credentials: false - name: 'Setup Node' uses: ./.github/actions/setup-node - name: 'Run sccache-cache' - uses: mozilla-actions/sccache-action@v0.0.8 + uses: mozilla-actions/sccache-action@65101d47ea8028ed0c98a1cdea8dd9182e9b5133 # v0.0.8 - name: 'Install dfx' - uses: dfinity/setup-dfx@main + uses: dfinity/setup-dfx@e50c04f104ee4285ec010f10609483cf41e4d365 # main (no tags/releases published) - name: 'Start local replica' run: dfx start --clean --system-canisters --host 127.0.0.1:4943 --background - name: 'Perform local deployment' @@ -234,16 +253,18 @@ jobs: - /var/run/docker.sock:/var/run/docker.sock:ro steps: - name: 'Checkout' - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + persist-credentials: false - name: 'Setup Docker' run: | docker info # Check if Docker is running - name: 'Setup Node' uses: ./.github/actions/setup-node - name: 'Run sccache-cache' - uses: mozilla-actions/sccache-action@v0.0.8 + uses: mozilla-actions/sccache-action@65101d47ea8028ed0c98a1cdea8dd9182e9b5133 # v0.0.8 - name: 'Install dfx' - uses: dfinity/setup-dfx@main + uses: dfinity/setup-dfx@e50c04f104ee4285ec010f10609483cf41e4d365 # main (no tags/releases published) - name: 'Start local replica' run: dfx start --clean --system-canisters --host 127.0.0.1:4943 --background - name: 'Test prod deployment script' diff --git a/.github/workflows/validation.yaml b/.github/workflows/validation.yaml index 5348e682f..08b5928d4 100644 --- a/.github/workflows/validation.yaml +++ b/.github/workflows/validation.yaml @@ -23,6 +23,9 @@ env: # Use the local .curlrc CURL_HOME: . +permissions: + contents: read + jobs: validate-commits: name: 'validate-commits:required' @@ -31,9 +34,10 @@ jobs: TITLE: ${{ github.event.pull_request.title }} steps: - name: 'Checkout' - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 with: fetch-depth: 0 + persist-credentials: false # Conditional steps for commit message check - if: ${{ github.event_name != 'pull_request' }} name: 'Setup Python' @@ -64,9 +68,11 @@ jobs: runs-on: ubuntu-latest steps: - name: 'Checkout' - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + persist-credentials: false - name: 'Install dfx' - uses: dfinity/setup-dfx@main + uses: dfinity/setup-dfx@e50c04f104ee4285ec010f10609483cf41e4d365 # main (no tags/releases published) - name: 'Run dfx generate' run: | dfx generate control_panel @@ -78,7 +84,9 @@ jobs: runs-on: ubuntu-latest steps: - name: 'Checkout' - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + persist-credentials: false - name: 'Check code' run: | cargo fmt --all -- --check @@ -91,7 +99,9 @@ jobs: runs-on: ubuntu-latest steps: - name: 'Checkout' - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + persist-credentials: false - name: 'Setup Node' uses: ./.github/actions/setup-node - name: 'Install Dependencies'