Skip to content

Commit e440b82

Browse files
authored
ci: address zizmor findings across workflows (#1009)
* ci: harden dependabot-approve actor check and pin lunariajs/action Two findings from a zizmor (static analysis for GitHub Actions) sweep: - dependabot-approve: github.actor is spoofable. A PR whose HEAD commit author is dependabot[bot] would pass this check even if the PR was opened by another account (the Synacktiv Dependabot bypass). Use github.event.pull_request.user.login, which is the PR opener and cannot be spoofed. https://docs.zizmor.sh/audits/#bot-conditions - lunaria: lunariajs/action was pinned to the mutable v1-prerelease branch tag. Every other action in the repo is SHA-pinned. Pin to the current branch tip with the v1-prerelease tag in a trailing comment so dependabot can keep it updated. * ci: address remaining zizmor medium findings Sweep of the remaining zizmor medium findings: artipacked (persist-credentials): - lunaria: was missing it. Workflow is read-only -- now persist-credentials: false. - sync-templates: was using the default. Sync step uses the app token via GH_TOKEN, not the persisted git credential -- persist-credentials: false. - auto-extract, auto-format (same-repo), format-command (same-repo), query-counts-apply (same-repo), release: persistence is intentional (each pushes back using the app token). Make it explicit with persist-credentials: true and a one-line comment so zizmor stops flagging and reviewers see the why. excessive-permissions (release.yml): - Add workflow-level permissions: {} so the sync-templates call doesn't fall through to default repo permissions. The release job already declares its own scoped block; the called workflow's job-level permissions still take precedence. secrets-inherit (release.yml -> sync-templates.yml): - Replace secrets: inherit with an explicit secrets: { APP_ID, APP_PRIVATE_KEY } mapping. sync-templates declares them as required on its workflow_call: trigger. After this commit + the upstream changes already in this PR, zizmor reports 0 high / 0 medium findings on the workflows that aren't gated behind the zizmor.yml config (which will land in a follow-up PR). The remaining findings are dangerous-triggers on the six workflows that use pull_request_target / workflow_run defensively, plus excessive-permissions on cla.yml -- all known and accepted.
1 parent 5185c4f commit e440b82

8 files changed

Lines changed: 46 additions & 3 deletions

File tree

.github/workflows/auto-extract.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ jobs:
2828
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
2929
with:
3030
token: ${{ steps.app-token.outputs.token }}
31+
# Intentional: the "Commit and push" step below pushes the
32+
# extracted catalogs back to main using this credential.
33+
persist-credentials: true
3134

3235
- uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8
3336

.github/workflows/auto-format.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ jobs:
4545
with:
4646
ref: ${{ steps.pr.outputs.ref }}
4747
token: ${{ steps.app-token.outputs.token }}
48+
# Intentional: the same-repo push step below pushes the
49+
# formatted changes back to the PR branch using this credential.
50+
persist-credentials: true
4851

4952
# --- Fork PRs: checkout the fork at the pinned SHA ---
5053

.github/workflows/dependabot-approve.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,12 @@ permissions:
1111
jobs:
1212
approve:
1313
name: Auto-Approve
14-
if: github.actor == 'dependabot[bot]'
14+
# Use github.event.pull_request.user.login, not github.actor.
15+
# github.actor reflects the last actor on the trigger and is spoofable
16+
# via a PR whose HEAD commit author is 'dependabot[bot]'. The
17+
# pull_request.user.login is the PR opener and cannot be spoofed.
18+
# https://docs.zizmor.sh/audits/#bot-conditions
19+
if: github.event.pull_request.user.login == 'dependabot[bot]'
1520
runs-on: ubuntu-latest
1621
timeout-minutes: 5
1722
steps:

.github/workflows/format-command.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ jobs:
6363
with:
6464
ref: ${{ steps.pr.outputs.ref }}
6565
token: ${{ steps.app-token.outputs.token }}
66+
# Intentional: the same-repo push step below pushes the
67+
# formatted changes back to the PR branch using this credential.
68+
persist-credentials: true
6669

6770
- name: Checkout (fork)
6871
if: steps.pr.outputs.is_fork == 'true'

.github/workflows/lunaria.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,11 @@ jobs:
2020
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
2121
with:
2222
fetch-depth: 0
23+
persist-credentials: false
2324
- uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8
2425
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
2526
with:
2627
node-version: 22
2728
cache: pnpm
2829
- run: pnpm install --frozen-lockfile
29-
- uses: lunariajs/action@v1-prerelease
30+
- uses: lunariajs/action@a0594f1c5b8d55fb367d8df607d5e2541c99e77d # v1-prerelease

.github/workflows/query-counts-apply.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,9 @@ jobs:
139139
with:
140140
ref: ${{ steps.pr.outputs.sha }}
141141
token: ${{ steps.app-token.outputs.token }}
142+
# Intentional: the same-repo push step below pushes the
143+
# updated snapshots back to the PR branch using this credential.
144+
persist-credentials: true
142145

143146
# --- Fork PRs: checkout the fork at the pinned SHA so we can push back ---
144147

.github/workflows/release.yml

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,13 @@ on:
1313

1414
concurrency: ${{ github.workflow }}-${{ github.ref }}
1515

16+
# Default-deny at the workflow level. Each job scopes its own permissions:
17+
# - `release` declares contents/id-token/pull-requests below.
18+
# - `sync-templates` calls a reusable workflow whose own job-level
19+
# permissions block (contents: read) takes precedence over the caller's
20+
# inherited permissions.
21+
permissions: {}
22+
1623
jobs:
1724
release:
1825
name: Release
@@ -36,6 +43,9 @@ jobs:
3643
with:
3744
fetch-depth: 0
3845
token: ${{ steps.app-token.outputs.token }}
46+
# Intentional: changesets/action pushes the release PR / tags
47+
# using this credential.
48+
persist-credentials: true
3949

4050
- name: Setup pnpm
4151
uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8
@@ -79,4 +89,8 @@ jobs:
7989
needs.release.outputs.published == 'true' ||
8090
inputs.publish-only
8191
uses: ./.github/workflows/sync-templates.yml
82-
secrets: inherit
92+
# Pass only the secrets sync-templates actually uses, not the full set
93+
# available to this release workflow.
94+
secrets:
95+
APP_ID: ${{ secrets.APP_ID }}
96+
APP_PRIVATE_KEY: ${{ secrets.APP_PRIVATE_KEY }}

.github/workflows/sync-templates.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@ name: Sync Templates
33
on:
44
workflow_dispatch:
55
workflow_call:
6+
secrets:
7+
APP_ID:
8+
required: true
9+
description: GitHub App client ID for emdashbot.
10+
APP_PRIVATE_KEY:
11+
required: true
12+
description: GitHub App private key for emdashbot.
613

714
jobs:
815
sync:
@@ -23,6 +30,10 @@ jobs:
2330

2431
- name: Checkout
2532
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
33+
with:
34+
# Read-only: the sync step uses the app token via GH_TOKEN, not
35+
# the persisted git credential.
36+
persist-credentials: false
2637

2738
- name: Setup Node
2839
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0

0 commit comments

Comments
 (0)