← Back to home

I Found 3 Security Bugs in Popular GitHub Actions

April 2026 · 8 min read · Alex Reed

I ran a security scanner across a popular open-source project's GitHub Actions workflows. What I found surprised me — and it's probably in your repo too.

The target: keephq/keep, a YC W23 company with 5,000+ GitHub stars. Their repo has 18 workflow files. I built a scanner to check for common CI/CD misconfigurations. Here's what it found.

Bug #1: pull_request_target Without Checkout Pinning

The issue: Two workflows use pull_request_target, which runs in the context of the target repository with access to secrets. This is by design — it lets you label PRs, comment on them, etc.

The danger: if the workflow also checks out the PR's code (which these did), a malicious PR can exfiltrate your secrets.

# Dangerous pattern
on:
  pull_request_target:

jobs:
  build:
    steps:
      - uses: actions/checkout@v4  # Checks out PR code with secret access
      - run: npm install && npm test  # PR code executes with your secrets

The fix: Never check out PR code in pull_request_target workflows. If you must, use ref: ${{ github.event.pull_request.base.sha }} to check out the base branch only.

# Safe pattern
on:
  pull_request_target:

jobs:
  label:
    steps:
      - uses: actions/labeler@v5  # Safe: only labels, no checkout

Bug #2: 60+ Unpinned Action References

The issue: The vast majority of action references use mutable tags like @v4 instead of immutable commit SHAs. A tag is just a pointer — it can be moved. If an action maintainer is compromised (or their account is), the tag can point to malicious code.

# Mutable (risky)
- uses: actions/checkout@v4
- uses: actions/setup-python@v5

# Immutable (safe)
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11  # v4.1.1
- uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c  # v5.0.0

This isn't theoretical. In 2024, the reviewdog/action-setup action was compromised through a tag attack. Any workflow using @v1 started running malicious code.

I found 60+ unpinned references across keephq/keep's workflows. That's 60+ supply chain attack vectors.

The fix: Pin every action to a commit SHA. It's verbose but deterministic. Tools like pinact can automate this.

Bug #3: Missing persist-credentials: false

The issue: 14 out of 15 actions/checkout steps don't set persist-credentials: false. By default, actions/checkout saves a GitHub token in .git/config that persists for the job's duration. Any subsequent step can use it to push to the repo — even if that step is running untrusted code.

# Default behavior (tokens persist)
- uses: actions/checkout@v4

# Explicit opt-out
- uses: actions/checkout@v4
  with:
    persist-credentials: false

If you run third-party actions or user-submitted code in the same job, they can exfiltrate the persisted token.

The fix: Always set persist-credentials: false unless you explicitly need to push in that step.

The Full Audit

These three patterns are just the highlights. Across 15 workflows, my scanner found 96 issues in 5 categories:

How to Check Your Own Repo

I built a CLI tool that automates this audit. It scans your .github/workflows/ directory and outputs a report:

pip install actions-audit
actions-audit scan /path/to/repo

The source is on my SourceHut. It's a single Python file, no dependencies beyond the standard library. Read it, understand it, use it.

The Takeaway

CI/CD security is an afterthought for most teams. The workflows pass, the deployments succeed, and nobody looks at the pipeline config until something goes wrong. But your GitHub Actions workflows run with your repository's secrets, and misconfigurations there are as dangerous as any production vulnerability.

Three things to check today:

  1. Do any pull_request_target workflows check out PR code?
  2. Are your actions pinned to commit SHAs?
  3. Is persist-credentials: false set on every checkout?

If the answer to any of these is "I don't know," run the scanner. It takes 30 seconds.