getplumber/plumber

Plumber detects CI/CD security issues in your GitHub workflows and gives you a score

Go

828

846 commits

updated Oct 19, 2026

See the code

README

Plumber

Plumber Score OpenSSF Scorecard SLSA 3 Latest Release Docker Pulls

CI/CD security scanner for GitHub Actions and GitLab CI

Securing the workflows of:

Lightpanda
Lightpanda
★ 36k
Delve
Delve
★ 24.9k
Duplicati
Duplicati
★ 15k
Resty
Resty
★ 11.8k
nginx-ui
nginx-ui
★ 11.5k
Bunkerity
Bunkerity
★ 11k
Stellarium
Stellarium
★ 10k
intuitem
intuitem
★ 4.4k
Outscale
Outscale
★  
Telstra
Telstra
★  

📡 Plumber Radar ➡️ 20k public repos scanned


What is Plumber?

Plumber scans CI/CD pipelines for risky patterns and security gaps.

  • GitHub Actions: scans .github/workflows/*.{yml,yaml} and repository settings.
  • GitLab CI: scans .gitlab-ci.yml, resolved includes, and repository settings.

Findings are reported in the terminal, JSON, SARIF, GitLab SAST, CSV, OCSF, PBOM, and CycloneDX.

plumber analyze scanning a repository

Where to run it

Use it for
💻 Run locallyTrying Plumber, or auditing repos from a script
🐙 Run in GitHub ActionsChecks on every PR and push, findings in Code Scanning
🦊 Run in GitLab CIChecks on every pipeline, findings in the MR widget

Quick start

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.

Install

Other options besides Homebrew:

Upgrade

brew 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.

Authenticate

# 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

Run

# 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.

GitHub Action

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

GitLab CI component

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.

Score badge

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:

[![Plumber Score](https://score.getplumber.io/github.com/OWNER/REPO.svg)](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.

Configuration

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.

Controls

A few of the checks Plumber runs:

  • Unpinned actions and images: a third-party action or container image referenced by a tag that can be moved to other code.
  • Remote scripts piped into a shell: curl | bash and similar, running code nobody reviewed.
  • Unprotected default branch: anyone with push access can change what gets built and released.
  • See all controls

Outputs

OutputFlagUse it for
TerminaldefaultHuman review during local or CI runs
JSON--output results.jsonAutomation and dashboards
SARIF--sarif results.sarifGitHub Code Scanning and SARIF tools
GitLab SAST--glsast gl-sast-report.jsonGitLab Security Dashboard / MR widget
CSV--csv results.csvSpreadsheets, ad-hoc analysis
OCSF--ocsf plumber.ocsf.jsonOCSF consumers and GRC platforms
PBOM--pbom pbom.jsonPipeline inventory
CycloneDX--pbom-cyclonedx cdx.jsonSBOM tooling

Exit codes

CodeMeaning
0Score meets the gate (--min-points / --min-score), or --no-controls was used and collection succeeded
1Score is below the gate
2Invalid usage or configuration, or a runtime / provider / auth / network failure
3Data 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)

Contributing

make build
make test

Contributing guide: CONTRIBUTING.md

Resources

License

Plumber is licensed under the Mozilla Public License 2.0.

cicd
compliance
pipeline
security

getplumber/plumber

Plumber detects CI/CD security issues in your GitHub workflows and gives you a score

Go

828

846 commits

updated Oct 19, 2026

See the code

README

Plumber

Plumber Score OpenSSF Scorecard SLSA 3 Latest Release Docker Pulls

CI/CD security scanner for GitHub Actions and GitLab CI

Securing the workflows of:

Lightpanda
Lightpanda
★ 36k
Delve
Delve
★ 24.9k
Duplicati
Duplicati
★ 15k
Resty
Resty
★ 11.8k
nginx-ui
nginx-ui
★ 11.5k
Bunkerity
Bunkerity
★ 11k
Stellarium
Stellarium
★ 10k
intuitem
intuitem
★ 4.4k
Outscale
Outscale
★  
Telstra
Telstra
★  

📡 Plumber Radar ➡️ 20k public repos scanned


What is Plumber?

Plumber scans CI/CD pipelines for risky patterns and security gaps.

  • GitHub Actions: scans .github/workflows/*.{yml,yaml} and repository settings.
  • GitLab CI: scans .gitlab-ci.yml, resolved includes, and repository settings.

Findings are reported in the terminal, JSON, SARIF, GitLab SAST, CSV, OCSF, PBOM, and CycloneDX.

plumber analyze scanning a repository

Where to run it

Use it for
💻 Run locallyTrying Plumber, or auditing repos from a script
🐙 Run in GitHub ActionsChecks on every PR and push, findings in Code Scanning
🦊 Run in GitLab CIChecks on every pipeline, findings in the MR widget

Quick start

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.

Install

Other options besides Homebrew:

Upgrade

brew 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.

Authenticate

# 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

Run

# 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.

GitHub Action

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

GitLab CI component

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.

Score badge

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:

[![Plumber Score](https://score.getplumber.io/github.com/OWNER/REPO.svg)](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.

Configuration

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.

Controls

A few of the checks Plumber runs:

  • Unpinned actions and images: a third-party action or container image referenced by a tag that can be moved to other code.
  • Remote scripts piped into a shell: curl | bash and similar, running code nobody reviewed.
  • Unprotected default branch: anyone with push access can change what gets built and released.
  • See all controls

Outputs

OutputFlagUse it for
TerminaldefaultHuman review during local or CI runs
JSON--output results.jsonAutomation and dashboards
SARIF--sarif results.sarifGitHub Code Scanning and SARIF tools
GitLab SAST--glsast gl-sast-report.jsonGitLab Security Dashboard / MR widget
CSV--csv results.csvSpreadsheets, ad-hoc analysis
OCSF--ocsf plumber.ocsf.jsonOCSF consumers and GRC platforms
PBOM--pbom pbom.jsonPipeline inventory
CycloneDX--pbom-cyclonedx cdx.jsonSBOM tooling

Exit codes

CodeMeaning
0Score meets the gate (--min-points / --min-score), or --no-controls was used and collection succeeded
1Score is below the gate
2Invalid usage or configuration, or a runtime / provider / auth / network failure
3Data 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)

Contributing

make build
make test

Contributing guide: CONTRIBUTING.md

Resources

License

Plumber is licensed under the Mozilla Public License 2.0.

cicd
compliance
pipeline
security

Project health

Overall

8.7/10

Maintained

10/10

Code review

2/10

Contributors

10/10
OpenSSF Scorecard

Latest release

v0.7.4 · Oct 18, 2026

Releases this year

308

Significant stargazers

David Berenstein

353 followers · starred Sep 2026

Erik Martin-Dorel

103 followers · starred Mar 2026

Pi3r

23 followers · starred Jun 2026

Florian BRUNIAUX

635 followers · starred Jun 2026