Plumber detects CI/CD security issues in your GitHub workflows and gives you a score
See the code
CI/CD security scanner for GitHub Actions and GitLab CI
Securing the workflows of:
Lightpanda ★ 36k | Delve ★ 24.9k | Duplicati ★ 15k | Resty ★ 11.8k | nginx-ui ★ 11.5k | Bunkerity ★ 11k | Stellarium ★ 10k | intuitem ★ 4.4k | Outscale ★ | Telstra ★ |
📡 Plumber Radar ➡️ 20k public repos scanned
Plumber scans CI/CD pipelines for risky patterns and security gaps.
.github/workflows/*.{yml,yaml} and repository settings..gitlab-ci.yml, resolved includes, and repository settings.Findings are reported in the terminal, JSON, SARIF, GitLab SAST, CSV, OCSF, PBOM, and CycloneDX.
| Use it for | |
|---|---|
| 💻 Run locally | Trying Plumber, or auditing repos from a script |
| 🐙 Run in GitHub Actions | Checks on every PR and push, findings in Code Scanning |
| 🦊 Run in GitLab CI | Checks on every pipeline, findings in the MR widget |
brew tap getplumber/plumber
brew trust --formula getplumber/plumber/plumber
brew install plumber
plumber analyze
No config file is needed: plumber analyze runs with the built-in default configuration (defaultConfig/.plumber.yaml) and auto-detects the provider from your git remote.
Other options besides Homebrew:
mise use -g github:getplumber/plumberdocker.io/getplumber/plumber on Docker Hubbrew upgrade plumber
# or
mise upgrade github:getplumber/plumber
A mise use install pins the version it resolved at install time, so an upgrade is an explicit step. mise also ignores a release younger than 24 hours by default (minimum_release_age), so a release of the day arrives the next day unless you set MISE_MINIMUM_RELEASE_AGE=0. plumber version prints an upgrade notice, with the command for your install, whenever a newer release exists.
# GitHub, using the gh CLI keyring
gh auth login
# or, for CI runners and automation
export GH_TOKEN=ghp_xxxx
# GitLab
export GITLAB_TOKEN=glpat_xxxx
# current repo
plumber analyze
# a GitHub repo without a local clone
plumber analyze github.com/owner/repo
# a GitLab project without a local clone (self-hosted instances work too)
plumber analyze gitlab.com/group/project
The target can also be a full URL pasted from the browser (https://github.com/owner/repo/tree/main selects the branch). Run plumber analyze --help for the full flag list.
Add the official Plumber action to .github/workflows/plumber.yml:
name: Plumber
on:
pull_request:
push:
branches: [main]
permissions:
contents: read
actions: read # lets Plumber read workflow states and one run log to prove the token scope
security-events: write
id-token: write # required by score-push
jobs:
plumber:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
- uses: getplumber/plumber@<version>
with:
# Publishes your Plumber Score and repository name publicly on
# score.getplumber.io (see Score badge below). Set to false to keep them private.
score-push: true
Full guide: getplumber.io/docs/cli/github#run-with-github-actions
Add the official Plumber component to .gitlab-ci.yml:
include:
- component: gitlab.com/getplumber/plumber/plumber@<version>
inputs:
# Publishes your Plumber Score and repository name publicly on
# score.getplumber.io (see Score badge below). Set to false to keep them private.
score_push: true
Add GITLAB_TOKEN in Settings -> CI/CD -> Variables: plumber environment, read_api + read_repository for scanning, or api if you want Plumber to post MR comments or badges.
Full guide: getplumber.io/docs/cli/gitlab#run-with-the-gitlab-ci-component
Self-hosted GitLab: host or mirror the component in your instance and include that URL. Guide.
The score follows attack paths, not issue counts: see the scoring docs.
The badge at the top of this README comes from the hosted score service. With score push on (score-push: true on the Action, score_push: true on the component, as in the snippets above), each run on the default branch keeps an A-E badge for your repo up to date:
[](https://score.getplumber.io/github.com/OWNER/REPO)
For a GitLab project, use gitlab.com/GROUP/PROJECT in place of github.com/OWNER/REPO.
Score push makes your score and repository name public, works in CI only, and is off by default when the input is omitted. See the score docs.
Plumber reads .plumber.yaml; without one, the built-in default applies.
plumber config init # create one interactively
plumber config generate # write the full commented default template
plumber config validate
plumber explain ISSUE-411
Example:
version: "2.0"
github:
controls:
actionsMustBePinnedByCommitSha:
enabled: true
trustedOwners:
- actions
- github
gitlab:
controls:
containerImageMustNotUseForbiddenTags:
enabled: true
Extend the baseline with extends: plumber:default and list only what you change; new controls Plumber ships then appear automatically. Full reference: defaultConfig/.plumber.yaml and getplumber.io/docs/cli.
A few of the checks Plumber runs:
curl | bash and similar, running code nobody reviewed.| Output | Flag | Use it for |
|---|---|---|
| Terminal | default | Human review during local or CI runs |
| JSON | --output results.json | Automation and dashboards |
| SARIF | --sarif results.sarif | GitHub Code Scanning and SARIF tools |
| GitLab SAST | --glsast gl-sast-report.json | GitLab Security Dashboard / MR widget |
| CSV | --csv results.csv | Spreadsheets, ad-hoc analysis |
| OCSF | --ocsf plumber.ocsf.json | OCSF consumers and GRC platforms |
| PBOM | --pbom pbom.json | Pipeline inventory |
| CycloneDX | --pbom-cyclonedx cdx.json | SBOM tooling |
| Code | Meaning |
|---|---|
0 | Score meets the gate (--min-points / --min-score), or --no-controls was used and collection succeeded |
1 | Score is below the gate |
2 | Invalid usage or configuration, or a runtime / provider / auth / network failure |
3 | Data collection was incomplete, so the score is withheld; or a check could not be verified and --fail-warnings is set (e.g. an action version that could not be resolved) |
make build
make test
Contributing guide: CONTRIBUTING.md
SECURITY.mdPlumber is licensed under the Mozilla Public License 2.0.
Overall
Maintained
Code review
Contributors
Go module
Latest release
Releases this year
353 followers · starred Sep 2026
103 followers · starred Mar 2026
23 followers · starred Jun 2026
635 followers · starred Jun 2026
Plumber detects CI/CD security issues in your GitHub workflows and gives you a score
See the code
CI/CD security scanner for GitHub Actions and GitLab CI
Securing the workflows of:
Lightpanda ★ 36k | Delve ★ 24.9k | Duplicati ★ 15k | Resty ★ 11.8k | nginx-ui ★ 11.5k | Bunkerity ★ 11k | Stellarium ★ 10k | intuitem ★ 4.4k | Outscale ★ | Telstra ★ |
📡 Plumber Radar ➡️ 20k public repos scanned
Plumber scans CI/CD pipelines for risky patterns and security gaps.
.github/workflows/*.{yml,yaml} and repository settings..gitlab-ci.yml, resolved includes, and repository settings.Findings are reported in the terminal, JSON, SARIF, GitLab SAST, CSV, OCSF, PBOM, and CycloneDX.
| Use it for | |
|---|---|
| 💻 Run locally | Trying Plumber, or auditing repos from a script |
| 🐙 Run in GitHub Actions | Checks on every PR and push, findings in Code Scanning |
| 🦊 Run in GitLab CI | Checks on every pipeline, findings in the MR widget |
brew tap getplumber/plumber
brew trust --formula getplumber/plumber/plumber
brew install plumber
plumber analyze
No config file is needed: plumber analyze runs with the built-in default configuration (defaultConfig/.plumber.yaml) and auto-detects the provider from your git remote.
Other options besides Homebrew:
mise use -g github:getplumber/plumberdocker.io/getplumber/plumber on Docker Hubbrew upgrade plumber
# or
mise upgrade github:getplumber/plumber
A mise use install pins the version it resolved at install time, so an upgrade is an explicit step. mise also ignores a release younger than 24 hours by default (minimum_release_age), so a release of the day arrives the next day unless you set MISE_MINIMUM_RELEASE_AGE=0. plumber version prints an upgrade notice, with the command for your install, whenever a newer release exists.
# GitHub, using the gh CLI keyring
gh auth login
# or, for CI runners and automation
export GH_TOKEN=ghp_xxxx
# GitLab
export GITLAB_TOKEN=glpat_xxxx
# current repo
plumber analyze
# a GitHub repo without a local clone
plumber analyze github.com/owner/repo
# a GitLab project without a local clone (self-hosted instances work too)
plumber analyze gitlab.com/group/project
The target can also be a full URL pasted from the browser (https://github.com/owner/repo/tree/main selects the branch). Run plumber analyze --help for the full flag list.
Add the official Plumber action to .github/workflows/plumber.yml:
name: Plumber
on:
pull_request:
push:
branches: [main]
permissions:
contents: read
actions: read # lets Plumber read workflow states and one run log to prove the token scope
security-events: write
id-token: write # required by score-push
jobs:
plumber:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
- uses: getplumber/plumber@<version>
with:
# Publishes your Plumber Score and repository name publicly on
# score.getplumber.io (see Score badge below). Set to false to keep them private.
score-push: true
Full guide: getplumber.io/docs/cli/github#run-with-github-actions
Add the official Plumber component to .gitlab-ci.yml:
include:
- component: gitlab.com/getplumber/plumber/plumber@<version>
inputs:
# Publishes your Plumber Score and repository name publicly on
# score.getplumber.io (see Score badge below). Set to false to keep them private.
score_push: true
Add GITLAB_TOKEN in Settings -> CI/CD -> Variables: plumber environment, read_api + read_repository for scanning, or api if you want Plumber to post MR comments or badges.
Full guide: getplumber.io/docs/cli/gitlab#run-with-the-gitlab-ci-component
Self-hosted GitLab: host or mirror the component in your instance and include that URL. Guide.
The score follows attack paths, not issue counts: see the scoring docs.
The badge at the top of this README comes from the hosted score service. With score push on (score-push: true on the Action, score_push: true on the component, as in the snippets above), each run on the default branch keeps an A-E badge for your repo up to date:
[](https://score.getplumber.io/github.com/OWNER/REPO)
For a GitLab project, use gitlab.com/GROUP/PROJECT in place of github.com/OWNER/REPO.
Score push makes your score and repository name public, works in CI only, and is off by default when the input is omitted. See the score docs.
Plumber reads .plumber.yaml; without one, the built-in default applies.
plumber config init # create one interactively
plumber config generate # write the full commented default template
plumber config validate
plumber explain ISSUE-411
Example:
version: "2.0"
github:
controls:
actionsMustBePinnedByCommitSha:
enabled: true
trustedOwners:
- actions
- github
gitlab:
controls:
containerImageMustNotUseForbiddenTags:
enabled: true
Extend the baseline with extends: plumber:default and list only what you change; new controls Plumber ships then appear automatically. Full reference: defaultConfig/.plumber.yaml and getplumber.io/docs/cli.
A few of the checks Plumber runs:
curl | bash and similar, running code nobody reviewed.| Output | Flag | Use it for |
|---|---|---|
| Terminal | default | Human review during local or CI runs |
| JSON | --output results.json | Automation and dashboards |
| SARIF | --sarif results.sarif | GitHub Code Scanning and SARIF tools |
| GitLab SAST | --glsast gl-sast-report.json | GitLab Security Dashboard / MR widget |
| CSV | --csv results.csv | Spreadsheets, ad-hoc analysis |
| OCSF | --ocsf plumber.ocsf.json | OCSF consumers and GRC platforms |
| PBOM | --pbom pbom.json | Pipeline inventory |
| CycloneDX | --pbom-cyclonedx cdx.json | SBOM tooling |
| Code | Meaning |
|---|---|
0 | Score meets the gate (--min-points / --min-score), or --no-controls was used and collection succeeded |
1 | Score is below the gate |
2 | Invalid usage or configuration, or a runtime / provider / auth / network failure |
3 | Data collection was incomplete, so the score is withheld; or a check could not be verified and --fail-warnings is set (e.g. an action version that could not be resolved) |
make build
make test
Contributing guide: CONTRIBUTING.md
SECURITY.mdPlumber is licensed under the Mozilla Public License 2.0.
Overall
Maintained
Code review
Contributors
Go module
Latest release
Releases this year
353 followers · starred Sep 2026
103 followers · starred Mar 2026
23 followers · starred Jun 2026
635 followers · starred Jun 2026