GitHub Actions' New vulnerability-alerts Permission: Read Dependabot Alerts Without a PAT
GitHub Actions' New vulnerability-alerts Permission: Read Dependabot Alerts Without a PAT
If you've ever tried to have a GitHub Actions workflow check for open Dependabot alerts โ to block a release, gate a merge, or post a Slack summary โ you've probably hit a wall: the default GITHUB_TOKEN can't read them. The workaround for years has been minting a classic personal access token with security_events scope, stashing it as a repo secret, and hoping whoever set it up doesn't leave the org before it needs rotating. As of the GitHub Actions early-September 2026 update, that workaround is no longer necessary. There's a new vulnerability-alerts permission you can grant directly to GITHUB_TOKEN, scoped to read-only, no PAT required.
This is a small change, but it closes a real, commonly-hit gap โ the GitHub Community discussion asking "why can't my Action see Dependabot alerts with the default token" has been open for a long time with no first-party fix until now.
Why this existed as a gap in the first place
Dependabot alert data has always been readable through the REST API (GET /repos/{owner}/{repo}/dependabot/alerts) and the GraphQL API (vulnerabilityAlerts on Repository), but both endpoints require a token with vulnerability-alert read access โ and GITHUB_TOKEN's built-in permission set never included that scope, no matter how broadly you set contents, security-events, or anything else in your workflow's permissions: block. Security-events permission gets you code scanning and secret scanning alerts; it does not get you Dependabot alerts. That mismatch is what sent people to personal access tokens as the only option.
A stored PAT for this purpose is a worse security posture than it looks:
- It typically outlives the person who created it.
- It's usually scoped far more broadly than "read Dependabot alerts" because classic PATs don't have that granularity either โ
security_eventson a classic PAT covers more than you need. - It's one more long-lived secret sitting in your repo or org secrets store that has to be rotated and audited.
The new permission removes the reason for all of that, for this one specific use case.
What vulnerability-alerts actually does
It's a new key under a workflow job's permissions: block, alongside the familiar contents, issues, pull-requests, etc. It takes exactly two values:
1permissions:
2 vulnerability-alerts: read
or, if you're setting permissions: {} at the top and opting in job-by-job:
1permissions:
2 vulnerability-alerts: none
There's no write โ this is deliberately read-only, matching how Dependabot alerts already work (a workflow shouldn't be creating or dismissing alerts through this path; that's still a Dependabot/repo-settings action). Once set, the ephemeral GITHUB_TOKEN GitHub issues for that job run can call the Dependabot alerts REST endpoint and the vulnerabilityAlerts GraphQL field for that repository, for the duration of the job, same as any other scoped permission.
Step-by-step: reading Dependabot alerts in a workflow
1. Grant the permission
At minimum, scope it to the job that needs it rather than the whole workflow:
1name: dependabot-alert-gate
2on:
3 pull_request:
4
5jobs:
6 check-alerts:
7 runs-on: ubuntu-latest
8 permissions:
9 contents: read
10 vulnerability-alerts: read
11 steps:
12 - name: Check open Dependabot alerts
13 env:
14 GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
15 run: |
16 gh api repos/${{ github.repository }}/dependabot/alerts \
17 --jq '[.[] | select(.state=="open")] | length' \
18 > alert_count.txt
19 echo "Open alerts: $(cat alert_count.txt)"
The gh api call here uses the REST endpoint directly through the GitHub CLI, which is already available on GitHub-hosted runners and picks up GH_TOKEN automatically โ no extra setup.
2. Gate on severity, not just count
A raw count isn't usually the useful signal โ most repos always have a few low-severity alerts. Filter for what actually matters:
1 - name: Fail if critical/high alerts are open
2 env:
3 GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
4 run: |
5 CRITICAL=$(gh api repos/${{ github.repository }}/dependabot/alerts \
6 --jq '[.[] | select(.state=="open" and (.security_advisory.severity=="critical" or .security_advisory.severity=="high"))] | length')
7 echo "Critical/high open alerts: $CRITICAL"
8 if [ "$CRITICAL" -gt 0 ]; then
9 echo "::error::$CRITICAL open critical/high Dependabot alerts โ blocking merge"
10 exit 1
11 fi
3. Or query via GraphQL if you want more fields in one call
1 - name: Query alerts via GraphQL
2 env:
3 GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
4 run: |
5 gh api graphql -f query='
6 query($owner:String!, $name:String!) {
7 repository(owner:$owner, name:$name) {
8 vulnerabilityAlerts(first: 20, states: OPEN) {
9 nodes {
10 securityVulnerability {
11 severity
12 package { name }
13 }
14 createdAt
15 }
16 }
17 }
18 }' -f owner=${{ github.repository_owner }} -f name=$(basename ${{ github.repository }})
4. Remove the old PAT once this is verified working
If you had a repo or org secret specifically for this (commonly named something like DEPENDABOT_READ_TOKEN or reused from a broader SECURITY_PAT), audit what else depends on it before deleting it โ teams often quietly reuse a "security" PAT for more than one job. Once nothing else references it, revoke it at the source (Settings โ Developer settings โ Personal access tokens) and remove the repo/org secret.
Best practices
- Scope it to the job, not the workflow. Set
permissions: {}at the workflow level and addvulnerability-alerts: readonly on the specific job that needs it โ the same least-privilege discipline you'd apply tocontentsorpull-requests. - Use it to gate, not just report. A workflow that silently logs alert counts doesn't change behavior. Tie it to a required status check on your protected branch if you want it to actually block merges with unresolved critical alerts.
- Combine with severity and package filters. Most repos will never hit zero open alerts across all severities; gate on what's actionable (critical/high, or specific ecosystems you've committed to keeping clean) rather than an all-or-nothing count.
- Audit existing PATs used for this purpose now. This permission directly replaces a common PAT use case โ go find where you're already doing this the old way and migrate it.
Common mistakes to avoid
- Setting
vulnerability-alerts: writeexpecting it to dismiss alerts. It doesn't exist โ this permission is read-only by design. Dismissing or reopening alerts still goes through the Dependabot UI, or aPATCH /repos/{owner}/{repo}/dependabot/alerts/{alert_number}call with a token that has the broadersecurity_eventswrite scope โ not through this new read-only permission. - Forgetting
GH_TOKENin the step'senv. TheghCLI won't pick up${{ secrets.GITHUB_TOKEN }}automatically unless it's exposed asGH_TOKENorGITHUB_TOKENin that step's environment โ a step without it will fail with an authentication error even though the job-levelpermissions:block looks correct. - Granting it workflow-wide "to be safe." Every job in that workflow now gets Dependabot alert read access whether it needs it or not โ scope narrowly.
- Assuming this fixes access for private security advisories or GHSA data outside your own repo. This permission is scoped to your repository's own Dependabot alerts, not GitHub's broader advisory database (that's the separate, unauthenticated Security Advisories API).
Troubleshooting
Still getting a 403 after adding the permission: confirm the workflow doesn't have permissions: read-all or a restrictive org-level default that's overriding the job-level block โ org and repo Actions settings can cap what GITHUB_TOKEN is allowed to request regardless of what the workflow file asks for. Check Settings โ Actions โ General โ Workflow permissions at the repo and org level.
gh api returns an empty array even though you know alerts exist: check you're hitting the right repo โ ${{ github.repository }} resolves to the repo the workflow runs in, which matters if this workflow is a reusable workflow called from elsewhere.
Works locally with gh auth login but fails in Actions: local gh uses your personal OAuth session, which already has broad access; in Actions you're authenticating as the job-scoped GITHUB_TOKEN, which only has exactly what permissions: grants for that run.
FAQ
Does this work on GitHub Enterprise Server?
Check your GHES version's release notes before relying on it โ permission additions to GITHUB_TOKEN on GitHub.com don't always land on GHES in the same release cycle.
Can I use this to auto-dismiss low-severity alerts? No โ it's read-only. Dismissal still requires a different, more broadly-scoped credential or doing it through the UI/Dependabot settings.
Does this replace Dependabot's own PR-opening behavior? No, they're unrelated. Dependabot still opens its own update PRs independently of this permission; this permission is about your own workflows reading alert state, e.g. to gate a deploy or generate a report.
Do I need this if I'm just using Dependabot's default security updates? No โ if you're not writing custom workflow logic that queries alert state, there's nothing to change. This only matters if you're building your own gating, reporting, or alerting on top of Dependabot data.
Will removing my old PAT break anything else? Only if something else was reusing it. Grep your workflows and org secrets usage for the token's name before revoking it โ don't assume single-purpose just because that's how it started.
Key takeaways
| Question | Answer |
|---|---|
| What is it? | A new vulnerability-alerts permission (read/none) for GITHUB_TOKEN in GitHub Actions |
| What does it unlock? | Read access to Dependabot alerts via REST (/dependabot/alerts) and GraphQL (vulnerabilityAlerts) |
| What did it replace? | Classic personal access tokens with security_events scope, stored as secrets, for this one purpose |
| Can it write/dismiss alerts? | No โ read-only by design |
| When did it ship? | GitHub Actions early-September 2026 changelog update |
| Action item | Scope it per-job, migrate off any existing PAT used for this, gate merges on severity not raw count |
Further Reading
- GitHub Actions Self-Hosted Runner Minimum Version Enforcement Brownouts
- GitHub Actions Checkout v7: Fixing the Pwn Request Vulnerability Before July 16
- GitHub Actions Now Holds Suspicious Workflow Runs Before They Touch Your Secrets
- Official: GitHub Actions Early September 2026 Updates
- Official: About Dependabot Alerts