GitHub Actions pinning#475
Conversation
📝 WalkthroughWalkthroughThis PR hardens GitHub Actions security across build and CI workflows by pinning third-party actions to specific commits, applying least-privilege job-level permissions, standardizing system environment variables, and establishing automated policy enforcement through Dependabot and zizmor scanning for unpinned action references. ChangesWorkflow Security and Reliability Hardening
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/ci.yml:
- Around line 32-35: The caller workflow job build_x86_64-linux currently grants
only id-token: write and contents: read but passes publish_manual: true to the
reusable workflow; update the permissions block for build_x86_64-linux to also
grant pull-requests: write, statuses: write, and deployments: write (in addition
to id-token: write and contents: read) so the called workflow’s manual job can
perform the Netlify/manual updates using secrets.GITHUB_TOKEN; if you prefer,
gate these elevated permissions behind the same condition that sets
publish_manual to true so they are only granted when needed.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: f9b052b7-6f2b-4fce-a339-b38bc351e576
📒 Files selected for processing (6)
.envrc.github/dependabot.yml.github/workflows/build.yml.github/workflows/ci.yml.github/workflows/zizmor.yml.github/zizmor.yml
| permissions: | ||
| id-token: write | ||
| contents: read | ||
| with: |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
In GitHub Actions reusable workflows, do permissions set on the caller job cap the GITHUB_TOKEN permissions available inside called workflow jobs, even if the called workflow asks for broader permissions?
💡 Result:
Yes, permissions set on a caller job explicitly cap the GITHUB_TOKEN permissions available to any jobs within a called reusable workflow [1][2]. When you call a reusable workflow, GitHub validates the permissions requested by the called workflow against the permissions granted by the caller job [3]. The following rules apply: 1. Mandatory Capping: The permissions available to a reusable workflow can only be maintained or reduced from those granted by the caller job; they cannot be elevated [4][1]. If the called workflow requests broader permissions than what is provided by the caller job, the workflow will fail validation before it even begins executing [2][3]. 2. Default Behavior: If you do not explicitly define permissions on the caller job, the called workflow inherits the default permissions for the GITHUB_TOKEN [1]. 3. Principle of Least Privilege: Because reusable workflows are treated similarly to actions in terms of permission scoping, it is considered a security best practice to grant the caller job only the minimum permissions required for the tasks performed by the called workflow [2][5]. If a called workflow requires specific permissions to function (e.g., contents: write), you must ensure that the caller job explicitly grants those permissions (or a broader superset) [2][3]. If the caller job fails to grant sufficient permissions, the workflow execution will be rejected by GitHub [3].
Citations:
- 1: https://github.com/github/docs/blob/main/content/actions/reference/workflows-and-actions/reusing-workflow-configurations.md
- 2: https://www.kenmuse.com/blog/github-actions-workflow-permissions/
- 3:
call-workflowgenerated caller jobs omit requiredpermissions:for reusable workflows github/gh-aw#21071 - 4: https://github.com/github/docs/blob/main/content/actions/how-tos/reuse-automations/reuse-workflows.md
- 5: https://github.com/github/docs/blob/main/content/actions/tutorials/authenticate-with-github_token.md
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Files =="
ls -la .github/workflows || true
echo
echo "== ci.yml (relevant sections) =="
# Show around reported lines and around build_x86_64-linux usage
sed -n '1,120p' .github/workflows/ci.yml | nl -ba | sed -n '20,80p' || true
rg -n "build_x86_64-linux|publish_manual|uses: \\.\/\\.github\\/workflows\\/build\\.yml|uses: \\.\\/\\.github\\/workflows\\/build\\.yml" .github/workflows/ci.yml || true
sed -n '1,220p' .github/workflows/ci.yml | nl -ba | sed -n '1,220p' || true
echo
echo "== build.yml (manual job + permissions) =="
rg -n "job[s]?:|manual:|publish_manual|permissions:" .github/workflows/build.yml || true
# Print around manual job section and around any publish_manual logic
python3 - <<'PY'
import re, itertools, pathlib
p = pathlib.Path(".github/workflows/build.yml")
text = p.read_text().splitlines()
# find lines with "manual:" and "permissions:"
hits = [i for i,l in enumerate(text) if re.search(r'^\s*manual:\s*$', l) or re.search(r'^\s*manual:\s*$', l)]
perm_hits = [i for i,l in enumerate(text) if re.search(r'^\s*permissions:\s*$', l)]
print("manual line indices:", hits)
print("permissions line indices:", perm_hits)
PY
# Best-effort contextual extracts (avoid huge output)
sed -n '1,260p' .github/workflows/build.yml | nl -ba | sed -n '1,260p' || trueRepository: DeterminateSystems/nix-src
Length of output: 1087
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== ci.yml: locate build_x86_64-linux and the uses: build.yml call =="
rg -n "build_x86_64-linux" .github/workflows/ci.yml || true
rg -n "uses:\s*\./\.github/workflows/build\.yml|publish_manual|permissions:" .github/workflows/ci.yml || true
echo
echo "== ci.yml: print around build_x86_64-linux usage (best-effort) =="
# Print entire file with line numbers via cat -n (file is small: 6086 bytes)
cat -n .github/workflows/ci.yml | sed -n '1,220p'
echo
echo "== build.yml: print around manual job and its permissions =="
cat -n .github/workflows/build.yml | sed -n '200,320p'
echo
echo "== build.yml: print around publish_manual input handling =="
rg -n "publish_manual" .github/workflows/build.yml || true
cat -n .github/workflows/build.yml | sed -n '1,120p'Repository: DeterminateSystems/nix-src
Length of output: 17052
Widen build_x86_64-linux permissions for the reusable workflow’s manual publish path.
In .github/workflows/ci.yml, build_x86_64-linux sets only permissions: id-token: write and contents: read but passes publish_manual: true to .github/workflows/build.yml. The called workflow’s manual job requires pull-requests, statuses, and deployments write permissions, and those permissions can’t be elevated beyond what the caller grants—so the Netlify/manual update path that uses secrets.GITHUB_TOKEN can fail.
Suggested fix
build_x86_64-linux:
uses: ./.github/workflows/build.yml
permissions:
id-token: write
contents: read
+ pull-requests: write
+ statuses: write
+ deployments: write
with:
system: x86_64-linux
runner: namespace-profile-linuxamd32c64g-cache
runner_for_virt: UbuntuLatest32Cores128G
runner_small: ubuntu-latest
run_tests: true
run_vm_tests: true
run_regression_tests: true
publish_manual: true📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| permissions: | |
| id-token: write | |
| contents: read | |
| with: | |
| permissions: | |
| id-token: write | |
| contents: read | |
| pull-requests: write | |
| statuses: write | |
| deployments: write | |
| with: |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/ci.yml around lines 32 - 35, The caller workflow job
build_x86_64-linux currently grants only id-token: write and contents: read but
passes publish_manual: true to the reusable workflow; update the permissions
block for build_x86_64-linux to also grant pull-requests: write, statuses:
write, and deployments: write (in addition to id-token: write and contents:
read) so the called workflow’s manual job can perform the Netlify/manual updates
using secrets.GITHUB_TOKEN; if you prefer, gate these elevated permissions
behind the same condition that sets publish_manual to true so they are only
granted when needed.
This PR updates our GitHub Actions setup to include pinned hashes for non-DetSys Actions, Zizmor checks, and a more granular Dependabot configuration.
Summary by CodeRabbit