fix: narrow down action permissions to per-job ones#828
fix: narrow down action permissions to per-job ones#828steveiliop56 wants to merge 1 commit intomainfrom
Conversation
📝 WalkthroughWalkthroughThis PR implements least-privilege permission scoping across four GitHub Actions workflows by reducing top-level workflow permissions to read-only and adding explicit job-level permission grants. Each job now receives only the specific permissions it requires. Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related PRs
Suggested labels
Suggested reviewers
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)
Review rate limit: 1/3 review remaining, refill in 30 minutes and 6 seconds. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/nightly.yml:
- Around line 149-150: The image-build jobs currently declare only "packages:
read" which prevents checkout and GHCR pushes; update each image-build job (the
four image-build job definitions referenced) to set job-level permissions to
include "contents: read" and "packages: write" so checkout works and images can
be pushed (i.e., replace or augment the existing permissions block for those
image-build jobs with contents: read and packages: write).
In @.github/workflows/release.yml:
- Around line 119-120: The job-level permissions for the image build jobs
currently only specify "packages: read", which prevents actions/checkout and
docker build-push from working; update each image build job's permissions block
to include both "contents: read" and "packages: write" (in addition to or
replacing the existing packages: read) so actions/checkout can access the repo
and docker/build-push-action can push to GHCR using GITHUB_TOKEN; locate the
YAML permission blocks that currently contain "permissions: packages: read" in
the four image build jobs and add "contents: read" and "packages: write" there.
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: ecc0114b-139a-4130-9d49-13254c9e0daf
📒 Files selected for processing (4)
.github/workflows/nightly.yml.github/workflows/release.yml.github/workflows/sponsors.yml.github/workflows/stale.yml
| permissions: | ||
| packages: read |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
In GitHub Actions, when jobs.<job_id>.permissionsdefines onlypackages: read, are unspecified scopes (like contents) set to none? Also, what GITHUB_TOKENscope is required to push Docker images to GHCR usingdocker/build-push-action?
💡 Result:
Yes, when jobs.<job_id>.permissions defines only packages: read, unspecified scopes like contents are set to none. To push Docker images to GHCR using docker/build-push-action, the GITHUB_TOKEN requires packages: write permission (and typically contents: read for checkout).
Citations:
- 1: https://github.blog/changelog/2021-04-20-github-actions-control-permissions-for-github_token
- 2: https://docs.github.com/actions/using-workflows/workflow-syntax-for-github-actions
- 3: https://stackoverflow.com/questions/75926611/github-workflow-to-push-docker-image-to-ghcr-io
- 4: https://oneuptime.com/blog/post/2026-02-08-how-to-push-docker-images-to-github-container-registry/view
- 5: https://devopsboys.com/blog/github-actions-docker-push-permission-denied-fix-2026
- 6: https://docs.github.com/en/packages/managing-github-packages-using-github-actions-workflows/publishing-and-installing-a-package-with-github-actions
🏁 Script executed:
fd -t f "nightly.yml" --path "*workflows*"Repository: tinyauthapp/tinyauth
Length of output: 298
🏁 Script executed:
fd -t f "nightly.yml"Repository: tinyauthapp/tinyauth
Length of output: 46
🏁 Script executed:
find . -name "*.yml" -o -name "*.yaml" | grep -i workflowRepository: tinyauthapp/tinyauth
Length of output: 252
🏁 Script executed:
wc -l ./.github/workflows/nightly.ymlRepository: tinyauthapp/tinyauth
Length of output: 100
🏁 Script executed:
cat -n ./.github/workflows/nightly.yml | sed -n '140,160p;200,220p;260,280p;320,340p'Repository: tinyauthapp/tinyauth
Length of output: 2778
🏁 Script executed:
cat -n ./.github/workflows/nightly.yml | sed -n '151,195p;211,255p;271,315p;331,375p'Repository: tinyauthapp/tinyauth
Length of output: 8276
Build jobs are under-scoped and will fail image publishing.
All four image-build jobs (at lines 149-150, 209-210, 269-270, and 329-330) have permissions set to packages: read, but these jobs perform checkout and push container images to GHCR with push=true. When job-level permissions are defined with only packages: read, unspecified scopes default to none. This means these jobs lack the required packages: write permission to push images and contents: read permission for checkout to function properly.
🔧 Proposed fix
image-build:
runs-on: ubuntu-latest
needs:
- create-release
- generate-metadata
permissions:
- packages: read
+ contents: read
+ packages: write
image-build-distroless:
runs-on: ubuntu-latest
needs:
- create-release
- generate-metadata
- image-build
permissions:
- packages: read
+ contents: read
+ packages: write
image-build-arm:
runs-on: ubuntu-24.04-arm
needs:
- create-release
- generate-metadata
permissions:
- packages: read
+ contents: read
+ packages: write
image-build-arm-distroless:
runs-on: ubuntu-24.04-arm
needs:
- create-release
- generate-metadata
- image-build-arm
permissions:
- packages: read
+ contents: read
+ packages: write📝 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: | |
| packages: read | |
| permissions: | |
| contents: read | |
| packages: write |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/nightly.yml around lines 149 - 150, The image-build jobs
currently declare only "packages: read" which prevents checkout and GHCR pushes;
update each image-build job (the four image-build job definitions referenced) to
set job-level permissions to include "contents: read" and "packages: write" so
checkout works and images can be pushed (i.e., replace or augment the existing
permissions block for those image-build jobs with contents: read and packages:
write).
There was a problem hiding this comment.
That's not true, only the merge job needs write permissions, the other ones just upload it as artifact.
There was a problem hiding this comment.
@steveiliop56, I understand the confusion, but the image-build jobs are actually pushing to GHCR — just without a named tag. Looking at the Build and push step in each image-build job:
outputs: type=image,push-by-digest=true,name-canonical=true,push=trueThe push=true here means docker/build-push-action pushes the image layers to ghcr.io/${{ github.repository_owner }}/tinyauth by digest. What gets uploaded to GitHub Actions artifacts is only a small text file containing the digest hash (e.g., abc123...), not the actual container image. The image-merge jobs then read those digest files and create a manifest list pointing to the already-pushed image layers.
So the flow is:
image-buildjobs → push image by digest to GHCR (packages: writeneeded) → upload digest hash as artifactimage-mergejobs → download digest hashes → create manifest list on GHCR (packages: writeneeded)
Both stages write to GHCR. With only packages: read set at the job level, the build jobs will fail at the docker/build-push-action step with a permission denied error when trying to push the image digest layers.
| permissions: | ||
| packages: read |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
For GitHub Actions job-level permissions, do unspecified scopes become none? For GHCR image publishing with docker/build-push-actionandGITHUB_TOKEN, is packages: write required?
💡 Result:
Yes, for GitHub Actions job-level permissions, unspecified scopes are set to none. Yes, packages: write is required for publishing images to GHCR using docker/build-push-action with GITHUB_TOKEN.
Citations:
- 1: https://github.blog/changelog/2021-04-20-github-actions-control-permissions-for-github_token
- 2: https://docs.github.com/actions/using-workflows/workflow-syntax-for-github-actions
- 3: https://docs.github.com/en/actions/concepts/use-cases/about-packaging-with-github-actions
- 4: https://docs.github.com/en/packages/learn-github-packages/about-permissions-for-github-packages
- 5: https://docs.github.com/packages/working-with-a-github-packages-registry/working-with-the-container-registry
- 6: https://docs.github.com/en/actions/guides/publishing-docker-images
- 7: https://stackoverflow.com/questions/75926611/github-workflow-to-push-docker-image-to-ghcr-io
- 8: https://oneuptime.com/blog/post/2026-02-20-github-actions-docker-build-push/view
🏁 Script executed:
# Check if the file exists and get its structure
file_path=".github/workflows/release.yml"
if [ -f "$file_path" ]; then
echo "File exists. Line count:"
wc -l "$file_path"
echo ""
echo "=== Lines 115-125 (around line 119) ==="
sed -n '115,125p' "$file_path"
echo ""
echo "=== Lines 172-182 (around line 176) ==="
sed -n '172,182p' "$file_path"
echo ""
echo "=== Lines 229-239 (around line 233) ==="
sed -n '229,239p' "$file_path"
echo ""
echo "=== Lines 286-296 (around line 290) ==="
sed -n '286,296p' "$file_path"
else
echo "File not found at $file_path"
git ls-files | grep -i release.yml
fiRepository: tinyauthapp/tinyauth
Length of output: 1347
🏁 Script executed:
# Get full job definitions for the image-build jobs
echo "=== image-build job (lines 115-170) ==="
sed -n '115,170p' .github/workflows/release.yml
echo ""
echo "=== image-build-distroless job (lines 172-227) ==="
sed -n '172,227p' .github/workflows/release.yml
echo ""
echo "=== Checking for docker/build-push-action usage ==="
grep -n "docker/build-push-action" .github/workflows/release.ymlRepository: tinyauthapp/tinyauth
Length of output: 4738
Image build jobs missing required permissions for checkout and publish.
All four image build jobs (lines 119, 176, 233, 290) currently have only packages: read. With job-level permission blocks, unspecified scopes default to none, causing these jobs to fail:
actions/checkoutrequirescontents: readto access repository codedocker/build-push-actionwithpush=trueto GHCR requirespackages: writeto publish images usingGITHUB_TOKEN
Add both permissions to each affected job:
🔧 Proposed fix
image-build:
runs-on: ubuntu-latest
needs:
- generate-metadata
permissions:
- packages: read
+ contents: read
+ packages: write
image-build-distroless:
runs-on: ubuntu-latest
needs:
- generate-metadata
- image-build
permissions:
- packages: read
+ contents: read
+ packages: write
image-build-arm:
runs-on: ubuntu-24.04-arm
needs:
- generate-metadata
permissions:
- packages: read
+ contents: read
+ packages: write
image-build-arm-distroless:
runs-on: ubuntu-24.04-arm
needs:
- generate-metadata
- image-build-arm
permissions:
- packages: read
+ contents: read
+ packages: write🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/release.yml around lines 119 - 120, The job-level
permissions for the image build jobs currently only specify "packages: read",
which prevents actions/checkout and docker build-push from working; update each
image build job's permissions block to include both "contents: read" and
"packages: write" (in addition to or replacing the existing packages: read) so
actions/checkout can access the repo and docker/build-push-action can push to
GHCR using GITHUB_TOKEN; locate the YAML permission blocks that currently
contain "permissions: packages: read" in the four image build jobs and add
"contents: read" and "packages: write" there.
There was a problem hiding this comment.
That's not true, only the merge job needs write permissions, the other ones just upload it as artifact.
There was a problem hiding this comment.
@steveiliop56, I appreciate the clarification, but I'd like to address both concerns separately:
Regarding packages: write:
Looking at the image-build jobs, the docker/build-push-action step does include push=true along with push-by-digest=true:
outputs: type=image,push-by-digest=true,name-canonical=true,push=trueThe "upload as artifact" step only uploads the digest reference (a small text file containing the digest hash). The actual image layers are still pushed to GHCR during this job — push-by-digest=true just means the image is pushed without a named tag. Omitting packages: write causes the push to GHCR to fail, as GITHUB_TOKEN only has read access to packages by default — write access must be explicitly set.
Regarding contents: read:
This concern stands regardless. When you specify the access for any permission in a job-level permissions block, all permissions that are not specified are set to none — they are not inherited from the workflow-level permissions: contents: read. So the image-build jobs with only packages: read will have contents: none, causing actions/checkout to fail.
GitHub's own official documentation for publishing Docker images to GHCR shows the correct job-level permissions as contents: read and packages: write.
The fix remains the same as originally proposed — the image-build jobs need both:
permissions:
contents: read
packages: write
scottmckendry
left a comment
There was a problem hiding this comment.
If coderabbit's comments are false positives (which I think they are) should be all good.
Summary by CodeRabbit