Skip to content

Commit a5b14ed

Browse files
flavorjonesclaude
andauthored
ci: harden GitHub Actions workflows (#18)
* Add zizmor and actionlint CI job to test workflow Add a lint-actions job near the existing lint job to audit GitHub Actions workflows on every push and PR to main. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Configure dependabot github-actions cooldown to 7 days Increase cooldown from 2 to 7 days for github-actions ecosystem to allow more soak time before updates are proposed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add lint-actions target to Makefile for local workflow linting Run actionlint and zizmor locally via `make lint-actions`, included in the full `check-all` suite. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Pin all GitHub Actions to SHA hashes with pinact Run pinact to ensure all action references include full version comments alongside their SHA pins. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix high severity zizmor findings - Apply dual bot-conditions check on dependabot-auto-merge workflows - Suppress dangerous-triggers on pull_request_target workflows that only run trusted actions (no PR code checkout) - Move excessive-permissions to job-level in release workflows - Suppress cache-poisoning on branch-isolated caches in release workflows Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix medium severity zizmor findings - Fix ref-version-mismatch in seed workflows (update version comments) - Suppress secrets-outside-env on private module tokens in seed CI workflows where environment protection would block PR-triggered runs - Move excessive-permissions to job-level in seed/security.yml Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix low severity zizmor findings - Add persist-credentials: false to all checkout steps (artipacked) - Fix dependabot cooldown: add default-days: 7 to gomod ecosystem - Fix seed dependabot cooldown: add default-days: 7 to both ecosystems Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Move all permissions to job-level across all workflows Set permissions: {} at workflow level for all single-job and multi-job workflows, with scoped permissions on each job. This ensures every job starts with zero permissions and fails safe. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Ensure all permissions are job-level across remaining workflows Move top-level permissions to permissions: {} for test.yml, security.yml, scorecard.yml and all corresponding seed workflows. Add per-job permissions: contents: read to every job. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent a1765c6 commit a5b14ed

19 files changed

+243
-139
lines changed

.github/dependabot.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,16 @@ updates:
1818
patterns:
1919
- "*"
2020
cooldown:
21-
default-days: 2
2221
semver-major-days: 7
2322
semver-minor-days: 3
2423
semver-patch-days: 2
24+
default-days: 7
2525
commit-message:
2626
prefix: "deps"
2727

2828
# GitHub Actions
2929
- package-ecosystem: github-actions
30-
directory: /
30+
directory: "/"
3131
schedule:
3232
interval: weekly
3333
day: monday
@@ -39,6 +39,6 @@ updates:
3939
patterns:
4040
- "*"
4141
cooldown:
42-
default-days: 2
42+
default-days: 7
4343
commit-message:
4444
prefix: "ci"

.github/workflows/ai-labeler.yml

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,14 @@
11
name: Classify PR
22

33
on:
4-
pull_request_target:
4+
pull_request_target: # zizmor: ignore[dangerous-triggers] -- required for write access to PRs from forks; workflow only calls reusable workflows, no PR code is checked out or executed
55
types: [opened, synchronize, reopened]
66

77
concurrency:
88
group: classify-pr-${{ github.event.pull_request.number }}
99
cancel-in-progress: true
1010

11-
permissions:
12-
contents: read
13-
issues: write
14-
models: read
15-
pull-requests: write
11+
permissions: {}
1612

1713
jobs:
1814
classify:

.github/workflows/dependabot-auto-merge.yml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,18 @@ name: Dependabot auto-merge
22

33
on: pull_request
44

5-
permissions:
6-
contents: write
7-
pull-requests: write
5+
permissions: {}
86

97
jobs:
108
auto-merge:
119
name: Auto-merge
1210
runs-on: ubuntu-latest
13-
if: github.actor == 'dependabot[bot]'
11+
permissions:
12+
contents: write
13+
pull-requests: write
14+
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
1415
steps:
15-
- uses: dependabot/fetch-metadata@21025c705c08248db411dc16f3619e6b5f9ea21a # v2
16+
- uses: dependabot/fetch-metadata@21025c705c08248db411dc16f3619e6b5f9ea21a # v2.5.0
1617
id: metadata
1718
with:
1819
github-token: ${{ secrets.GITHUB_TOKEN }}

.github/workflows/direct-push-alert.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@ on:
44
push:
55
branches: [main]
66

7-
permissions:
8-
contents: read
9-
issues: write
7+
permissions: {}
108

119
jobs:
1210
alert:

.github/workflows/labeler.yml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
name: Label PRs
22

33
on:
4-
pull_request_target:
4+
pull_request_target: # zizmor: ignore[dangerous-triggers] -- required for write access to PRs from forks; workflow only runs actions/labeler, no PR code is checked out or executed
55
types: [opened, synchronize, reopened]
66

7-
permissions:
8-
contents: read
9-
pull-requests: write
7+
permissions: {}
108

119
jobs:
1210
label:
1311
runs-on: ubuntu-latest
12+
permissions:
13+
contents: read
14+
pull-requests: write
1415
steps:
1516
- uses: actions/labeler@634933edcd8ababfe52f92936142cc22ac488b1b # v6.0.1
1617
with:

.github/workflows/release.yml

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,33 +9,34 @@ concurrency:
99
group: release-${{ github.ref }}
1010
cancel-in-progress: false
1111

12-
permissions:
13-
contents: write
14-
security-events: write
15-
pull-requests: read
16-
models: read
12+
permissions: {}
1713

1814
jobs:
1915
security:
2016
name: Security
2117
uses: ./.github/workflows/security.yml
18+
permissions:
19+
contents: read
20+
security-events: write
21+
pull-requests: read
2222

2323
test:
2424
name: Test gate
2525
runs-on: ubuntu-latest
2626
permissions:
2727
contents: read
2828
steps:
29-
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
29+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
3030
with:
3131
fetch-depth: 0
32+
persist-credentials: false
3233

33-
- uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6
34+
- uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 # zizmor: ignore[cache-poisoning] -- cache is branch-isolated; fork PRs cannot write to this cache
3435
with:
3536
go-version-file: go.mod
3637

3738
- name: Install golangci-lint
38-
uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9
39+
uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0
3940
with:
4041
version: v2.9.0
4142
install-only: true
@@ -84,9 +85,10 @@ jobs:
8485
contents: write
8586
models: read
8687
steps:
87-
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
88+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
8889
with:
8990
fetch-depth: 0
91+
persist-credentials: false
9092

9193
- name: Verify tag is on main
9294
env:
@@ -202,7 +204,9 @@ jobs:
202204
env:
203205
HAS_SKILLS_KEY: ${{ secrets.SKILLS_APP_PRIVATE_KEY && 'true' || '' }}
204206
steps:
205-
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
207+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
208+
with:
209+
persist-credentials: false
206210

207211
- name: Check prerequisites
208212
id: check
@@ -220,7 +224,7 @@ jobs:
220224
- name: Generate token
221225
if: steps.check.outputs.ready == 'true'
222226
id: skills-token
223-
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2
227+
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
224228
with:
225229
app-id: ${{ vars.SKILLS_APP_ID }}
226230
private-key: ${{ secrets.SKILLS_APP_PRIVATE_KEY }}

.github/workflows/scorecard.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ on:
77
- cron: '30 1 * * 6'
88
workflow_dispatch:
99

10-
permissions: read-all
10+
permissions: {}
1111

1212
jobs:
1313
analysis:
@@ -17,7 +17,7 @@ jobs:
1717
id-token: write
1818
contents: read
1919
steps:
20-
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
20+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
2121
with:
2222
persist-credentials: false
2323

@@ -33,7 +33,7 @@ jobs:
3333
path: results.sarif
3434
retention-days: 5
3535

36-
- uses: github/codeql-action/upload-sarif@0d579ffd059c29b07949a3cce3983f0780820c98 # v4
36+
- uses: github/codeql-action/upload-sarif@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6
3737
continue-on-error: true
3838
with:
3939
sarif_file: results.sarif

.github/workflows/security.yml

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,7 @@ on:
1010
workflow_call:
1111
workflow_dispatch:
1212

13-
permissions:
14-
contents: read
15-
security-events: write
16-
pull-requests: read
13+
permissions: {}
1714

1815
jobs:
1916
secrets:
@@ -22,9 +19,10 @@ jobs:
2219
permissions:
2320
contents: read
2421
steps:
25-
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
22+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
2623
with:
2724
fetch-depth: 0
25+
persist-credentials: false
2826

2927
- name: Install gitleaks
3028
run: |
@@ -41,7 +39,9 @@ jobs:
4139
contents: read
4240
security-events: write
4341
steps:
44-
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
42+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
43+
with:
44+
persist-credentials: false
4545

4646
- name: Run Trivy vulnerability scanner (filesystem)
4747
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # 0.35.0
@@ -56,7 +56,7 @@ jobs:
5656
version: 'v0.69.3'
5757

5858
- name: Upload Trivy scan results to GitHub Security tab
59-
uses: github/codeql-action/upload-sarif@0d579ffd059c29b07949a3cce3983f0780820c98 # v4
59+
uses: github/codeql-action/upload-sarif@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6
6060
if: always()
6161
continue-on-error: true # Requires GitHub Advanced Security
6262
with:
@@ -69,9 +69,11 @@ jobs:
6969
contents: read
7070
security-events: write
7171
steps:
72-
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
72+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
73+
with:
74+
persist-credentials: false
7375

74-
- uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6
76+
- uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
7577
with:
7678
go-version-file: go.mod
7779

@@ -82,7 +84,7 @@ jobs:
8284
run: gosec -no-fail -fmt sarif -out gosec-results.sarif ./...
8385

8486
- name: Upload gosec scan results to GitHub Security tab
85-
uses: github/codeql-action/upload-sarif@0d579ffd059c29b07949a3cce3983f0780820c98 # v4
87+
uses: github/codeql-action/upload-sarif@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6
8688
if: always()
8789
continue-on-error: true # Requires GitHub Advanced Security
8890
with:
@@ -96,9 +98,11 @@ jobs:
9698
contents: read
9799
pull-requests: read
98100
steps:
99-
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
101+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
102+
with:
103+
persist-credentials: false
100104

101-
- uses: actions/dependency-review-action@2031cfc080254a8a887f58cffee85186f0e49e48 # v4
105+
- uses: actions/dependency-review-action@2031cfc080254a8a887f58cffee85186f0e49e48 # v4.9.0
102106
continue-on-error: true # Requires GitHub Advanced Security
103107

104108
codeql:
@@ -108,14 +112,16 @@ jobs:
108112
contents: read
109113
security-events: write
110114
steps:
111-
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
115+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
116+
with:
117+
persist-credentials: false
112118

113-
- uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6
119+
- uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
114120
with:
115121
go-version-file: go.mod
116122

117123
- name: Initialize CodeQL
118-
uses: github/codeql-action/init@0d579ffd059c29b07949a3cce3983f0780820c98 # v4
124+
uses: github/codeql-action/init@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6
119125
with:
120126
languages: go
121127
build-mode: manual
@@ -127,14 +133,14 @@ jobs:
127133
run: go build ./...
128134

129135
- name: Perform CodeQL analysis
130-
uses: github/codeql-action/analyze@0d579ffd059c29b07949a3cce3983f0780820c98 # v4
136+
uses: github/codeql-action/analyze@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6
131137
with:
132138
category: codeql-go
133139
upload: never
134140
output: sarif-results
135141

136142
- name: Upload SARIF to GitHub Security tab
137-
uses: github/codeql-action/upload-sarif@0d579ffd059c29b07949a3cce3983f0780820c98 # v4
143+
uses: github/codeql-action/upload-sarif@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6
138144
continue-on-error: true # Requires GitHub Advanced Security
139145
with:
140146
sarif_file: sarif-results

.github/workflows/sensitive-change-gate.yml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
name: Sensitive Change Gate
22

33
on:
4-
pull_request_target:
4+
pull_request_target: # zizmor: ignore[dangerous-triggers] -- required for write access to PRs from forks; workflow only calls reusable workflow, no PR code is checked out or executed
55
types: [opened, synchronize, reopened]
66

7-
permissions:
8-
contents: read
9-
pull-requests: write
7+
permissions: {}
108

119
jobs:
1210
gate:

0 commit comments

Comments
 (0)