CI/CD compliance scanner for GitLab pipelines
Website β’ Discord β’ Issues
Plumber is a compliance scanner for GitLab. It reads your .gitlab-ci.yml and repository settings, then checks for security and compliance issues like:
- Container images using mutable tags (
latest,dev) - Container images from untrusted registries
- Unprotected branches
- Hardcoded jobs not from includes/components
- Outdated includes/templates
- Forbidden version patterns (e.g.,
main,HEAD) - Missing required components or templates
How does it work? Plumber connects to your GitLab instance via API, analyzes your pipeline configuration, and reports any issues it finds. You define what's allowed in a config file (.plumber.yaml), and Plumber tells you if your project complies.
π‘ Want to see it in action? Check out our example projects:
- go-build-test-compliant - A compliant project passing all checks
- go-build-test-non-compliant - A non-compliant project showing detected issues
Choose one of these methods. You don't need both:
| Method | Best for | How it works |
|---|---|---|
| CLI | Quick evaluation, local testing, one-off scans | Install binary and run from terminal |
| GitLab CI Component | Automated checks on every pipeline run | Add 2 lines to your .gitlab-ci.yml |
- What is Plumber?
- CLI
- GitLab CI Component
- Configuration
- Installation
- CLI Reference
- Self-Hosted GitLab
- Troubleshooting
Try Plumber in 2 minutes! No commits, no CI changes, just run it.
Choose one of the following:
brew tap getplumber/plumber
brew install plumbermise use -g github:getplumber/plumberRequires mise activation in your shell, or run with
mise exec -- plumber.
# For Linux/MacOs
curl -LO "https://github.com/getplumber/plumber/releases/latest/download/plumber-$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m | sed 's/x86_64/amd64/' | sed 's/aarch64/arm64/')"
chmod +x plumber-* && sudo mv plumber-* /usr/local/bin/plumberπ¦ See Installation for Windows, Docker, or building from source.
plumber config generateThis creates .plumber.yaml with default compliance rules. You can customize it later.
- In GitLab, go to User Settings β Access Tokens (direct link)
- Create a Personal Access Token with
read_api+read_repositoryscopes - Export it in your terminal:
β οΈ Important: The token must belong to a user with Maintainer role (or higher) on the project to access branch protection settings and other project configurations.
export GITLAB_TOKEN=glpat-xxxxPlumber auto-detects the GitLab URL and project from your git remote but requires the remote to be set to 'origin'.
# if in git remote with remote = origin, run:
plumber analyze
# Or specify the project explicitly:
plumber analyze --gitlab-url https://gitlab.com --project mygroup/myprojectIt reads your .plumber.yaml config and outputs a compliance report. You can also tell it to store the output in JSON format with the --output flag.
π‘ Like what you see? Add Plumber to your CI/CD with the GitLab CI Component for automated checks on every pipeline.
Add Plumber to your GitLab pipeline: it will run automatically on the default branch, tags and open merge requests.
π¬ These instructions are for gitlab.com. Self-hosted? See Self-Hosted GitLab.
- In GitLab, go to User Settings β Access Tokens (or create one here)
- Create a Personal Access Token with
read_api+read_repositoryscopes - Go to your project's Settings β CI/CD β Variables
- Add the token as
GITLAB_TOKEN(masked recommended)
β οΈ Important: The token must belong to a user with Maintainer role (or higher) on the project to access branch protection settings and other project configurations.
Add this to your .gitlab-ci.yml:
include:
- component: gitlab.com/getplumber/plumber/plumber@v0.1.21- Get the latest version from the Catalog
That's it! Plumber will now run on every pipeline and report compliance issues.
π‘ Want to customize? See Configuration to set thresholds, enable/disable controls, and whitelist trusted images.
Override any input to fit your needs:
include:
- component: gitlab.com/getplumber/plumber/plumber@v0.1.21
inputs:
threshold: 80 # Minimum % to pass (default: 100)
config_file: configs/my-plumber.yaml # Custom config path
server_url: https://gitlab.example.com # Self-hosted GitLab
branch: develop # Specific branch to analyze
verbose: true # Debug outputπ¦ Find the latest version on the GitLab CI/CD Catalog
All available inputs
| Input | Default | Description |
|---|---|---|
server_url |
$CI_SERVER_URL |
GitLab instance URL |
project_path |
$CI_PROJECT_PATH |
Project to analyze |
branch |
$CI_COMMIT_REF_NAME |
Branch to analyze |
gitlab_token |
$GITLAB_TOKEN |
GitLab API token (requires read_api + read_repository) |
threshold |
100 |
Minimum compliance % to pass |
config_file |
(auto-detect) | Path to config file (relative to repo root) |
output_file |
plumber-report.json |
Path to write JSON results |
print_output |
true |
Print text output to stdout |
stage |
.pre |
Pipeline stage for the job |
image |
getplumber/plumber:0.1 |
Docker image to use |
allow_failure |
false |
Allow job to fail without blocking |
verbose |
false |
Enable debug output |
Generate a default configuration file with:
plumber config generate
Flags:
-f, --force Overwrite existing file
-o, --output string Output file path (default ".plumber.yaml")This creates .plumber.yaml with sensible defaults. Customize it to fit your needs.
Plumber includes 8 compliance controls. Each can be enabled/disabled and customized in .plumber.yaml:
1. Container images must not use forbidden tags
Detects container images using mutable tags that can change unexpectedly.
containerImageMustNotUseForbiddenTags:
enabled: true
tags:
- latest
- dev
- development
- staging
- main
- master2. Container images must come from authorized sources
Ensures container images come from trusted registries only.
containerImageMustComeFromAuthorizedSources:
enabled: true
trustDockerHubOfficialImages: true
trustedUrls:
- docker.io/docker:*
- gcr.io/kaniko-project/*
- $CI_REGISTRY_IMAGE:*
- $CI_REGISTRY_IMAGE/*
- getplumber/plumber:*
- docker.io/getplumber/plumber:*
- registry.gitlab.com/security-products/*3. Branch must be protected
Verifies that critical branches have proper protection settings.
branchMustBeProtected:
enabled: true
defaultMustBeProtected: true
namePatterns:
- main
- master
- release/*
- production
- dev
allowForcePush: false
codeOwnerApprovalRequired: false
minMergeAccessLevel: 30 # Developer
minPushAccessLevel: 40 # Maintainer4. Pipeline must not include hardcoded jobs
Detects jobs defined directly in .gitlab-ci.yml instead of coming from includes/components.
pipelineMustNotIncludeHardcodedJobs:
enabled: true5. Includes must be up to date
Checks if included templates/components have newer versions available.
includesMustBeUpToDate:
enabled: true6. Includes must not use forbidden versions
Prevents use of mutable version references for includes that can change unexpectedly.
includesMustNotUseForbiddenVersions:
enabled: true
forbiddenVersions:
- latest
- "~latest"
- main
- master
- HEAD
defaultBranchIsForbiddenVersion: false7. Pipeline must include component
Ensures required GitLab CI/CD components are included in the pipeline.
There are two ways to define requirements (use one, not both):
Expression syntax β a natural boolean expression using AND, OR, and parentheses:
pipelineMustIncludeComponent:
enabled: true
# AND binds tighter than OR, so "a AND b OR c" means "(a AND b) OR c"
required: components/sast/sast AND components/secret-detection/secret-detection
# With alternatives:
# required: (components/sast/sast AND components/secret-detection/secret-detection) OR your-org/full-security/full-securityArray syntax β a list of groups using "OR of ANDs" logic:
pipelineMustIncludeComponent:
enabled: true
# Outer array = OR (at least one group must be satisfied)
# Inner array = AND (all components in group must be present)
requiredGroups:
- ["components/sast/sast", "components/secret-detection/secret-detection"]
- ["your-org/full-security/full-security"]8. Pipeline must include template
Ensures required templates (project includes) are present in the pipeline.
There are two ways to define requirements (use one, not both):
Expression syntax β a natural boolean expression using AND, OR, and parentheses:
pipelineMustIncludeTemplate:
enabled: true
required: templates/go/go AND templates/trivy/trivy AND templates/iso27001/iso27001
# With alternatives:
# required: (templates/go/go AND templates/trivy/trivy) OR templates/full-go-pipelineArray syntax β a list of groups using "OR of ANDs" logic:
pipelineMustIncludeTemplate:
enabled: true
requiredGroups:
- ["templates/go/go", "templates/trivy/trivy", "templates/iso27001/iso27001"]
- ["templates/full-go-pipeline"]By default, Plumber prints a colorized detail and a summary of a project's compliance
- You can disable the printing via the config flags
You can also configure Plumber to output a json file with the results.
- This is enabled by default if using Plumber Component in Gitlab
Plumber provides colorized terminal output for easy scanning:
- Green checkmarks (β) indicate passing controls
- Red crosses (β) indicate failing controls
- Yellow bullets (β’) highlight specific issues found
- Summary tables show compliance percentages at a glance
brew tap getplumber/plumber
brew install plumberTo install a specific version:
brew install getplumber/plumber/plumber@0.1.26Note: Versioned formulas are keg-only. Use the full path for example
/usr/local/opt/plumber@0.1.26/bin/plumberor runbrew link plumber@0.1.26to add it to your PATH.
mise use -g github:getplumber/plumberRequires mise activation in your shell, or run with
mise exec -- plumber.
Linux (amd64)
curl -LO https://github.com/getplumber/plumber/releases/latest/download/plumber-linux-amd64
chmod +x plumber-linux-amd64
sudo mv plumber-linux-amd64 /usr/local/bin/plumberLinux (arm64)
curl -LO https://github.com/getplumber/plumber/releases/latest/download/plumber-linux-arm64
chmod +x plumber-linux-arm64
sudo mv plumber-linux-arm64 /usr/local/bin/plumbermacOS (Apple Silicon)
curl -LO https://github.com/getplumber/plumber/releases/latest/download/plumber-darwin-arm64
chmod +x plumber-darwin-arm64
sudo mv plumber-darwin-arm64 /usr/local/bin/plumbermacOS (Intel)
curl -LO https://github.com/getplumber/plumber/releases/latest/download/plumber-darwin-amd64
chmod +x plumber-darwin-amd64
sudo mv plumber-darwin-amd64 /usr/local/bin/plumberWindows (PowerShell)
Invoke-WebRequest -Uri https://github.com/getplumber/plumber/releases/latest/download/plumber-windows-amd64.exe -OutFile plumber.exeVerify checksum
curl -LO https://github.com/getplumber/plumber/releases/latest/download/checksums.txt
sha256sum -c checksums.txt --ignore-missingdocker pull getplumber/plumber:latest
docker run --rm \
-e GITLAB_TOKEN=glpat-xxxx \
getplumber/plumber:latest analyze \
--gitlab-url https://your-gitlab-instance.com \
--project mygroup/myprojectRequires Go 1.24+ and Make.
git clone https://github.com/getplumber/plumber.git
cd plumber
make build # or make install to build and copy to /usr/local/bin/Run compliance analysis on a GitLab project.
plumber analyze [flags]| Flag | Required | Default | Description |
|---|---|---|---|
--gitlab-url |
No* | auto-detect | GitLab instance URL |
--project |
No* | auto-detect | Project path (e.g., group/project) |
--config |
No | .plumber.yaml |
Path to config file |
--threshold |
No | 100 |
Minimum compliance % to pass (0-100) |
--branch |
No | default | Branch to analyze |
--output |
No | β | Write JSON results to file |
--print |
No | true |
Print text output to stdout |
* Auto-detected from git remote (
origin) if not specified. Supports both SSH and HTTPS remote URLs.
| Variable | Required | Description |
|---|---|---|
GITLAB_TOKEN |
Yes | GitLab API token with read_api + read_repository scopes (from a Maintainer or higher) |
| Code | Meaning |
|---|---|
0 |
Compliance β₯ threshold |
1 |
Compliance < threshold or error |
Generate a default .plumber.yaml configuration file.
plumber config generate [flags]| Flag | Default | Description |
|---|---|---|
--output, -o |
.plumber.yaml |
Output file path |
--force, -f |
false |
Overwrite existing file |
Examples:
# Generate default config
plumber config generate
# Custom filename
plumber config generate --output my-plumber.yaml
# Overwrite existing conf file
plumber config generate --forceDisplay a clean, human-readable view of the effective configuration without comments.
plumber config view [flags]| Flag | Default | Description |
|---|---|---|
--config, -c |
.plumber.yaml |
Path to configuration file |
--no-color |
false |
Disable colorized output |
Booleans are colorized for quick scanning: true in green, false in red. Color is automatically disabled when piping output.
Examples:
# View the default .plumber.yaml
plumber config view
# View a specific config file
plumber config view --config custom-plumber.yaml
# View without colors (for piping or scripts)
plumber config view --no-colorIf you're running a self-hosted GitLab instance, you'll need to host your own copy of the component.
Step-by-step setup
Step 1: Import the repository
- Go to New Project β Import project β Repository by URL
- URL:
https://gitlab.com/getplumber/plumber.git - Choose a group/project name (e.g.,
infrastructure/plumber)
Step 2: Enable CI/CD Catalog
- Go to Settings β General
- Make sure the project has a description (required for CI/CD Catalog)
- Expand Visibility, project features, permissions
- Toggle CI/CD Catalog resource to enabled
- Click Save changes
Step 3: Create a release
- Go to Code β Tags β New tag
- Enter a version (e.g.,
1.0.0) - Click Create tag
Step 4: Create a GitLab Token
In the project you want to scan:
- Go to User Settings β Access Tokens on your GitLab instance
- Create a Personal Access Token with
read_api+read_repositoryscopes - Go to the project's Settings β CI/CD β Variables
- Add the token as
GITLAB_TOKEN(masked recommended)
β οΈ The token must belong to a user with Maintainer role (or higher) on the project.
Step 5: Use in your pipelines
include:
- component: gitlab.example.com/infrastructure/plumber/plumber@v1.0.0π‘ Format:
<your-gitlab-host>/<project-path>/plumber@<tag>
| Issue | Solution |
|---|---|
GITLAB_TOKEN environment variable is required |
Set GITLAB_TOKEN in CI/CD Variables or export it locally |
401 Unauthorized |
Token needs read_api + read_repository scopes, from a Maintainer or higher |
403 Forbidden on MR settings |
Expected on non-Premium GitLab; continues without that data |
404 Not Found |
Verify project path and GitLab URL are correct |
| Configuration file not found | Use absolute path in Docker, relative path otherwise |
π‘ Need help? Open an issue or join our Discord
Contributions are welcome! Please read our Contributing Guide for details on how to submit pull requests, report issues, and coding conventions.

