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.
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.
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.2.0.tar.gz.
File metadata
- Download URL: pipeguard_cli-0.2.0.tar.gz
- Upload date:
- Size: 333.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 |
02331043785c09944eb3710fa2d70ce8ffb82b35706a31965aff5a8f1ea2083c
|
|
| MD5 |
88b64d7dd86c73b0b7e68524886c8ff0
|
|
| BLAKE2b-256 |
e2bb4cdc94fc29ebf040c0c3d5f5dbff998d9bdae01158a8d6ab5309961233e5
|
Provenance
The following attestation bundles were made for pipeguard_cli-0.2.0.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.2.0.tar.gz -
Subject digest:
02331043785c09944eb3710fa2d70ce8ffb82b35706a31965aff5a8f1ea2083c - Sigstore transparency entry: 1202796056
- Sigstore integration time:
-
Permalink:
alex-jung/pipeguard-cli@e032ffb10f644842eb80957141bd035af3b29a48 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/alex-jung
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
deploy.yml@e032ffb10f644842eb80957141bd035af3b29a48 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pipeguard_cli-0.2.0-py3-none-any.whl.
File metadata
- Download URL: pipeguard_cli-0.2.0-py3-none-any.whl
- Upload date:
- Size: 38.3 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 |
199a2a253a6ef48fc2857282c900b976864c11102517449dd5fbea16579adec9
|
|
| MD5 |
5154c09d9993cd8be3f43c9b055420b4
|
|
| BLAKE2b-256 |
2a99f2a395fa1a77ce15f2f8991a25b2346cbbbf35a6f3623294eebf82959b42
|
Provenance
The following attestation bundles were made for pipeguard_cli-0.2.0-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.2.0-py3-none-any.whl -
Subject digest:
199a2a253a6ef48fc2857282c900b976864c11102517449dd5fbea16579adec9 - Sigstore transparency entry: 1202796060
- Sigstore integration time:
-
Permalink:
alex-jung/pipeguard-cli@e032ffb10f644842eb80957141bd035af3b29a48 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/alex-jung
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
deploy.yml@e032ffb10f644842eb80957141bd035af3b29a48 -
Trigger Event:
push
-
Statement type: