Safeguard
Security Guides•

Your copied workflow has no permissions block. What should you add?

A workflow without a permissions key relies on your default token settings. Add an explicit block, start at contents read, and raise it per job as needed.

Safeguard Security Team
4 min read

Add a permissions block with contents set to read at the top of the file, then grant extra scopes to individual jobs that truly need them. Without a block, the workflow depends on whatever default your repository or organization chose, which you may not have checked.

What the token is

Every workflow run receives a generated token, available as secrets.GITHUB_TOKEN and through the github.token context. GitHub says an action can reach the token through that context even if the workflow never passes it in. So every action in the file, including ones you did not write, can use it.

GitHub also says the token's permissions are limited to the repository that contains the workflow and that it expires when the job finishes. Those limits help. They do not change the advice to give it the least access it needs.

The recommended shape

GitHub's secure use guide says it is good practice to set the default for the token to read access for repository contents only, and then increase permissions for individual jobs. In YAML, that looks like this:

permissions:
  contents: read

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - run: echo "build and test here"

  comment:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write
    steps:
      - run: echo "this job may write pull request comments"

The top block sets the floor. The second job opts in to one extra scope. If a job needs nothing from the token, you can go further and set an empty block for it.

Why it matters for copied files

If someone abuses a compromised step, the damage depends on what the token can do. GitHub's page on compromised runners says an attacker with the job's token can modify repository contents, including releases, if the assigned permissions are not restricted. A starter workflow that only runs tests has no reason to carry that power.

A short decision table

The job does thisPermission to consider
Checks out code and runs testscontents read
Posts a pull request commentpull-requests write, in that job only
Opens or labels issuesissues write, in that job only
Publishes a releasecontents write, in a release job with review
Needs another repositoryA GitHub App token or similar, not wider token scopes

GitHub's guidance for needing more than the token can give is to create a GitHub App and generate an installation token in the workflow, or to store a personal access token as a secret. A narrowly scoped app is usually the cleaner choice.

Check the defaults too

Open the Actions settings for your repository and organization and see what the default token permission is. Set it to read, so a workflow with no block is safe by default. Then your explicit blocks become documentation of intent, not the only line of defense. See GITHUB_TOKEN least privilege for more patterns.

Quick check

Does a read only default make the block unnecessary?

The block still helps. It records intent in the file and keeps the workflow safe if someone later changes the repository default.

Can a job grant itself more than the workflow level block?

A job level block sets that job's own permissions. That is why changes to workflow files need review and code owners, since a write access user can edit them.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.