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.
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
# Run only specific scanner(s) — always runs locally
pipeguard scan --scanner sha-pinning
pipeguard scan --scanner sha-pinning --scanner cve
When scanning a directory, pipeguard prints a per-file header and a summary at the end:
.github/workflows/ci.yml
✓ No issues found
.github/workflows/deploy.yml
✗ error sha-pinning line 12 Action 'actions/checkout' is pinned to 'v3' instead of a full commit SHA.
✗ error cve-cve-2025-30066 line 15 Action 'tj-actions/changed-files@v35' is affected by CVE-2025-30066: ...
⚠ warning supply-chain line 15 Untrusted action 'tj-actions/changed-files' — verify publisher and pin to a SHA.
✗ error permissions-missing line 1 No top-level 'permissions:' key found.
· info action-inventory line 12 Action 'actions/checkout' used with ref(s): 'v3' (1 occurrence(s)).
5 issue(s) found (3 error(s), 1 warning(s))
Scanned 2 file(s) — 3 error(s), 1 warning(s), 1 info(s) total.
Exit codes
| Code | Meaning |
|---|---|
0 |
No errors or warnings |
1 |
One or more errors or warnings found |
2 |
Invalid or expired Pro license key |
Info-level findings (action inventory) do not affect the exit code.
Options
| Flag | Description |
|---|---|
--format terminal|json|sarif |
Output format (default: terminal) |
--fix |
Apply auto-fixes via Pro API (requires license key) |
--config PATH |
Path to a .pipeguard.yml config file |
--verbose / -v |
Show per-scanner progress and Pro API response |
--scanner NAME |
Run only this scanner. Can be repeated. Respects config file settings (e.g. trusted_publishers, min_cvss) but overrides skip. Bypasses Pro API — always runs locally. Valid names: sha-pinning, supply-chain, cve, permissions, secrets-flow, pull-request-target, actionlint, action-inventory |
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-31
| 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.
All scanner settings live under a scanners: key. Each scanner can be configured independently:
# .pipeguard.yml
scanners:
# Disable a scanner entirely
supply-chain:
skip: false # default; set to true to disable
trusted_publishers:
- my-org # suppresses warnings for my-org/*
- some-vendor
trusted_actions:
- other-org/specific-action # exact match, without @ref
# Lower the CVE severity threshold (default: 9.0)
cve:
min_cvss: 7.0
# Skip a scanner entirely
actionlint:
skip: true
Scanner names: sha-pinning, supply-chain, cve, permissions, secrets-flow, pull-request-target, actionlint, action-inventory.
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. - Scanners not listed in the config file run with their defaults.
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:
[
{
"id": "a1b2c3d4e5f6",
"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,
"col": 0,
"fix_suggestion": "Immediately pin 'tj-actions/changed-files' to a verified safe SHA.",
"patch": null,
"score": null,
"detail": null
}
]
Each finding has a stable id (sha256(rule:line:message)[:12]) — valid within one scan session. Use it to reference specific findings when calling the Pro API for auto-fixes.
SARIF — compatible with GitHub Code Scanning.
PipeGuard Pro
PipeGuard Pro extends the free CLI with deeper analysis and auto-fix capabilities via a cloud API.
Activate a license key
pipeguard auth <your-license-key>
The key is stored in ~/.pipeguard/credentials and picked up automatically on every pipeguard scan. You can also set it via the environment:
export PIPEGUARD_LICENSE_KEY=<your-license-key>
How Pro mode works
When a license key is present, all scanning is handled by the Pro API — free local scanners are skipped entirely. The Pro API runs the same free checks plus additional deep-analysis scanners, and returns findings with optional patch and score fields.
If the Pro API is unreachable or returns an error, PipeGuard automatically falls back to the free local scanners.
Auto-fix
pipeguard scan --fix
Sends findings with available patches back to the Pro API (POST /v1/fix). The patched workflow is written back to disk. Applied and skipped counts are printed at the end.
License
Apache 2.0 — see LICENSE.
Metadata
Release files for pipeguard-cli 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pipeguard_cli-0.2.0.tar.gz | 333.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pipeguard_cli-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 372.1 kB
Release files / pipeguard_cli-0.2.0.tar.gz
| Download URL | pipeguard_cli-0.2.0.tar.gz |
|---|---|
| Size | 333.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
02331043785c09944eb3710fa2d70ce8ffb82b35706a31965aff5a8f1ea2083c
|
|
BLAKE2b-256 checksum How to use checksums |
e2bb4cdc94fc29ebf040c0c3d5f5dbff998d9bdae01158a8d6ab5309961233e5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.12.9
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Mar 31, 2026.
Transparency logRelease files / pipeguard_cli-0.2.0-py3-none-any.whl
| Download URL | pipeguard_cli-0.2.0-py3-none-any.whl |
|---|---|
| Size | 38.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
199a2a253a6ef48fc2857282c900b976864c11102517449dd5fbea16579adec9
|
|
BLAKE2b-256 checksum How to use checksums |
2a99f2a395fa1a77ce15f2f8991a25b2346cbbbf35a6f3623294eebf82959b42
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.12.9
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Mar 31, 2026.
Transparency log