From 38a494fca38ef1d1254a855dc37c7f6abb9dce8a Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Fri, 20 Mar 2026 13:29:51 -0400 Subject: [PATCH 1/9] Add GitHub Actions audit job (actionlint + zizmor) Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/test.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6f9fd54..30f1ceb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -49,6 +49,22 @@ jobs: with: version: v2.9.0 + lint-actions: + name: GitHub Actions audit + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + persist-credentials: false + + - name: Run actionlint + uses: rhysd/actionlint@v1.7.11 + + - name: Run zizmor + uses: zizmorcore/zizmor-action@v0.5.2 + with: + advanced-security: false + security: name: Govulncheck runs-on: ubuntu-latest From 27df00bab42b4e0c26eeeec3793bccb51649708f Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Fri, 20 Mar 2026 13:30:01 -0400 Subject: [PATCH 2/9] Update dependabot github-actions cooldown to 7 days Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/dependabot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 496138a..1f30a7c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -39,6 +39,6 @@ updates: patterns: - "*" cooldown: - default-days: 2 + default-days: 7 commit-message: prefix: "ci" From a1f9fc23a06026663510876ffbe569f80073583c Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Fri, 20 Mar 2026 13:30:26 -0400 Subject: [PATCH 3/9] Add lint-actions target for local workflow linting Runs actionlint and zizmor locally, included in check-all. Co-Authored-By: Claude Opus 4.6 (1M context) --- Makefile | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 12f1d28..22b76b2 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ .DEFAULT_GOAL := check -.PHONY: check test test-race vet lint fmt fmt-check bench check-all \ +.PHONY: check test test-race vet lint lint-actions fmt fmt-check bench check-all \ tidy tidy-check replace-check vuln secrets security release-check release # Default target: fast checks for inner-loop dev. @@ -18,6 +18,10 @@ vet: lint: golangci-lint run +lint-actions: + actionlint + zizmor . + fmt: gofmt -w . @@ -74,7 +78,7 @@ secrets: security: lint vuln secrets # Full suite: everything CI runs. -check-all: fmt-check vet lint test-race bench tidy-check +check-all: fmt-check vet lint lint-actions test-race bench tidy-check # Full pre-flight for release release-check: check-all replace-check vuln secrets From 484e8cfe7aaf9efb0632216b18c767dd32cd4b32 Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Fri, 20 Mar 2026 13:30:46 -0400 Subject: [PATCH 4/9] Pin all GitHub Actions to SHA hashes with pinact Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/dependabot-auto-merge.yml | 2 +- .github/workflows/release.yml | 12 ++++---- .github/workflows/scorecard.yml | 4 +-- .github/workflows/security.yml | 26 ++++++++-------- .github/workflows/test.yml | 34 ++++++++++----------- 5 files changed, 39 insertions(+), 39 deletions(-) diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml index 08fb61a..08ecadc 100644 --- a/.github/workflows/dependabot-auto-merge.yml +++ b/.github/workflows/dependabot-auto-merge.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest if: github.actor == 'dependabot[bot]' steps: - - uses: dependabot/fetch-metadata@21025c705c08248db411dc16f3619e6b5f9ea21a # v2 + - uses: dependabot/fetch-metadata@21025c705c08248db411dc16f3619e6b5f9ea21a # v2.5.0 id: metadata with: github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ba16a45..542a174 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,16 +26,16 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 + - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 with: go-version-file: go.mod - name: Install golangci-lint - uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9 + uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0 with: version: v2.9.0 install-only: true @@ -84,7 +84,7 @@ jobs: contents: write models: read steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 @@ -202,7 +202,7 @@ jobs: env: HAS_SKILLS_KEY: ${{ secrets.SKILLS_APP_PRIVATE_KEY && 'true' || '' }} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Check prerequisites id: check @@ -220,7 +220,7 @@ jobs: - name: Generate token if: steps.check.outputs.ready == 'true' id: skills-token - uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2 + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 with: app-id: ${{ vars.SKILLS_APP_ID }} private-key: ${{ secrets.SKILLS_APP_PRIVATE_KEY }} diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index fc5a0dc..151b868 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -17,7 +17,7 @@ jobs: id-token: write contents: read steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -33,7 +33,7 @@ jobs: path: results.sarif retention-days: 5 - - uses: github/codeql-action/upload-sarif@0d579ffd059c29b07949a3cce3983f0780820c98 # v4 + - uses: github/codeql-action/upload-sarif@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6 continue-on-error: true with: sarif_file: results.sarif diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index 35c65bd..37c4970 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -22,7 +22,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 @@ -41,7 +41,7 @@ jobs: contents: read security-events: write steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Run Trivy vulnerability scanner (filesystem) uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # 0.35.0 @@ -56,7 +56,7 @@ jobs: version: 'v0.69.3' - name: Upload Trivy scan results to GitHub Security tab - uses: github/codeql-action/upload-sarif@0d579ffd059c29b07949a3cce3983f0780820c98 # v4 + uses: github/codeql-action/upload-sarif@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6 if: always() continue-on-error: true # Requires GitHub Advanced Security with: @@ -69,9 +69,9 @@ jobs: contents: read security-events: write steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 + - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 with: go-version-file: go.mod @@ -82,7 +82,7 @@ jobs: run: gosec -no-fail -fmt sarif -out gosec-results.sarif ./... - name: Upload gosec scan results to GitHub Security tab - uses: github/codeql-action/upload-sarif@0d579ffd059c29b07949a3cce3983f0780820c98 # v4 + uses: github/codeql-action/upload-sarif@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6 if: always() continue-on-error: true # Requires GitHub Advanced Security with: @@ -96,9 +96,9 @@ jobs: contents: read pull-requests: read steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: actions/dependency-review-action@2031cfc080254a8a887f58cffee85186f0e49e48 # v4 + - uses: actions/dependency-review-action@2031cfc080254a8a887f58cffee85186f0e49e48 # v4.9.0 continue-on-error: true # Requires GitHub Advanced Security codeql: @@ -108,14 +108,14 @@ jobs: contents: read security-events: write steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 + - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 with: go-version-file: go.mod - name: Initialize CodeQL - uses: github/codeql-action/init@0d579ffd059c29b07949a3cce3983f0780820c98 # v4 + uses: github/codeql-action/init@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6 with: languages: go build-mode: manual @@ -127,14 +127,14 @@ jobs: run: go build ./... - name: Perform CodeQL analysis - uses: github/codeql-action/analyze@0d579ffd059c29b07949a3cce3983f0780820c98 # v4 + uses: github/codeql-action/analyze@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6 with: category: codeql-go upload: never output: sarif-results - name: Upload SARIF to GitHub Security tab - uses: github/codeql-action/upload-sarif@0d579ffd059c29b07949a3cce3983f0780820c98 # v4 + uses: github/codeql-action/upload-sarif@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6 continue-on-error: true # Requires GitHub Advanced Security with: sarif_file: sarif-results diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 30f1ceb..f598567 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,9 +15,9 @@ jobs: name: Test runs-on: ubuntu-latest steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 + - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 with: go-version-file: go.mod @@ -39,13 +39,13 @@ jobs: name: Lint runs-on: ubuntu-latest steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 + - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 with: go-version-file: go.mod - - uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9 + - uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0 with: version: v2.9.0 @@ -53,15 +53,15 @@ jobs: name: GitHub Actions audit runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Run actionlint - uses: rhysd/actionlint@v1.7.11 + uses: rhysd/actionlint@393031adb9afb225ee52ae2ccd7a5af5525e03e8 # v1.7.11 - name: Run zizmor - uses: zizmorcore/zizmor-action@v0.5.2 + uses: zizmorcore/zizmor-action@71321a20a9ded102f6e9ce5718a2fcec2c4f70d8 # v0.5.2 with: advanced-security: false @@ -69,9 +69,9 @@ jobs: name: Govulncheck runs-on: ubuntu-latest steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 + - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 with: go-version-file: go.mod @@ -85,9 +85,9 @@ jobs: name: Test (race detector) runs-on: ubuntu-latest steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 + - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 with: go-version-file: go.mod @@ -99,13 +99,13 @@ jobs: runs-on: ubuntu-latest if: github.event_name == 'push' && github.ref == 'refs/heads/main' steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 2 - name: Check for benchmark-relevant changes id: filter - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3 + uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 with: filters: | bench: @@ -115,7 +115,7 @@ jobs: - name: Set up Go if: steps.filter.outputs.bench == 'true' - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 with: go-version-file: go.mod @@ -127,7 +127,7 @@ jobs: - name: Download previous benchmark baseline if: steps.filter.outputs.bench == 'true' - uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5 + uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 with: path: benchmarks-baseline.txt key: benchmarks-baseline-${{ github.sha }} @@ -161,7 +161,7 @@ jobs: - name: Cache benchmark baseline if: steps.filter.outputs.bench == 'true' - uses: actions/cache/save@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5 + uses: actions/cache/save@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 with: path: benchmarks-baseline.txt key: benchmarks-baseline-${{ github.sha }} From c3405026a0042b049af5bcf982b3ec8840c2b3dc Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Fri, 20 Mar 2026 13:34:13 -0400 Subject: [PATCH 5/9] Fix high-severity zizmor findings - bot-conditions: apply dual check (actor + user.login) for dependabot - dangerous-triggers: suppress pull_request_target with justification - excessive-permissions: move to permissions: {} with per-job scoping - cache-poisoning: suppress with justification (branch-isolated caches) Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/ai-labeler.yml | 2 +- .github/workflows/dependabot-auto-merge.yml | 2 +- .github/workflows/labeler.yml | 2 +- .github/workflows/release.yml | 12 ++++++------ .github/workflows/sensitive-change-gate.yml | 2 +- seed/.github/workflows/ai-labeler.yml | 18 ++++++++++++------ .../workflows/dependabot-auto-merge.yml | 2 +- seed/.github/workflows/labeler.yml | 2 +- seed/.github/workflows/release.yml | 18 ++++++++---------- 9 files changed, 32 insertions(+), 28 deletions(-) diff --git a/.github/workflows/ai-labeler.yml b/.github/workflows/ai-labeler.yml index f51819b..0be00df 100644 --- a/.github/workflows/ai-labeler.yml +++ b/.github/workflows/ai-labeler.yml @@ -1,7 +1,7 @@ name: Classify PR on: - pull_request_target: + pull_request_target: # zizmor: ignore[dangerous-triggers] -- required for write access to PRs from forks; workflow only runs trusted reusable workflows, no PR code is checked out or executed types: [opened, synchronize, reopened] concurrency: diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml index 08ecadc..3f4a664 100644 --- a/.github/workflows/dependabot-auto-merge.yml +++ b/.github/workflows/dependabot-auto-merge.yml @@ -10,7 +10,7 @@ jobs: auto-merge: name: Auto-merge runs-on: ubuntu-latest - if: github.actor == 'dependabot[bot]' + if: github.actor == 'dependabot[bot]' && github.event.pull_request.user.login == 'dependabot[bot]' # zizmor: ignore[bot-conditions] -- dual check: actor validates current trigger, user.login validates PR origin steps: - uses: dependabot/fetch-metadata@21025c705c08248db411dc16f3619e6b5f9ea21a # v2.5.0 id: metadata diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 955c4d8..bcdab76 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -1,7 +1,7 @@ name: Label PRs on: - pull_request_target: + pull_request_target: # zizmor: ignore[dangerous-triggers] -- required for write access to PRs from forks; workflow only runs trusted actions, no PR code is checked out or executed types: [opened, synchronize, reopened] permissions: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 542a174..5d0523e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,16 +9,16 @@ concurrency: group: release-${{ github.ref }} cancel-in-progress: false -permissions: - contents: write - security-events: write - pull-requests: read - models: read +permissions: {} jobs: security: name: Security uses: ./.github/workflows/security.yml + permissions: + contents: read + security-events: write + pull-requests: read test: name: Test gate @@ -30,7 +30,7 @@ jobs: with: fetch-depth: 0 - - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 + - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 # zizmor: ignore[cache-poisoning] -- cache is branch-isolated; fork PRs cannot write to this cache with: go-version-file: go.mod diff --git a/.github/workflows/sensitive-change-gate.yml b/.github/workflows/sensitive-change-gate.yml index 2ca5d7e..d07ff29 100644 --- a/.github/workflows/sensitive-change-gate.yml +++ b/.github/workflows/sensitive-change-gate.yml @@ -1,7 +1,7 @@ name: Sensitive Change Gate on: - pull_request_target: + pull_request_target: # zizmor: ignore[dangerous-triggers] -- required for write access to PRs from forks; workflow only runs trusted reusable workflow, no PR code is checked out or executed types: [opened, synchronize, reopened] permissions: diff --git a/seed/.github/workflows/ai-labeler.yml b/seed/.github/workflows/ai-labeler.yml index c4813f4..73dab86 100644 --- a/seed/.github/workflows/ai-labeler.yml +++ b/seed/.github/workflows/ai-labeler.yml @@ -1,22 +1,23 @@ name: Classify PR on: - pull_request_target: + pull_request_target: # zizmor: ignore[dangerous-triggers] -- required for write access to PRs from forks; workflow only checks out base branch and uses gh CLI for PR data, no PR code is executed types: [opened, synchronize, reopened] concurrency: group: classify-pr-${{ github.event.pull_request.number }} cancel-in-progress: true -permissions: - contents: read - issues: write - models: read - pull-requests: write +permissions: {} jobs: classify: runs-on: ubuntu-latest + permissions: + contents: read + issues: write + models: read + pull-requests: write steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 @@ -100,6 +101,11 @@ jobs: breaking: runs-on: ubuntu-latest + permissions: + contents: read + issues: write + models: read + pull-requests: write steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 diff --git a/seed/.github/workflows/dependabot-auto-merge.yml b/seed/.github/workflows/dependabot-auto-merge.yml index b285a85..500abbc 100644 --- a/seed/.github/workflows/dependabot-auto-merge.yml +++ b/seed/.github/workflows/dependabot-auto-merge.yml @@ -9,7 +9,7 @@ permissions: jobs: auto-merge: runs-on: ubuntu-latest - if: github.actor == 'dependabot[bot]' + if: github.actor == 'dependabot[bot]' && github.event.pull_request.user.login == 'dependabot[bot]' # zizmor: ignore[bot-conditions] -- dual check: actor validates current trigger, user.login validates PR origin steps: - name: Fetch Dependabot metadata id: metadata diff --git a/seed/.github/workflows/labeler.yml b/seed/.github/workflows/labeler.yml index 955c4d8..bcdab76 100644 --- a/seed/.github/workflows/labeler.yml +++ b/seed/.github/workflows/labeler.yml @@ -1,7 +1,7 @@ name: Label PRs on: - pull_request_target: + pull_request_target: # zizmor: ignore[dangerous-triggers] -- required for write access to PRs from forks; workflow only runs trusted actions, no PR code is checked out or executed types: [opened, synchronize, reopened] permissions: diff --git a/seed/.github/workflows/release.yml b/seed/.github/workflows/release.yml index 614b491..02824e2 100644 --- a/seed/.github/workflows/release.yml +++ b/seed/.github/workflows/release.yml @@ -9,13 +9,7 @@ concurrency: group: release-${{ github.ref }} cancel-in-progress: false -permissions: - contents: write - id-token: write - attestations: write - security-events: write - pull-requests: read - models: read +permissions: {} jobs: security: @@ -23,6 +17,10 @@ jobs: uses: ./.github/workflows/security.yml secrets: RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} + permissions: + contents: read + security-events: write + pull-requests: read test: name: Test before release @@ -49,7 +47,7 @@ jobs: # repositories: ,homebrew-tap - name: Set up Go - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 # zizmor: ignore[cache-poisoning] -- cache is branch-isolated; fork PRs cannot write to this cache with: go-version-file: 'go.mod' @@ -67,7 +65,7 @@ jobs: - name: Cache BATS id: cache-bats - uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5 + uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5 # zizmor: ignore[cache-poisoning] -- cache key is a static version string, not attacker-controllable with: path: /usr/local/libexec/bats-core key: bats-1.11.0 @@ -151,7 +149,7 @@ jobs: # repositories: ,homebrew-tap - name: Set up Go - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 # zizmor: ignore[cache-poisoning] -- cache is branch-isolated; fork PRs cannot write to this cache with: go-version-file: 'go.mod' From c15fd9d80b58164f3e343c0ee04d287bf39f1e2d Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Fri, 20 Mar 2026 13:40:08 -0400 Subject: [PATCH 6/9] Fix medium-severity zizmor findings in seed workflows - excessive-permissions: move workflow-level permissions to job-level in security.yml - secrets-outside-env: suppress with justification in release.yml, security.yml, test.yml - Update version comments to match exact pinned versions Co-Authored-By: Claude Opus 4.6 (1M context) --- seed/.github/workflows/release.yml | 16 ++++++------ seed/.github/workflows/scorecard.yml | 2 +- seed/.github/workflows/security.yml | 38 ++++++++++++++++++---------- seed/.github/workflows/test.yml | 34 ++++++++++++------------- 4 files changed, 50 insertions(+), 40 deletions(-) diff --git a/seed/.github/workflows/release.yml b/seed/.github/workflows/release.yml index 02824e2..6598095 100644 --- a/seed/.github/workflows/release.yml +++ b/seed/.github/workflows/release.yml @@ -39,10 +39,10 @@ jobs: - name: Generate token for private module access if: vars.RELEASE_CLIENT_ID != '' id: app-token - uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2 + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 with: app-id: ${{ vars.RELEASE_CLIENT_ID }} - private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} + private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} # zizmor: ignore[secrets-outside-env] -- release workflow needs private module access; environment protection would block tag-triggered releases owner: basecamp # repositories: ,homebrew-tap @@ -65,7 +65,7 @@ jobs: - name: Cache BATS id: cache-bats - uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5 # zizmor: ignore[cache-poisoning] -- cache key is a static version string, not attacker-controllable + uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 # zizmor: ignore[cache-poisoning] -- cache key is a static version string, not attacker-controllable with: path: /usr/local/libexec/bats-core key: bats-1.11.0 @@ -141,10 +141,10 @@ jobs: - name: Generate token for private module and tap access if: vars.RELEASE_CLIENT_ID != '' id: sdk-token - uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2 + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 with: app-id: ${{ vars.RELEASE_CLIENT_ID }} - private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} + private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} # zizmor: ignore[secrets-outside-env] -- release workflow needs private module access; environment protection would block tag-triggered releases owner: basecamp # repositories: ,homebrew-tap @@ -195,7 +195,7 @@ jobs: uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0 - name: Install Syft - uses: anchore/sbom-action/download-syft@17ae1740179002c89186b61233e0f892c3118b11 # v0 + uses: anchore/sbom-action/download-syft@17ae1740179002c89186b61233e0f892c3118b11 # v0.23.0 # Configure vars.ENABLE_AI_CHANGELOG=true plus models:read permission to enable AI changelog - name: Build changelog context @@ -363,10 +363,10 @@ jobs: - name: Generate token for skills repo if: steps.check-skills.outputs.found == 'true' id: skills-token - uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2 + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 with: app-id: ${{ vars.SKILLS_APP_ID }} - private-key: ${{ secrets.SKILLS_APP_PRIVATE_KEY }} + private-key: ${{ secrets.SKILLS_APP_PRIVATE_KEY }} # zizmor: ignore[secrets-outside-env] -- skills sync needs app credentials; environment protection would block tag-triggered releases owner: basecamp repositories: skills diff --git a/seed/.github/workflows/scorecard.yml b/seed/.github/workflows/scorecard.yml index 6f0223e..1537d9a 100644 --- a/seed/.github/workflows/scorecard.yml +++ b/seed/.github/workflows/scorecard.yml @@ -33,7 +33,7 @@ jobs: path: results.sarif retention-days: 5 - - uses: github/codeql-action/upload-sarif@c793b717bc78562f491db7b0e93a3a178b099162 # v4 + - uses: github/codeql-action/upload-sarif@c793b717bc78562f491db7b0e93a3a178b099162 # v4.32.5 continue-on-error: true with: sarif_file: results.sarif diff --git a/seed/.github/workflows/security.yml b/seed/.github/workflows/security.yml index a5dc882..90f12c6 100644 --- a/seed/.github/workflows/security.yml +++ b/seed/.github/workflows/security.yml @@ -14,15 +14,14 @@ on: required: false workflow_dispatch: -permissions: - contents: read - security-events: write - pull-requests: read +permissions: {} jobs: secrets: name: Secret Scanning runs-on: ubuntu-latest + permissions: + contents: read steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: @@ -39,6 +38,9 @@ jobs: trivy: name: Trivy Security Scan runs-on: ubuntu-latest + permissions: + contents: read + security-events: write steps: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 @@ -56,7 +58,7 @@ jobs: version: 'v0.69.3' - name: Upload Trivy scan results to GitHub Security tab - uses: github/codeql-action/upload-sarif@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4 + uses: github/codeql-action/upload-sarif@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4 if: always() continue-on-error: true # Requires GitHub Advanced Security with: @@ -65,6 +67,9 @@ jobs: gosec: name: Go Security Checker runs-on: ubuntu-latest + permissions: + contents: read + security-events: write steps: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 @@ -73,10 +78,10 @@ jobs: - name: Generate token for private module access if: vars.RELEASE_CLIENT_ID != '' id: app-token - uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2 + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 with: app-id: ${{ vars.RELEASE_CLIENT_ID }} - private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} + private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} # zizmor: ignore[secrets-outside-env] -- private module access needed in PR CI runs; environment protection would block CI owner: basecamp - name: Set up Go @@ -96,7 +101,7 @@ jobs: gosec -no-fail -fmt sarif -out gosec-results.sarif ./... - name: Upload gosec scan results to GitHub Security tab - uses: github/codeql-action/upload-sarif@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4 + uses: github/codeql-action/upload-sarif@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4 if: always() continue-on-error: true # Requires GitHub Advanced Security with: @@ -107,15 +112,20 @@ jobs: dependency-review: name: Dependency Review runs-on: ubuntu-latest + permissions: + contents: read if: github.event_name == 'pull_request' continue-on-error: true # Requires GitHub Advanced Security (not available on all plans) steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - uses: actions/dependency-review-action@05fe4576374b728f0c523d6a13d64c25081e0803 # v4 + - uses: actions/dependency-review-action@05fe4576374b728f0c523d6a13d64c25081e0803 # v4.8.3 codeql: name: CodeQL Analysis runs-on: ubuntu-latest + permissions: + contents: read + security-events: write steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 @@ -123,10 +133,10 @@ jobs: - name: Generate token for private module access if: vars.RELEASE_CLIENT_ID != '' id: app-token - uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2 + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 with: app-id: ${{ vars.RELEASE_CLIENT_ID }} - private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} + private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} # zizmor: ignore[secrets-outside-env] -- private module access needed in PR CI runs; environment protection would block CI owner: basecamp - name: Set up Go @@ -141,7 +151,7 @@ jobs: run: git config --global url."https://x-access-token:${TOKEN}@github.com/".insteadOf "https://github.com/" - name: Initialize CodeQL - uses: github/codeql-action/init@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4 + uses: github/codeql-action/init@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4 with: languages: go build-mode: manual @@ -153,14 +163,14 @@ jobs: run: go build ./... - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4 + uses: github/codeql-action/analyze@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4 with: category: codeql-go upload: never output: sarif-results - name: Upload SARIF to GitHub Security tab - uses: github/codeql-action/upload-sarif@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4 + uses: github/codeql-action/upload-sarif@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4 continue-on-error: true # Requires GitHub Advanced Security with: sarif_file: sarif-results diff --git a/seed/.github/workflows/test.yml b/seed/.github/workflows/test.yml index 3c88e8b..70c2c9c 100644 --- a/seed/.github/workflows/test.yml +++ b/seed/.github/workflows/test.yml @@ -24,10 +24,10 @@ jobs: - name: Generate token for private module access if: vars.RELEASE_CLIENT_ID != '' id: app-token - uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2 + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 with: app-id: ${{ vars.RELEASE_CLIENT_ID }} - private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} + private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} # zizmor: ignore[secrets-outside-env] -- private module access needed in PR CI runs; environment protection would block CI owner: basecamp # repositories: @@ -67,10 +67,10 @@ jobs: - name: Generate token for private module access if: vars.RELEASE_CLIENT_ID != '' id: app-token - uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2 + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 with: app-id: ${{ vars.RELEASE_CLIENT_ID }} - private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} + private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} # zizmor: ignore[secrets-outside-env] -- private module access needed in PR CI runs; environment protection would block CI owner: basecamp - name: Set up Go @@ -98,10 +98,10 @@ jobs: - name: Generate token for private module access if: vars.RELEASE_CLIENT_ID != '' id: app-token - uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2 + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 with: app-id: ${{ vars.RELEASE_CLIENT_ID }} - private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} + private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} # zizmor: ignore[secrets-outside-env] -- private module access needed in PR CI runs; environment protection would block CI owner: basecamp - name: Set up Go @@ -134,10 +134,10 @@ jobs: - name: Generate token for private module access if: vars.RELEASE_CLIENT_ID != '' id: app-token - uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2 + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 with: app-id: ${{ vars.RELEASE_CLIENT_ID }} - private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} + private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} # zizmor: ignore[secrets-outside-env] -- private module access needed in PR CI runs; environment protection would block CI owner: basecamp - name: Set up Go @@ -166,10 +166,10 @@ jobs: - name: Generate token for private module access if: vars.RELEASE_CLIENT_ID != '' id: app-token - uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2 + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 with: app-id: ${{ vars.RELEASE_CLIENT_ID }} - private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} + private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} # zizmor: ignore[secrets-outside-env] -- private module access needed in PR CI runs; environment protection would block CI owner: basecamp - name: Set up Go @@ -185,7 +185,7 @@ jobs: - name: Cache BATS id: cache-bats - uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5 + uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 with: path: /usr/local/libexec/bats-core key: bats-1.11.0 @@ -213,10 +213,10 @@ jobs: - name: Generate token for private module access if: vars.RELEASE_CLIENT_ID != '' id: app-token - uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2 + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 with: app-id: ${{ vars.RELEASE_CLIENT_ID }} - private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} + private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} # zizmor: ignore[secrets-outside-env] -- private module access needed in PR CI runs; environment protection would block CI owner: basecamp - name: Set up Go @@ -268,10 +268,10 @@ jobs: - name: Generate token for private module access if: vars.RELEASE_CLIENT_ID != '' id: app-token - uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2 + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 with: app-id: ${{ vars.RELEASE_CLIENT_ID }} - private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} + private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} # zizmor: ignore[secrets-outside-env] -- private module access needed in PR CI runs; environment protection would block CI owner: basecamp - name: Check for benchmark-relevant changes @@ -302,7 +302,7 @@ jobs: - name: Download previous benchmark baseline if: steps.filter.outputs.bench == 'true' - uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5 + uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 with: path: benchmarks-baseline.txt key: benchmarks-baseline-${{ github.sha }} @@ -336,7 +336,7 @@ jobs: - name: Cache benchmark baseline if: steps.filter.outputs.bench == 'true' - uses: actions/cache/save@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5 + uses: actions/cache/save@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 with: path: benchmarks-baseline.txt key: benchmarks-baseline-${{ github.sha }} From ab22fe8636fde863db538efa792b29d548a52a2e Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Fri, 20 Mar 2026 13:41:26 -0400 Subject: [PATCH 7/9] Fix low-severity zizmor findings - artipacked: add persist-credentials: false to all checkout steps - dependabot-cooldown: increase default-days to 7 where insufficient Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/dependabot.yml | 2 +- .github/workflows/release.yml | 4 ++++ .github/workflows/security.yml | 9 +++++++++ .github/workflows/test.yml | 9 +++++++++ seed/.github/dependabot.yml | 4 ++-- seed/.github/workflows/ai-labeler.yml | 4 ++++ seed/.github/workflows/release.yml | 4 ++++ seed/.github/workflows/security.yml | 9 +++++++++ seed/.github/workflows/test.yml | 12 ++++++++++++ 9 files changed, 54 insertions(+), 3 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 1f30a7c..318c922 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -18,7 +18,7 @@ updates: patterns: - "*" cooldown: - default-days: 2 + default-days: 7 semver-major-days: 7 semver-minor-days: 3 semver-patch-days: 2 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5d0523e..fd52716 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,6 +29,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 + persist-credentials: false - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 # zizmor: ignore[cache-poisoning] -- cache is branch-isolated; fork PRs cannot write to this cache with: @@ -87,6 +88,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 + persist-credentials: false - name: Verify tag is on main env: @@ -203,6 +205,8 @@ jobs: HAS_SKILLS_KEY: ${{ secrets.SKILLS_APP_PRIVATE_KEY && 'true' || '' }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Check prerequisites id: check diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index 37c4970..bc18f35 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -25,6 +25,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 + persist-credentials: false - name: Install gitleaks run: | @@ -42,6 +43,8 @@ jobs: security-events: write steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Run Trivy vulnerability scanner (filesystem) uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # 0.35.0 @@ -70,6 +73,8 @@ jobs: security-events: write steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 with: @@ -97,6 +102,8 @@ jobs: pull-requests: read steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - uses: actions/dependency-review-action@2031cfc080254a8a887f58cffee85186f0e49e48 # v4.9.0 continue-on-error: true # Requires GitHub Advanced Security @@ -109,6 +116,8 @@ jobs: security-events: write steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 with: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f598567..384b06e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,6 +16,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 with: @@ -40,6 +42,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 with: @@ -70,6 +74,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 with: @@ -86,6 +92,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 with: @@ -102,6 +110,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 2 + persist-credentials: false - name: Check for benchmark-relevant changes id: filter diff --git a/seed/.github/dependabot.yml b/seed/.github/dependabot.yml index f04f54a..f1ae092 100644 --- a/seed/.github/dependabot.yml +++ b/seed/.github/dependabot.yml @@ -17,7 +17,7 @@ updates: patterns: - "*" cooldown: - default-days: 2 + default-days: 7 semver-major-days: 7 semver-minor-days: 3 semver-patch-days: 2 @@ -38,6 +38,6 @@ updates: patterns: - "*" cooldown: - default-days: 2 + default-days: 7 commit-message: prefix: "ci" diff --git a/seed/.github/workflows/ai-labeler.yml b/seed/.github/workflows/ai-labeler.yml index 73dab86..155eda2 100644 --- a/seed/.github/workflows/ai-labeler.yml +++ b/seed/.github/workflows/ai-labeler.yml @@ -20,6 +20,8 @@ jobs: pull-requests: write steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false - name: Build prompt env: @@ -108,6 +110,8 @@ jobs: pull-requests: write steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false - name: Build prompt id: cmd-diff diff --git a/seed/.github/workflows/release.yml b/seed/.github/workflows/release.yml index 6598095..28cc52f 100644 --- a/seed/.github/workflows/release.yml +++ b/seed/.github/workflows/release.yml @@ -34,6 +34,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: fetch-depth: 0 + persist-credentials: false # Configure vars.RELEASE_CLIENT_ID and secrets.RELEASE_APP_PRIVATE_KEY to enable private module access - name: Generate token for private module access @@ -136,6 +137,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: fetch-depth: 0 + persist-credentials: false # Configure vars.RELEASE_CLIENT_ID and secrets.RELEASE_APP_PRIVATE_KEY for private modules + Homebrew tap - name: Generate token for private module and tap access @@ -349,6 +351,8 @@ jobs: contents: read steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false - name: Check for skill files id: check-skills diff --git a/seed/.github/workflows/security.yml b/seed/.github/workflows/security.yml index 90f12c6..48c226c 100644 --- a/seed/.github/workflows/security.yml +++ b/seed/.github/workflows/security.yml @@ -26,6 +26,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: fetch-depth: 0 + persist-credentials: false - name: Install gitleaks run: | @@ -44,6 +45,8 @@ jobs: steps: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false - name: Run Trivy vulnerability scanner (filesystem) uses: aquasecurity/trivy-action@e368e328979b113139d6f9068e03accaed98a518 # 0.34.1 @@ -73,6 +76,8 @@ jobs: steps: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false # Configure vars.RELEASE_CLIENT_ID and secrets.RELEASE_APP_PRIVATE_KEY to enable private module access - name: Generate token for private module access @@ -118,6 +123,8 @@ jobs: continue-on-error: true # Requires GitHub Advanced Security (not available on all plans) steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false - uses: actions/dependency-review-action@05fe4576374b728f0c523d6a13d64c25081e0803 # v4.8.3 codeql: @@ -128,6 +135,8 @@ jobs: security-events: write steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false # Configure vars.RELEASE_CLIENT_ID and secrets.RELEASE_APP_PRIVATE_KEY to enable private module access - name: Generate token for private module access diff --git a/seed/.github/workflows/test.yml b/seed/.github/workflows/test.yml index 70c2c9c..87cf498 100644 --- a/seed/.github/workflows/test.yml +++ b/seed/.github/workflows/test.yml @@ -19,6 +19,8 @@ jobs: # Uncomment for private module access: GOPRIVATE: github.com/basecamp/ steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false # Configure vars.RELEASE_CLIENT_ID and secrets.RELEASE_APP_PRIVATE_KEY to enable private module access - name: Generate token for private module access @@ -62,6 +64,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false # Uncomment for private module access: GOPRIVATE: github.com/basecamp/ - name: Generate token for private module access @@ -94,6 +98,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false - name: Generate token for private module access if: vars.RELEASE_CLIENT_ID != '' @@ -130,6 +136,8 @@ jobs: # Uncomment for private module access: GOPRIVATE: github.com/basecamp/ steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false - name: Generate token for private module access if: vars.RELEASE_CLIENT_ID != '' @@ -162,6 +170,8 @@ jobs: # Uncomment for private module access: GOPRIVATE: github.com/basecamp/ steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false - name: Generate token for private module access if: vars.RELEASE_CLIENT_ID != '' @@ -209,6 +219,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: fetch-depth: 0 + persist-credentials: false - name: Generate token for private module access if: vars.RELEASE_CLIENT_ID != '' @@ -264,6 +275,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: fetch-depth: 2 + persist-credentials: false - name: Generate token for private module access if: vars.RELEASE_CLIENT_ID != '' From 7f0c14d859442c981485f55edc04d6a5754af1e1 Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Fri, 20 Mar 2026 13:43:39 -0400 Subject: [PATCH 8/9] Move all permissions to job-level across all workflows Replace workflow-level permissions with permissions: {} and add explicit per-job permissions. This ensures every job starts with zero permissions and fails safe if permissions are omitted. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/ai-labeler.yml | 6 +----- .github/workflows/dependabot-auto-merge.yml | 7 ++++--- .github/workflows/direct-push-alert.yml | 4 +--- .github/workflows/labeler.yml | 7 ++++--- .github/workflows/scorecard.yml | 2 +- .github/workflows/security.yml | 5 +---- .github/workflows/sensitive-change-gate.yml | 4 +--- .github/workflows/test.yml | 15 +++++++++++++-- .../.github/workflows/dependabot-auto-merge.yml | 7 ++++--- seed/.github/workflows/labeler.yml | 7 ++++--- seed/.github/workflows/scorecard.yml | 2 +- seed/.github/workflows/test.yml | 17 +++++++++++++++-- 12 files changed, 50 insertions(+), 33 deletions(-) diff --git a/.github/workflows/ai-labeler.yml b/.github/workflows/ai-labeler.yml index 0be00df..407f9a7 100644 --- a/.github/workflows/ai-labeler.yml +++ b/.github/workflows/ai-labeler.yml @@ -8,11 +8,7 @@ concurrency: group: classify-pr-${{ github.event.pull_request.number }} cancel-in-progress: true -permissions: - contents: read - issues: write - models: read - pull-requests: write +permissions: {} jobs: classify: diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml index 3f4a664..cb262db 100644 --- a/.github/workflows/dependabot-auto-merge.yml +++ b/.github/workflows/dependabot-auto-merge.yml @@ -2,14 +2,15 @@ name: Dependabot auto-merge on: pull_request -permissions: - contents: write - pull-requests: write +permissions: {} jobs: auto-merge: name: Auto-merge runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write if: github.actor == 'dependabot[bot]' && github.event.pull_request.user.login == 'dependabot[bot]' # zizmor: ignore[bot-conditions] -- dual check: actor validates current trigger, user.login validates PR origin steps: - uses: dependabot/fetch-metadata@21025c705c08248db411dc16f3619e6b5f9ea21a # v2.5.0 diff --git a/.github/workflows/direct-push-alert.yml b/.github/workflows/direct-push-alert.yml index 8e71119..943082f 100644 --- a/.github/workflows/direct-push-alert.yml +++ b/.github/workflows/direct-push-alert.yml @@ -4,9 +4,7 @@ on: push: branches: [main] -permissions: - contents: read - issues: write +permissions: {} jobs: alert: diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index bcdab76..075d37f 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -4,13 +4,14 @@ on: pull_request_target: # zizmor: ignore[dangerous-triggers] -- required for write access to PRs from forks; workflow only runs trusted actions, no PR code is checked out or executed types: [opened, synchronize, reopened] -permissions: - contents: read - pull-requests: write +permissions: {} jobs: label: runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write steps: - uses: actions/labeler@634933edcd8ababfe52f92936142cc22ac488b1b # v6.0.1 with: diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 151b868..f44e488 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -7,7 +7,7 @@ on: - cron: '30 1 * * 6' workflow_dispatch: -permissions: read-all +permissions: {} jobs: analysis: diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index bc18f35..025e65b 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -10,10 +10,7 @@ on: workflow_call: workflow_dispatch: -permissions: - contents: read - security-events: write - pull-requests: read +permissions: {} jobs: secrets: diff --git a/.github/workflows/sensitive-change-gate.yml b/.github/workflows/sensitive-change-gate.yml index d07ff29..eae66cd 100644 --- a/.github/workflows/sensitive-change-gate.yml +++ b/.github/workflows/sensitive-change-gate.yml @@ -4,9 +4,7 @@ on: pull_request_target: # zizmor: ignore[dangerous-triggers] -- required for write access to PRs from forks; workflow only runs trusted reusable workflow, no PR code is checked out or executed types: [opened, synchronize, reopened] -permissions: - contents: read - pull-requests: write +permissions: {} jobs: gate: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 384b06e..cbe95f6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,13 +7,14 @@ on: branches: [main] workflow_dispatch: -permissions: - contents: read +permissions: {} jobs: test: name: Test runs-on: ubuntu-latest + permissions: + contents: read steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -40,6 +41,8 @@ jobs: lint: name: Lint runs-on: ubuntu-latest + permissions: + contents: read steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -56,6 +59,8 @@ jobs: lint-actions: name: GitHub Actions audit runs-on: ubuntu-latest + permissions: + contents: read steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -72,6 +77,8 @@ jobs: security: name: Govulncheck runs-on: ubuntu-latest + permissions: + contents: read steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -90,6 +97,8 @@ jobs: test-race: name: Test (race detector) runs-on: ubuntu-latest + permissions: + contents: read steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -105,6 +114,8 @@ jobs: benchmarks: name: Benchmarks runs-on: ubuntu-latest + permissions: + contents: read if: github.event_name == 'push' && github.ref == 'refs/heads/main' steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/seed/.github/workflows/dependabot-auto-merge.yml b/seed/.github/workflows/dependabot-auto-merge.yml index 500abbc..35cc334 100644 --- a/seed/.github/workflows/dependabot-auto-merge.yml +++ b/seed/.github/workflows/dependabot-auto-merge.yml @@ -2,13 +2,14 @@ name: Dependabot Auto-Merge on: pull_request -permissions: - contents: write - pull-requests: write +permissions: {} jobs: auto-merge: runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write if: github.actor == 'dependabot[bot]' && github.event.pull_request.user.login == 'dependabot[bot]' # zizmor: ignore[bot-conditions] -- dual check: actor validates current trigger, user.login validates PR origin steps: - name: Fetch Dependabot metadata diff --git a/seed/.github/workflows/labeler.yml b/seed/.github/workflows/labeler.yml index bcdab76..075d37f 100644 --- a/seed/.github/workflows/labeler.yml +++ b/seed/.github/workflows/labeler.yml @@ -4,13 +4,14 @@ on: pull_request_target: # zizmor: ignore[dangerous-triggers] -- required for write access to PRs from forks; workflow only runs trusted actions, no PR code is checked out or executed types: [opened, synchronize, reopened] -permissions: - contents: read - pull-requests: write +permissions: {} jobs: label: runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write steps: - uses: actions/labeler@634933edcd8ababfe52f92936142cc22ac488b1b # v6.0.1 with: diff --git a/seed/.github/workflows/scorecard.yml b/seed/.github/workflows/scorecard.yml index 1537d9a..95f9ea6 100644 --- a/seed/.github/workflows/scorecard.yml +++ b/seed/.github/workflows/scorecard.yml @@ -7,7 +7,7 @@ on: - cron: '30 1 * * 6' workflow_dispatch: -permissions: read-all +permissions: {} jobs: analysis: diff --git a/seed/.github/workflows/test.yml b/seed/.github/workflows/test.yml index 87cf498..6c08519 100644 --- a/seed/.github/workflows/test.yml +++ b/seed/.github/workflows/test.yml @@ -7,13 +7,14 @@ on: branches: [main] workflow_dispatch: -permissions: - contents: read +permissions: {} jobs: test: name: Tests runs-on: ubuntu-latest + permissions: + contents: read env: APPNAME_NO_KEYRING: "1" # Uncomment for private module access: GOPRIVATE: github.com/basecamp/ @@ -62,6 +63,8 @@ jobs: lint: name: Lint runs-on: ubuntu-latest + permissions: + contents: read steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: @@ -96,6 +99,8 @@ jobs: security: name: Security runs-on: ubuntu-latest + permissions: + contents: read steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: @@ -131,6 +136,8 @@ jobs: race-check: name: Race Detection runs-on: ubuntu-latest + permissions: + contents: read env: APPNAME_NO_KEYRING: "1" # Uncomment for private module access: GOPRIVATE: github.com/basecamp/ @@ -165,6 +172,8 @@ jobs: integration: name: Integration Tests runs-on: ubuntu-latest + permissions: + contents: read env: APPNAME_NO_KEYRING: "1" # Uncomment for private module access: GOPRIVATE: github.com/basecamp/ @@ -212,6 +221,8 @@ jobs: cli-surface: name: CLI Surface Check runs-on: ubuntu-latest + permissions: + contents: read env: APPNAME_NO_KEYRING: "1" # Uncomment for private module access: GOPRIVATE: github.com/basecamp/ @@ -266,6 +277,8 @@ jobs: benchmarks: name: Benchmarks runs-on: ubuntu-latest + permissions: + contents: read if: github.event_name == 'push' && github.ref == 'refs/heads/main' continue-on-error: true env: From 39673e5a84739484e7f381dab076a4c93fa4fc86 Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Fri, 20 Mar 2026 13:49:38 -0400 Subject: [PATCH 9/9] Revert seed/ workflow changes The seed/ directory contains templates for scaffolding new CLI projects, not live workflows. Only .github/workflows/ should be hardened. Co-Authored-By: Claude Opus 4.6 (1M context) --- seed/.github/dependabot.yml | 4 +- seed/.github/workflows/ai-labeler.yml | 22 ++----- .../workflows/dependabot-auto-merge.yml | 9 ++- seed/.github/workflows/labeler.yml | 9 ++- seed/.github/workflows/release.yml | 36 +++++------ seed/.github/workflows/scorecard.yml | 4 +- seed/.github/workflows/security.yml | 47 +++++--------- seed/.github/workflows/test.yml | 63 ++++++------------- 8 files changed, 68 insertions(+), 126 deletions(-) diff --git a/seed/.github/dependabot.yml b/seed/.github/dependabot.yml index f1ae092..f04f54a 100644 --- a/seed/.github/dependabot.yml +++ b/seed/.github/dependabot.yml @@ -17,7 +17,7 @@ updates: patterns: - "*" cooldown: - default-days: 7 + default-days: 2 semver-major-days: 7 semver-minor-days: 3 semver-patch-days: 2 @@ -38,6 +38,6 @@ updates: patterns: - "*" cooldown: - default-days: 7 + default-days: 2 commit-message: prefix: "ci" diff --git a/seed/.github/workflows/ai-labeler.yml b/seed/.github/workflows/ai-labeler.yml index 155eda2..c4813f4 100644 --- a/seed/.github/workflows/ai-labeler.yml +++ b/seed/.github/workflows/ai-labeler.yml @@ -1,27 +1,24 @@ name: Classify PR on: - pull_request_target: # zizmor: ignore[dangerous-triggers] -- required for write access to PRs from forks; workflow only checks out base branch and uses gh CLI for PR data, no PR code is executed + pull_request_target: types: [opened, synchronize, reopened] concurrency: group: classify-pr-${{ github.event.pull_request.number }} cancel-in-progress: true -permissions: {} +permissions: + contents: read + issues: write + models: read + pull-requests: write jobs: classify: runs-on: ubuntu-latest - permissions: - contents: read - issues: write - models: read - pull-requests: write steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - with: - persist-credentials: false - name: Build prompt env: @@ -103,15 +100,8 @@ jobs: breaking: runs-on: ubuntu-latest - permissions: - contents: read - issues: write - models: read - pull-requests: write steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - with: - persist-credentials: false - name: Build prompt id: cmd-diff diff --git a/seed/.github/workflows/dependabot-auto-merge.yml b/seed/.github/workflows/dependabot-auto-merge.yml index 35cc334..b285a85 100644 --- a/seed/.github/workflows/dependabot-auto-merge.yml +++ b/seed/.github/workflows/dependabot-auto-merge.yml @@ -2,15 +2,14 @@ name: Dependabot Auto-Merge on: pull_request -permissions: {} +permissions: + contents: write + pull-requests: write jobs: auto-merge: runs-on: ubuntu-latest - permissions: - contents: write - pull-requests: write - if: github.actor == 'dependabot[bot]' && github.event.pull_request.user.login == 'dependabot[bot]' # zizmor: ignore[bot-conditions] -- dual check: actor validates current trigger, user.login validates PR origin + if: github.actor == 'dependabot[bot]' steps: - name: Fetch Dependabot metadata id: metadata diff --git a/seed/.github/workflows/labeler.yml b/seed/.github/workflows/labeler.yml index 075d37f..955c4d8 100644 --- a/seed/.github/workflows/labeler.yml +++ b/seed/.github/workflows/labeler.yml @@ -1,17 +1,16 @@ name: Label PRs on: - pull_request_target: # zizmor: ignore[dangerous-triggers] -- required for write access to PRs from forks; workflow only runs trusted actions, no PR code is checked out or executed + pull_request_target: types: [opened, synchronize, reopened] -permissions: {} +permissions: + contents: read + pull-requests: write jobs: label: runs-on: ubuntu-latest - permissions: - contents: read - pull-requests: write steps: - uses: actions/labeler@634933edcd8ababfe52f92936142cc22ac488b1b # v6.0.1 with: diff --git a/seed/.github/workflows/release.yml b/seed/.github/workflows/release.yml index 28cc52f..614b491 100644 --- a/seed/.github/workflows/release.yml +++ b/seed/.github/workflows/release.yml @@ -9,7 +9,13 @@ concurrency: group: release-${{ github.ref }} cancel-in-progress: false -permissions: {} +permissions: + contents: write + id-token: write + attestations: write + security-events: write + pull-requests: read + models: read jobs: security: @@ -17,10 +23,6 @@ jobs: uses: ./.github/workflows/security.yml secrets: RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} - permissions: - contents: read - security-events: write - pull-requests: read test: name: Test before release @@ -34,21 +36,20 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: fetch-depth: 0 - persist-credentials: false # Configure vars.RELEASE_CLIENT_ID and secrets.RELEASE_APP_PRIVATE_KEY to enable private module access - name: Generate token for private module access if: vars.RELEASE_CLIENT_ID != '' id: app-token - uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2 with: app-id: ${{ vars.RELEASE_CLIENT_ID }} - private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} # zizmor: ignore[secrets-outside-env] -- release workflow needs private module access; environment protection would block tag-triggered releases + private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} owner: basecamp # repositories: ,homebrew-tap - name: Set up Go - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 # zizmor: ignore[cache-poisoning] -- cache is branch-isolated; fork PRs cannot write to this cache + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 with: go-version-file: 'go.mod' @@ -66,7 +67,7 @@ jobs: - name: Cache BATS id: cache-bats - uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 # zizmor: ignore[cache-poisoning] -- cache key is a static version string, not attacker-controllable + uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5 with: path: /usr/local/libexec/bats-core key: bats-1.11.0 @@ -137,21 +138,20 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: fetch-depth: 0 - persist-credentials: false # Configure vars.RELEASE_CLIENT_ID and secrets.RELEASE_APP_PRIVATE_KEY for private modules + Homebrew tap - name: Generate token for private module and tap access if: vars.RELEASE_CLIENT_ID != '' id: sdk-token - uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2 with: app-id: ${{ vars.RELEASE_CLIENT_ID }} - private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} # zizmor: ignore[secrets-outside-env] -- release workflow needs private module access; environment protection would block tag-triggered releases + private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} owner: basecamp # repositories: ,homebrew-tap - name: Set up Go - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 # zizmor: ignore[cache-poisoning] -- cache is branch-isolated; fork PRs cannot write to this cache + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 with: go-version-file: 'go.mod' @@ -197,7 +197,7 @@ jobs: uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0 - name: Install Syft - uses: anchore/sbom-action/download-syft@17ae1740179002c89186b61233e0f892c3118b11 # v0.23.0 + uses: anchore/sbom-action/download-syft@17ae1740179002c89186b61233e0f892c3118b11 # v0 # Configure vars.ENABLE_AI_CHANGELOG=true plus models:read permission to enable AI changelog - name: Build changelog context @@ -351,8 +351,6 @@ jobs: contents: read steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - with: - persist-credentials: false - name: Check for skill files id: check-skills @@ -367,10 +365,10 @@ jobs: - name: Generate token for skills repo if: steps.check-skills.outputs.found == 'true' id: skills-token - uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2 with: app-id: ${{ vars.SKILLS_APP_ID }} - private-key: ${{ secrets.SKILLS_APP_PRIVATE_KEY }} # zizmor: ignore[secrets-outside-env] -- skills sync needs app credentials; environment protection would block tag-triggered releases + private-key: ${{ secrets.SKILLS_APP_PRIVATE_KEY }} owner: basecamp repositories: skills diff --git a/seed/.github/workflows/scorecard.yml b/seed/.github/workflows/scorecard.yml index 95f9ea6..6f0223e 100644 --- a/seed/.github/workflows/scorecard.yml +++ b/seed/.github/workflows/scorecard.yml @@ -7,7 +7,7 @@ on: - cron: '30 1 * * 6' workflow_dispatch: -permissions: {} +permissions: read-all jobs: analysis: @@ -33,7 +33,7 @@ jobs: path: results.sarif retention-days: 5 - - uses: github/codeql-action/upload-sarif@c793b717bc78562f491db7b0e93a3a178b099162 # v4.32.5 + - uses: github/codeql-action/upload-sarif@c793b717bc78562f491db7b0e93a3a178b099162 # v4 continue-on-error: true with: sarif_file: results.sarif diff --git a/seed/.github/workflows/security.yml b/seed/.github/workflows/security.yml index 48c226c..a5dc882 100644 --- a/seed/.github/workflows/security.yml +++ b/seed/.github/workflows/security.yml @@ -14,19 +14,19 @@ on: required: false workflow_dispatch: -permissions: {} +permissions: + contents: read + security-events: write + pull-requests: read jobs: secrets: name: Secret Scanning runs-on: ubuntu-latest - permissions: - contents: read steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: fetch-depth: 0 - persist-credentials: false - name: Install gitleaks run: | @@ -39,14 +39,9 @@ jobs: trivy: name: Trivy Security Scan runs-on: ubuntu-latest - permissions: - contents: read - security-events: write steps: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - with: - persist-credentials: false - name: Run Trivy vulnerability scanner (filesystem) uses: aquasecurity/trivy-action@e368e328979b113139d6f9068e03accaed98a518 # 0.34.1 @@ -61,7 +56,7 @@ jobs: version: 'v0.69.3' - name: Upload Trivy scan results to GitHub Security tab - uses: github/codeql-action/upload-sarif@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4 + uses: github/codeql-action/upload-sarif@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4 if: always() continue-on-error: true # Requires GitHub Advanced Security with: @@ -70,23 +65,18 @@ jobs: gosec: name: Go Security Checker runs-on: ubuntu-latest - permissions: - contents: read - security-events: write steps: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - with: - persist-credentials: false # Configure vars.RELEASE_CLIENT_ID and secrets.RELEASE_APP_PRIVATE_KEY to enable private module access - name: Generate token for private module access if: vars.RELEASE_CLIENT_ID != '' id: app-token - uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2 with: app-id: ${{ vars.RELEASE_CLIENT_ID }} - private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} # zizmor: ignore[secrets-outside-env] -- private module access needed in PR CI runs; environment protection would block CI + private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} owner: basecamp - name: Set up Go @@ -106,7 +96,7 @@ jobs: gosec -no-fail -fmt sarif -out gosec-results.sarif ./... - name: Upload gosec scan results to GitHub Security tab - uses: github/codeql-action/upload-sarif@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4 + uses: github/codeql-action/upload-sarif@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4 if: always() continue-on-error: true # Requires GitHub Advanced Security with: @@ -117,35 +107,26 @@ jobs: dependency-review: name: Dependency Review runs-on: ubuntu-latest - permissions: - contents: read if: github.event_name == 'pull_request' continue-on-error: true # Requires GitHub Advanced Security (not available on all plans) steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - with: - persist-credentials: false - - uses: actions/dependency-review-action@05fe4576374b728f0c523d6a13d64c25081e0803 # v4.8.3 + - uses: actions/dependency-review-action@05fe4576374b728f0c523d6a13d64c25081e0803 # v4 codeql: name: CodeQL Analysis runs-on: ubuntu-latest - permissions: - contents: read - security-events: write steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - with: - persist-credentials: false # Configure vars.RELEASE_CLIENT_ID and secrets.RELEASE_APP_PRIVATE_KEY to enable private module access - name: Generate token for private module access if: vars.RELEASE_CLIENT_ID != '' id: app-token - uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2 with: app-id: ${{ vars.RELEASE_CLIENT_ID }} - private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} # zizmor: ignore[secrets-outside-env] -- private module access needed in PR CI runs; environment protection would block CI + private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} owner: basecamp - name: Set up Go @@ -160,7 +141,7 @@ jobs: run: git config --global url."https://x-access-token:${TOKEN}@github.com/".insteadOf "https://github.com/" - name: Initialize CodeQL - uses: github/codeql-action/init@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4 + uses: github/codeql-action/init@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4 with: languages: go build-mode: manual @@ -172,14 +153,14 @@ jobs: run: go build ./... - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4 + uses: github/codeql-action/analyze@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4 with: category: codeql-go upload: never output: sarif-results - name: Upload SARIF to GitHub Security tab - uses: github/codeql-action/upload-sarif@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4 + uses: github/codeql-action/upload-sarif@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4 continue-on-error: true # Requires GitHub Advanced Security with: sarif_file: sarif-results diff --git a/seed/.github/workflows/test.yml b/seed/.github/workflows/test.yml index 6c08519..3c88e8b 100644 --- a/seed/.github/workflows/test.yml +++ b/seed/.github/workflows/test.yml @@ -7,30 +7,27 @@ on: branches: [main] workflow_dispatch: -permissions: {} +permissions: + contents: read jobs: test: name: Tests runs-on: ubuntu-latest - permissions: - contents: read env: APPNAME_NO_KEYRING: "1" # Uncomment for private module access: GOPRIVATE: github.com/basecamp/ steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - with: - persist-credentials: false # Configure vars.RELEASE_CLIENT_ID and secrets.RELEASE_APP_PRIVATE_KEY to enable private module access - name: Generate token for private module access if: vars.RELEASE_CLIENT_ID != '' id: app-token - uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2 with: app-id: ${{ vars.RELEASE_CLIENT_ID }} - private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} # zizmor: ignore[secrets-outside-env] -- private module access needed in PR CI runs; environment protection would block CI + private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} owner: basecamp # repositories: @@ -63,21 +60,17 @@ jobs: lint: name: Lint runs-on: ubuntu-latest - permissions: - contents: read steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - with: - persist-credentials: false # Uncomment for private module access: GOPRIVATE: github.com/basecamp/ - name: Generate token for private module access if: vars.RELEASE_CLIENT_ID != '' id: app-token - uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2 with: app-id: ${{ vars.RELEASE_CLIENT_ID }} - private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} # zizmor: ignore[secrets-outside-env] -- private module access needed in PR CI runs; environment protection would block CI + private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} owner: basecamp - name: Set up Go @@ -99,20 +92,16 @@ jobs: security: name: Security runs-on: ubuntu-latest - permissions: - contents: read steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - with: - persist-credentials: false - name: Generate token for private module access if: vars.RELEASE_CLIENT_ID != '' id: app-token - uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2 with: app-id: ${{ vars.RELEASE_CLIENT_ID }} - private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} # zizmor: ignore[secrets-outside-env] -- private module access needed in PR CI runs; environment protection would block CI + private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} owner: basecamp - name: Set up Go @@ -136,23 +125,19 @@ jobs: race-check: name: Race Detection runs-on: ubuntu-latest - permissions: - contents: read env: APPNAME_NO_KEYRING: "1" # Uncomment for private module access: GOPRIVATE: github.com/basecamp/ steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - with: - persist-credentials: false - name: Generate token for private module access if: vars.RELEASE_CLIENT_ID != '' id: app-token - uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2 with: app-id: ${{ vars.RELEASE_CLIENT_ID }} - private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} # zizmor: ignore[secrets-outside-env] -- private module access needed in PR CI runs; environment protection would block CI + private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} owner: basecamp - name: Set up Go @@ -172,23 +157,19 @@ jobs: integration: name: Integration Tests runs-on: ubuntu-latest - permissions: - contents: read env: APPNAME_NO_KEYRING: "1" # Uncomment for private module access: GOPRIVATE: github.com/basecamp/ steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - with: - persist-credentials: false - name: Generate token for private module access if: vars.RELEASE_CLIENT_ID != '' id: app-token - uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2 with: app-id: ${{ vars.RELEASE_CLIENT_ID }} - private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} # zizmor: ignore[secrets-outside-env] -- private module access needed in PR CI runs; environment protection would block CI + private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} owner: basecamp - name: Set up Go @@ -204,7 +185,7 @@ jobs: - name: Cache BATS id: cache-bats - uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 + uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5 with: path: /usr/local/libexec/bats-core key: bats-1.11.0 @@ -221,8 +202,6 @@ jobs: cli-surface: name: CLI Surface Check runs-on: ubuntu-latest - permissions: - contents: read env: APPNAME_NO_KEYRING: "1" # Uncomment for private module access: GOPRIVATE: github.com/basecamp/ @@ -230,15 +209,14 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: fetch-depth: 0 - persist-credentials: false - name: Generate token for private module access if: vars.RELEASE_CLIENT_ID != '' id: app-token - uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2 with: app-id: ${{ vars.RELEASE_CLIENT_ID }} - private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} # zizmor: ignore[secrets-outside-env] -- private module access needed in PR CI runs; environment protection would block CI + private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} owner: basecamp - name: Set up Go @@ -277,8 +255,6 @@ jobs: benchmarks: name: Benchmarks runs-on: ubuntu-latest - permissions: - contents: read if: github.event_name == 'push' && github.ref == 'refs/heads/main' continue-on-error: true env: @@ -288,15 +264,14 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: fetch-depth: 2 - persist-credentials: false - name: Generate token for private module access if: vars.RELEASE_CLIENT_ID != '' id: app-token - uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2 with: app-id: ${{ vars.RELEASE_CLIENT_ID }} - private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} # zizmor: ignore[secrets-outside-env] -- private module access needed in PR CI runs; environment protection would block CI + private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} owner: basecamp - name: Check for benchmark-relevant changes @@ -327,7 +302,7 @@ jobs: - name: Download previous benchmark baseline if: steps.filter.outputs.bench == 'true' - uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 + uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5 with: path: benchmarks-baseline.txt key: benchmarks-baseline-${{ github.sha }} @@ -361,7 +336,7 @@ jobs: - name: Cache benchmark baseline if: steps.filter.outputs.bench == 'true' - uses: actions/cache/save@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 + uses: actions/cache/save@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5 with: path: benchmarks-baseline.txt key: benchmarks-baseline-${{ github.sha }}