Pre-commit security scanner for GitHub Actions workflows
Project description
PipeGuard
Catch GitHub Actions security issues before they reach your runners.
Pre-commit security scanner for GitHub Actions workflows. Catches supply-chain risks, unpinned actions, known CVEs, and secret leaks — before you push.
Demo
The recording shows a scan of a real workflow file with multiple security issues. PipeGuard detects them all in under a second — no API key, no network call, fully offline:
- No top-level
permissions:block — GitHub grants write access to all scopes by default tj-actions/changed-files@v35andreviewdog/action-setup@v1match known CVEs in the bundled database- All actions are pinned to tags instead of commit SHAs — a supply-chain risk
echo ${{ secrets.DEPLOY_TOKEN }}leaks a secret value to the workflow log8398a7/action-slackis a third-party action from an unverified publisher
PipeGuard vs. the alternatives
| Feature | PipeGuard | actionlint | StepSecurity | act |
|---|---|---|---|---|
| SHA-pinning check | ✅ | ❌ | ✅ | ❌ |
| CVE database (offline) | ✅ | ❌ | ✅ (online) | ❌ |
| Permissions analysis | ✅ | ⚠️ syntax only | ✅ | ❌ |
| Secrets-leak detection | ✅ | ❌ | ✅ (runtime) | ❌ |
| Supply-chain audit | ✅ | ❌ | ✅ | ❌ |
| Syntax / type checks | ✅ via actionlint | ✅ | ❌ | ❌ |
| Run workflows locally | ❌ | ❌ | ❌ | ✅ |
| Runtime hardening | ❌ | ❌ | ✅ | ❌ |
| Pre-commit hook | ✅ | ✅ | ❌ | ❌ |
| SARIF output | ✅ | ✅ | ❌ | ❌ |
| No API key required | ✅ | ✅ | ❌ | ✅ |
| Fully offline | ✅ | ✅ | ❌ | ⚠️ needs images |
| Open source | ✅ | ✅ | ✅ | ✅ |
| Free | ✅ core | ✅ | ⚠️ freemium | ✅ |
PipeGuard fills the gap between authoring and execution: static security analysis, offline, before you push, without any external service.
Installation
Prerequisites: Python 3.11+
pip install pipeguard-cli
Install actionlint (required for syntax checks, optional for other checks):
# Linux
curl -fsSL https://github.com/rhysd/actionlint/releases/latest/download/actionlint_linux_amd64.tar.gz \
| tar xz actionlint && sudo mv actionlint /usr/local/bin/
# macOS
brew install actionlint
# Ubuntu/Debian (snap)
sudo snap install actionlint
Usage
pipeguard scan [PATH]
PATH can be a single workflow file or a directory. If omitted, defaults to .github/workflows.
Examples
# Scan entire .github/workflows directory (default)
pipeguard scan
# Scan a specific directory
pipeguard scan .github/workflows/
# Scan a single file
pipeguard scan .github/workflows/ci.yml
# JSON output (for scripts / CI pipelines)
pipeguard scan --format json
# SARIF output (for IDE integration, GitHub Code Scanning)
pipeguard scan --format sarif
# Use a custom config file
pipeguard scan --config path/to/.pipeguard.yml
When scanning a directory, pipeguard prints a per-file header and a summary at the end:
Scanning: .github/workflows/ci.yml
✓ No issues found
Scanning: .github/workflows/deploy.yml
╭─────────┬──────────────────┬──────────────────────────┬──────────────╮
│ Severity│ Rule │ Location │ Message │
├─────────┼──────────────────┼──────────────────────────┼──────────────┤
│ error │ cve-cve-2025-... │ deploy.yml:12 │ Action ... │
│ error │ sha-pinning │ deploy.yml:15 │ Action ... │
╰─────────┴──────────────────┴──────────────────────────┴──────────────╯
Scanned 2 file(s) — 2 error(s), 1 warning(s), 3 info(s) total.
Exit codes
| Code | Meaning |
|---|---|
0 |
No errors or warnings |
1 |
One or more errors or warnings found |
Info-level findings (action inventory) do not affect the exit code.
Security checks
Errors & Warnings
| Rule | Severity | Description |
|---|---|---|
sha-pinning |
error | Action pinned to a tag or branch instead of a full commit SHA — supply-chain risk (cf. CVE-2025-30066) |
sha-pinning-reusable |
error | Reusable workflow called with a tag or branch ref instead of a full commit SHA — same supply-chain risk as unpinned actions |
cve-<id> |
error | Action matches a known CVE in the local database (offline, no API) |
supply-chain |
warning | Third-party action from an unverified publisher — suppress per publisher or action via .pipeguard.yml |
secrets-leak |
error | Secret value echoed or logged in a run: step |
secrets-leak-debug |
error / warning | set -x or set -o xtrace in a run: step — shell debug mode prints every expanded command; error when secrets are in env scope, warning otherwise |
permissions-missing |
error | No permissions: block — GitHub grants write access to most scopes by default |
permissions-write-all |
error | permissions: write-all at top-level or per-job |
permissions-excessive |
warning | Sensitive scope (e.g. secrets, workflows) set to write |
permissions-id-token-unused |
warning | id-token: write is set but no OIDC-consuming action is present — allows minting cloud credentials unnecessarily |
permissions-invalid |
error | Unknown permission level (not read, write, or none) |
pull-request-target |
warning | pull_request_target trigger runs with base-repo write permissions and secret access — even for fork PRs |
pull-request-target-pwn |
error | pull_request_target combined with checkout of the PR head ref — allows untrusted fork code to run with secrets (Pwn Request) |
actionlint |
error | Syntax and type errors (requires actionlint) |
Info
| Rule | Severity | Description |
|---|---|---|
action-inventory |
info | Inventory of all third-party actions used — ref(s) and occurrence count |
CVE database
PipeGuard ships with a built-in offline CVE database — no API key, no network call during scan. The database is updated daily via NVD and shipped with each new weekly release.
[!NOTE] Last updated: 2026-03-12
| CVE | Action | Description |
|---|---|---|
| CVE-2025-30066 | tj-actions/changed-files |
tj-actions/changed-files was compromised in a supply-chain attack. All tag-based refs are potentially affected — pin to a known-good SHA. |
| CVE-2025-30154 | reviewdog/action-setup |
reviewdog/action-setup was compromised in the same supply-chain campaign as tj-actions/changed-files. Pin to a vetted SHA. |
| CVE-2023-49291 | tj-actions/branch-names |
tj-actions/branch-names improperly references github.event.pull_request.head.ref and github.head_ref in a run step, allowing a specially crafted branch name to execute arbitrary code and steal secrets or abuse GITHUB_TOKEN permissions. |
SHA-pinned refs are not flagged — another reason to always pin to a commit SHA.
To get the latest CVEs, upgrade to the newest release:
pip install --upgrade pipeguard-cli
Configuration
PipeGuard looks for a .pipeguard.yml (or .pipeguard.yaml, pipeguard.yml, pipeguard.yaml) in the current directory and its parents.
Suppress supply-chain warnings for trusted publishers
If you trust a specific publisher or action, add them to the allowlist:
# .pipeguard.yml
trusted_publishers:
- my-org # suppresses all warnings for my-org/*
- some-vendor # suppresses all warnings for some-vendor/*
trusted_actions:
- other-org/specific-action # suppresses warning for this exact action only
trusted_publishersmatches as a prefix — every action frommy-org/*is considered trusted.trusted_actionsrequires an exact match on the action name (without the@ref).- A trailing
/in publisher names is optional; PipeGuard normalises it automatically.
The built-in allowlist (GitHub, AWS, Azure, Google, Docker, HashiCorp, etc.) is always active and cannot be removed.
CI integration
Add pipeguard as a pre-push check or CI step:
# .github/workflows/pipeguard.yml
- name: Scan workflows
run: |
pip install pipeguard-cli
pipeguard scan .github/workflows/
Or as a pre-commit hook (.pre-commit-config.yaml):
repos:
- repo: local
hooks:
- id: pipeguard
name: PipeGuard workflow scan
entry: pipeguard scan
language: python
additional_dependencies: [pipeguard-cli]
files: \.github/workflows/.*\.ya?ml$
Output formats
Terminal (default) — human-readable table with rule, location, and fix suggestions.
JSON — machine-readable, one object per finding:
[
{
"rule": "cve-cve-2025-30066",
"severity": "error",
"message": "Action 'tj-actions/changed-files@v35' is affected by CVE-2025-30066: ...",
"file": ".github/workflows/ci.yml",
"line": 12,
"fix_suggestion": "Immediately pin 'tj-actions/changed-files' to a verified safe SHA."
}
]
SARIF — compatible with GitHub Code Scanning.
License
Apache 2.0 — see LICENSE.
Status
In active development. All Free-tier scanners are functional. Planned: auto-fix PRs (Pro), Maintainer Trust Score (Pro), cloud sandbox testing, IDE plugin.
Free-tier checks: SHA-pinning, CVE database, permissions audit (incl. id-token: write without OIDC), secrets-leak detection, action inventory, actionlint syntax checks.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pipeguard_cli-0.1.2.tar.gz.
File metadata
- Download URL: pipeguard_cli-0.1.2.tar.gz
- Upload date:
- Size: 319.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4264ab4e99440c5bb733383f2366f99a7a7c6d89ef1c42e9297fb45430c3676f
|
|
| MD5 |
4c37b524ec595b206e39b081a07b1f25
|
|
| BLAKE2b-256 |
b5fc400030ba5e3029ba45ef18fc686be9067a795a70a815dcc508b60b7a5979
|
Provenance
The following attestation bundles were made for pipeguard_cli-0.1.2.tar.gz:
Publisher:
deploy.yml on alex-jung/pipeguard-cli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pipeguard_cli-0.1.2.tar.gz -
Subject digest:
4264ab4e99440c5bb733383f2366f99a7a7c6d89ef1c42e9297fb45430c3676f - Sigstore transparency entry: 1106194272
- Sigstore integration time:
-
Permalink:
alex-jung/pipeguard-cli@e4f02cf50177bd565fc9bccd87ea651b799423a8 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/alex-jung
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
deploy.yml@e4f02cf50177bd565fc9bccd87ea651b799423a8 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pipeguard_cli-0.1.2-py3-none-any.whl.
File metadata
- Download URL: pipeguard_cli-0.1.2-py3-none-any.whl
- Upload date:
- Size: 30.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
24a29f8932c0e24f37ab41e214c1a520e38fc9f7ea430cc88f0e839afabc76ec
|
|
| MD5 |
471ff97bb136f5c89e0b184aa05d2613
|
|
| BLAKE2b-256 |
4047e513f5edbe316e368d7b5f627ae8a086c0ad19d6149a10a5ec4bffcf67de
|
Provenance
The following attestation bundles were made for pipeguard_cli-0.1.2-py3-none-any.whl:
Publisher:
deploy.yml on alex-jung/pipeguard-cli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pipeguard_cli-0.1.2-py3-none-any.whl -
Subject digest:
24a29f8932c0e24f37ab41e214c1a520e38fc9f7ea430cc88f0e839afabc76ec - Sigstore transparency entry: 1106194352
- Sigstore integration time:
-
Permalink:
alex-jung/pipeguard-cli@e4f02cf50177bd565fc9bccd87ea651b799423a8 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/alex-jung
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
deploy.yml@e4f02cf50177bd565fc9bccd87ea651b799423a8 -
Trigger Event:
push
-
Statement type: