Skip to content

Commit 70373f8

Browse files
ci(workflows): SHA-pin every action + scope token permissions to jobs
Closes 71 of the 75 open Code Scanning alerts (68 Pinned-Dependencies + 3 Token-Permissions). All findings are Scorecard checks on workflow security posture, not exploitable code defects — but the public-repo flip exposes them to anyone browsing the Security tab, so closing them improves the project's posture signal before Phase 3.8 GA. PIN: every `uses: <action>@<tag>` reference in .github/workflows/*.yml is now pinned to its full commit SHA with the original tag preserved as a trailing comment: uses: actions/checkout@de0fac2 # v6 This is the Scorecard- and Dependabot-recognized format. SHAs were resolved on 2026-05-22 via `gh api repos/<action>/commits/<tag>`. Dependabot will continue to bump them on its own schedule — it recognizes the trailing tag comment and updates both the SHA and the comment atomically. 13 actions across 16 workflow files, 57 references pinned total (some workflows reference the same action multiple times): actions/checkout @v6 -> de0fac2... actions/configure-pages @v6 -> 45bfe01... actions/deploy-pages @v5 -> cd2ce8f... actions/setup-node @v6 -> 48b55a0... actions/setup-python @v6 -> a309ff8... actions/upload-artifact @v7 -> 043fb46... actions/upload-pages-artifact @v5 -> fc324d3... github/codeql-action/{init,analyze,autobuild,upload-sarif} @V3 -> 03e4368... gitleaks/gitleaks-action @v2 -> ff98106... ossf/scorecard-action @v2.4.0 -> 62b2cac... sigstore/cosign-installer @V3 -> 398d4b0... splunk/appinspect-api-action @v3.0.5 -> 86013a6... zaproxy/action-baseline @v0.14.0 -> 7c4deb1... TOKEN PERMISSIONS: release.yml and codeql.yml had workflow-scope `contents: write` / `security-events: write` permissions. Scorecard flags workflow-scope writes that could be limited to a specific job. Both workflows now declare `contents: read` at workflow scope and elevate per-job where actually needed: - release.yml: `release` job keeps `contents: write` + `id-token: write` (Sigstore OIDC + asset upload). - codeql.yml: `analyze` job keeps `security-events: write` (SARIF upload to Code Scanning UI). Rationale comments added inline so future contributors understand why the split exists and don't collapse the scopes back to workflow- level on the next maintenance pass. Remaining 4 alerts to be addressed separately: - 1 Branch-Protection (repo Settings → Branches) - 1 Code-Review (resolves with Branch-Protection) - 1 Fuzzing (dismiss-with-rationale) - 1 CIIBestPractices (dismiss-with-rationale) No behaviour change to any workflow. Identical OIDC flow, identical cosign behavior, identical SARIF upload behavior — SHA pinning is a no-op until an attacker compromises an action repo (the threat model this closes).
1 parent 02be284 commit 70373f8

16 files changed

Lines changed: 71 additions & 60 deletions

.github/workflows/a11y-audit.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,10 @@ jobs:
4040
contents: read
4141

4242
steps:
43-
- uses: actions/checkout@v6
43+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
4444

4545
- name: Set up Node.js
46-
uses: actions/setup-node@v6
46+
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
4747
with:
4848
node-version: "20"
4949
cache: "npm"
@@ -87,7 +87,7 @@ jobs:
8787
# forms a longitudinal record. Reviewing the trend across
8888
# runs is more useful than just the latest run.
8989
if: always()
90-
uses: actions/upload-artifact@v7
90+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
9191
with:
9292
name: a11y-reports-${{ github.run_id }}
9393
path: tests/a11y/reports/
@@ -104,7 +104,7 @@ jobs:
104104
105105
- name: Upload Splunk logs on failure
106106
if: failure()
107-
uses: actions/upload-artifact@v7
107+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
108108
with:
109109
name: a11y-splunk-logs-${{ github.run_id }}
110110
path: ci-logs/

.github/workflows/appinspect-api.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ jobs:
6767

6868
steps:
6969
- name: Checkout
70-
uses: actions/checkout@v6
70+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
7171

7272
- name: Build .spl
7373
run: bash scripts/package.sh
@@ -105,7 +105,7 @@ jobs:
105105
echo "Staged for AppInspect: $APPINSPECT_DIR/$(basename "$SPL_FILE")"
106106
107107
- name: AppInspect API — Cloud Vetting profile (cloud tag)
108-
uses: splunk/appinspect-api-action@v3.0.5
108+
uses: splunk/appinspect-api-action@86013a62b0c773d1e9cd4623080b4495973936c3 # v3.0.5
109109
with:
110110
username: ${{ secrets.SPLUNK_DEV_USERNAME }}
111111
password: ${{ secrets.SPLUNK_DEV_PASSWORD }}
@@ -115,7 +115,7 @@ jobs:
115115

