Safeguard
Developer Tools•

What GITHUB_TOKEN permissions does a release workflow need?

A release workflow needs only the permissions its job uses. Set a permissions block per job, add id-token only where needed, and default the rest to none.

Safeguard Security Team
4 min read

A release workflow needs the smallest set of GITHUB_TOKEN permissions that its publish job uses, usually contents read plus id-token write for trusted publishing. Declare a permissions block on the job, because any permission you list turns every unlisted one off.

Why this matters for publishers

Every action in a job can read the token through the github.token context, even when you do not pass it in. If a test step runs with write access, a compromised dependency in that step has write access too. GitHub's hardening guidance is to grant the minimum, set the repository default to read only for contents, and raise permissions only for the individual jobs that need them.

How the permissions key behaves

According to the workflow syntax reference:

  • You can set permissions at the top of the workflow or inside a job.
  • If you specify access for any permission, all unspecified ones are set to none.
  • An empty permissions mapping disables all of them.
  • Available names include contents, packages, attestations, id-token and pull-requests, among others.
  • Without a block, the token starts at the enterprise, organization or repository default.

A split that works for most projects

permissions: {}

jobs:
  test:
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      - uses: actions/checkout@v5
        with:
          persist-credentials: false
      - run: npm ci && npm test

  publish:
    needs: test
    runs-on: ubuntu-latest
    environment: release
    permissions:
      contents: read
      id-token: write
    steps:
      - uses: actions/checkout@v5
        with:
          persist-credentials: false
      - run: npm publish

The test job cannot publish and cannot write to the repository. The publish job can ask for an OIDC token, which is what trusted publishing uses, and can read contents.

Permission by task

Task in the workflowPermission to add on that job
Check out codecontents: read
Trusted publishing to a registryid-token: write
Build provenance attestationid-token: write, attestations: write, contents: read
Push a container image to GitHub's registrypackages: write
Create a GitHub releasecontents: write
Comment on a pull requestpull-requests: write

The attestation row follows the artifact attestations guide, which lists id-token, contents and attestations for binaries and adds packages for container images.

Token lifetime and other facts

The token is a GitHub App installation token created at the start of each job, and it expires when the job finishes or at its maximum lifetime. On GitHub hosted runners that can be up to 6 hours. Events caused by the token generally do not start new workflow runs, with exceptions such as workflow_dispatch.

Checklist

  1. Put an empty permissions mapping at the top of each workflow, as in the example, or set the repository default to read.
  2. Add permissions per job.
  3. Keep id-token write off any job that runs untrusted code. See pull_request_target risks.
  4. Set persist-credentials to false on checkout.
  5. Review the workflow folder through CODEOWNERS.

Safeguard policies and gates can block merges that change workflow permissions in ways you did not approve; the free scan at gold.safeguard.sh/scan covers your dependencies.

Quick check

If I set only contents read, can the job still publish with OIDC?

No. Unspecified permissions become none, so id-token write must be listed.

Should id-token be set for the whole workflow?

Prefer the job level, so only the publish job can request an identity token.

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.