GitHub Actions permissions: use least privilege
Set a read-only top-level `permissions:` default in every GitHub Actions workflow, then grant each job only the additional `GITHUB_TOKEN` scopes it needs, so unrelated steps cannot inherit repository write access.
How StarSling worksAI agents open the PR
StarSling agents inspect your workflow, apply this optimization, and open a reviewable PR automatically.
Do this: start read-only and elevate one job
Every workflow declares a restrictive top-level permissions block. Jobs that only build or test keep contents: read; a job that comments, publishes, or updates a status receives only that named write scope. Job-level permissions replace the workflow defaults, so each elevated job also re-declares any read scopes it still needs.12
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- run: npm test
comment:
needs: test
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- run: gh pr comment "${{ github.event.pull_request.number }}" --body "Tests passed"
env:
GH_TOKEN: ${{ github.token }}Avoid this: grant write access to the whole workflow
Every action and install script in every job receives a token with write access it does not need, widening the damage if any executed code is compromised.
permissions: write-all
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- run: npm install
- run: npm testPermissions do not make an unsafe trigger safe
Least privilege limits blast radius, but a write scope still belongs only in a job whose trigger and inputs are trusted. If an outsider can influence the run, separate the privileged operation from the untrusted build. See secure pull_request_target for that boundary. The ci-secure skill checks both broad permissions and write tokens on untrusted triggers.
Runs on your machine. GitHub API optional, for the impostor-SHA check and the dormancy note.
Why it matters
GitHub creates a repository-scoped token for every job, and actions can access it through the github.token context even when it is not passed explicitly. A restrictive permissions block turns a compromised build step from a repository writer into a read-only process unless that specific job genuinely needs more.
When to declare GitHub Actions permissions
Use it when
Use an explicit top-level permissions block in every workflow. Add a job-level write scope only for the smallest job that performs the matching operation, such as commenting on a pull request or publishing a package.
Be careful when
Do not add a write permission speculatively to silence an error. Identify the API call that needs it, scope it to one job, and keep untrusted code out of that job.
Verify on your repo
Hand this prompt to your coding agent (Claude Code, Cursor, and the like) to audit and fix this practice in your own repo.
Audit every workflow under .github/workflows for missing or broad GITHUB_TOKEN permissions. Add a top-level `permissions: { contents: read }` default where appropriate, replace `write-all` and workflow-wide write scopes with the minimum named permissions, and grant write access only in the job that performs the matching operation. Remember that a job-level permissions block replaces workflow defaults, so re-declare required read scopes. Do not put write access in a job an outsider can trigger or influence; split that workflow instead. Preserve behavior, show the diff, and open a PR rather than applying it blindly.
Ground these changes in the upstream docs before you edit: https://docs.github.com/en/actions/reference/security/secure-use, https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#permissions. If you cannot fetch them, say so rather than guessing, and cite what you used in the PR description.Prefer to check by hand?
Inspect every workflow for a top-level
permissions:block; flag missing blocks,write-all, and broad workflow-level write scopes.For each job-level write permission, trace the exact step that consumes it and remove unrelated scopes.
Confirm jobs triggered by outsider-controlled activity do not receive write access; split trusted and untrusted work when they do.
Go further
One fix, all of them, or forever.
You have the prompt for this one practice. Here is how much further you can take it, each step doing more for you than the last.
Fix this one thing
Copy the prompt above
Hand P5.5 / P14.3 to your coding agent and fix it in your repo today.
Fix everything, once
Install the ci-secure skill
One prompt checks every workflow against ten critical exploit-chain attack vectors, explains each reachable path, and lets you choose focused fixes. Open source, runs locally. It is not a comprehensive security audit.
Keep it fixed, forever
Install the StarSling GitHub App
Connect GitHub and the fixes stay applied as your CI evolves, with agents that keep inspecting your workflows and opening optimization PRs you review.
More best practices for GitHub Actions
Where to go next in the CI best-practices catalog.
- Security
Scope id-token: write to the publishing job
In GitHub Actions, declare
id-token: writein the specific publishing job'spermissions:block, never at the workflow's top level, so an OIDC publish token isn't minted for jobs that run untrusted code. - Security
Secure pull_request_target in GitHub Actions
Use
pull_requestfor any job that checks out, builds, installs, or tests contributor code; reservepull_request_targetfor metadata-only work on trusted base-branch code with minimal permissions. - Security
GitHub Actions secrets: prevent leaks and overexposure
Expose a GitHub Actions secret only to the step that consumes it, gate production secrets with an environment, prefer short-lived OIDC credentials, and keep credentials out of logs, command arguments, caches, and artifacts.
Trying to make GitHub Actions faster?
If you do not know why a run is slow yet, start with the diagnostic guide. Let an agent find which of these applies: /ci-speedup. If your workflows are already optimized but still slow, see our guide to fast GitHub Actions and GitHub Actions runner alternatives. Building containers in CI? The Docker workflow guide covers layer caching end to end. Want to see how your configuration measures up before changing anything? CI Score grades workflow config against a pass/fail rubric of these practices. It is not a speed measurement.
FAQ
What permissions should GitHub Actions have by default?
Start with contents: read, then add only the named scope a specific job needs. Avoid write-all; most build and test jobs need no repository write access.
Can an action use GITHUB_TOKEN if I do not pass it?
Yes. An action can access the token through the github.token context, which is why restricting the job's permissions matters even when your YAML never passes a token input.
Sources
Last updated 2026-08-19