116116
- name: AppInspect API — Self-Service Cloud profile (private_app tag)
117117
if: always()
118-
uses: splunk/appinspect-api-action@v3.0.5
118+
uses: splunk/appinspect-api-action@86013a62b0c773d1e9cd4623080b4495973936c3 # v3.0.5
119119
with:
120120
username: ${{ secrets.SPLUNK_DEV_USERNAME }}
121121
password: ${{ secrets.SPLUNK_DEV_PASSWORD }}

.github/workflows/appinspect.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ jobs:
5050

5151
steps:
5252
- name: Checkout
53-
uses: actions/checkout@v6
53+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
5454

5555
# `cache: pip` was removed 2026-05-18 — actions/setup-python@v5
5656
# requires `requirements.txt` or `pyproject.toml` to compute the
@@ -61,7 +61,7 @@ jobs:
6161
# restore the cache, but with a single one-line install of a pinned
6262
# package the cache hit is worthless. Keep simple.
6363
- name: Set up Python 3.11
64-
uses: actions/setup-python@v6
64+
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
6565
with:
6666
python-version: '3.11'
6767

@@ -159,7 +159,7 @@ jobs:
159159
160160
- name: Upload AppInspect JSON reports
161161
if: always()
162-
uses: actions/upload-artifact@v7
162+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
163163
with:
164164
name: appinspect-reports
165165
path: .planning/appinspect/appinspect-*-ci.json

.github/workflows/ci.yml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,10 @@ jobs:
5353
permissions:
5454
contents: read
5555
steps:
56-
- uses: actions/checkout@v6
56+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
5757

5858
- name: Set up Python
59-
uses: actions/setup-python@v6
59+
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
6060
with:
6161
python-version: "3.11"
6262

@@ -67,7 +67,7 @@ jobs:
6767
run: bash scripts/package.sh
6868

6969
- name: Upload .spl artifact
70-
uses: actions/upload-artifact@v7
70+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
7171
with:
7272
name: wl_manager-spl
7373
path: dist/*.spl
@@ -79,7 +79,7 @@ jobs:
7979
permissions:
8080
contents: read
8181
steps:
82-
- uses: actions/checkout@v6
82+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
8383

8484
- name: Run doc-drift guard
8585
# Same script the pre-commit hook runs locally. Verifies
@@ -95,10 +95,10 @@ jobs:
9595
permissions:
9696
contents: read
9797
steps:
98-
- uses: actions/checkout@v6
98+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
9999

100100
- name: Set up Python
101-
uses: actions/setup-python@v6
101+
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
102102
with:
103103
python-version: "3.11"
104104

@@ -152,13 +152,13 @@ jobs:
152152
permissions:
153153
contents: read
154154
steps:
155-
- uses: actions/checkout@v6
155+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
156156

157157
- name: Set up Node.js
158158
# Node 20 LTS. Vitest 3.x requires Node ≥18; we
159159
# pin to the latest LTS so the toolchain matches
160160
# what most contributors run locally.
161-
uses: actions/setup-node@v6
161+
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
162162
with:
163163
node-version: "20"
164164
# Cache npm dependencies keyed on package-lock.json

.github/workflows/codeql.yml

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,13 @@ on:
3232
# synchronized load with other GitHub-hosted workflows.
3333
- cron: '17 6 * * 1'
3434

35+
# Workflow-scope = least privilege (read-only). The `analyze` job
36+
# below elevates to `security-events: write` for uploading CodeQL
37+
# results to the Code Scanning UI. This pattern silences Scorecard's
38+
# TokenPermissions warning by scoping the write to the specific job
39+
# that needs it.
3540
permissions:
36-
actions: read
3741
contents: read
38-
security-events: write
3942

4043
# Cancel in-flight CodeQL runs on the same ref (PR pushes can stack).
4144
concurrency:
@@ -55,6 +58,10 @@ jobs:
5558
if: github.event.repository.private == false
5659
runs-on: ubuntu-latest
5760
timeout-minutes: 30
61+
permissions:
62+
actions: read
63+
contents: read
64+
security-events: write
5865

5966
strategy:
6067
fail-fast: false
@@ -63,10 +70,10 @@ jobs:
6370

6471
steps:
6572
- name: Checkout
66-
uses: actions/checkout@v6
73+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
6774

6875
- name: Initialize CodeQL
69-
uses: github/codeql-action/init@v3
76+
uses: github/codeql-action/init@03e4368ac7daa2bd82b3e85262f3bf87ee112f57 # v3
7077
with:
7178
languages: ${{ matrix.language }}
7279
# Default query pack: security-and-quality.
@@ -78,9 +85,9 @@ jobs:
7885
# autobuild is a no-op for both but harmless to leave in — it's
7986
# the documented step for compiled-language matrices.
8087
- name: Autobuild
81-
uses: github/codeql-action/autobuild@v3
88+
uses: github/codeql-action/autobuild@03e4368ac7daa2bd82b3e85262f3bf87ee112f57 # v3
8289

8390
- name: Perform CodeQL analysis
84-
uses: github/codeql-action/analyze@v3
91+
uses: github/codeql-action/analyze@03e4368ac7daa2bd82b3e85262f3bf87ee112f57 # v3
8592
with:
8693
category: "/language:${{ matrix.language }}"

.github/workflows/docs.yml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,14 @@ jobs:
5252

5353
steps:
5454
- name: Checkout
55-
uses: actions/checkout@v6
55+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
5656
with:
5757
# Fetch full history so future `git-revision-date-localized`
5858
# plugin support (if we add it) can read commit dates.
5959
fetch-depth: 0
6060

6161
- name: Set up Python
62-
uses: actions/setup-python@v6
62+
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
6363
with:
6464
python-version: '3.11'
6565

@@ -88,7 +88,7 @@ jobs:
8888
# Always upload — even on build failure we want the partial
8989
# site for debugging (mkdocs writes what it has).
9090
if: always()
91-
uses: actions/upload-artifact@v7
91+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
9292
with:
9393
name: site-${{ github.sha }}
9494
path: site/
@@ -120,12 +120,12 @@ jobs:
120120

121121
steps:
122122
- name: Checkout
123-
uses: actions/checkout@v6
123+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
124124
with:
125125
fetch-depth: 0
126126

127127
- name: Set up Python
128-
uses: actions/setup-python@v6
128+
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
129129
with:
130130
python-version: '3.11'
131131

@@ -143,10 +143,10 @@ jobs:
143143
run: mkdocs build --verbose
144144

145145
- name: Configure Pages
146-
uses: actions/configure-pages@v6
146+
uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6
147147

148148
- name: Upload Pages artifact
149-
uses: actions/upload-pages-artifact@v5
149+
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5
150150
with:
151151
path: site/
152152
# upload-pages-artifact v4.0.0 introduced a "hidden files are
@@ -161,4 +161,4 @@ jobs:
161161

162162
- name: Deploy to GitHub Pages
163163
id: deployment
164-
uses: actions/deploy-pages@v5
164+
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5

.github/workflows/e2e-full.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,10 @@ jobs:
5050
contents: read
5151

5252
steps:
53-
- uses: actions/checkout@v6
53+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
5454

5555
- name: Set up Node.js
56-
uses: actions/setup-node@v6
56+
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
5757
with:
5858
node-version: "20"
5959
cache: "npm"
@@ -171,7 +171,7 @@ jobs:
171171
172172
- name: Upload logs on failure
173173
if: failure()
174-
uses: actions/upload-artifact@v7
174+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
175175
with:
176176
name: e2e-full-logs-${{ github.run_id }}
177177
path: ci-logs/
@@ -182,7 +182,7 @@ jobs:
182182
# when a comparison fails. Uploading them lets the next morning's
183183
# triage check the actual visual divergence.
184184
if: failure()
185-
uses: actions/upload-artifact@v7
185+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
186186
with:
187187
name: visual-diffs-${{ github.run_id }}
188188
path: tests/e2e/visual_diff/

.github/workflows/e2e-smoke.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,10 @@ jobs:
4545
contents: read
4646

4747
steps:
48-
- uses: actions/checkout@v6
48+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
4949

5050
- name: Set up Node.js
51-
uses: actions/setup-node@v6
51+
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
5252
with:
5353
node-version: "20"
5454
cache: "npm"
@@ -121,7 +121,7 @@ jobs:
121121
122122
- name: Upload logs on failure
123123
if: failure()
124-
uses: actions/upload-artifact@v7
124+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
125125
with:
126126
name: e2e-smoke-logs-${{ github.run_id }}
127127
path: ci-logs/

.github/workflows/integration-tests.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,10 @@ jobs:
4444
contents: read
4545

4646
steps:
47-
- uses: actions/checkout@v6
47+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
4848

4949
- name: Set up Python
50-
uses: actions/setup-python@v6
50+
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
5151
with:
5252
python-version: "3.11"
5353

@@ -163,7 +163,7 @@ jobs:
163163
164164
- name: Upload logs on failure
165165
if: failure()
166-
uses: actions/upload-artifact@v7
166+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
167167
with:
168168
name: splunk-logs-${{ github.run_id }}
169169
path: ci-logs/

.github/workflows/pip-audit.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@ jobs:
3434
permissions:
3535
contents: read
3636
steps:
37-
- uses: actions/checkout@v6
37+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
3838

3939
- name: Set up Python
40-
uses: actions/setup-python@v6
40+
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
4141
with:
4242
python-version: "3.11"
4343

0 commit comments

Comments
 (0)