Most GitLab linters tell you if your YAML is valid. Very few tell you if your YAML is dangerous.
It's easy to feel safe when you see a green build, but a "successful" pipeline can still have major governance gaps. A green checkmark won't tell you if:
- Your pipeline uses mutable
:latesttags or untrusted registries. - A developer accidentally disabled a security job with
allow_failure: true. - Your "protected" branch settings are actually misconfigured.
- Sensitive variables are being leaked via
CI_DEBUG_TRACE.
We built Plumber to bridge the gap between "valid syntax" and "secure configuration." It's an open-source CLI that checks both your .gitlab-ci.yml and your GitLab project settings to see if they meet your organization's compliance standards.
Beyond Linting: The PBOM
One of the most powerful features is the Pipeline Bill of Materials (PBOM). Plumber can export a CycloneDX SBOM specifically for your CI/CD. This gives you a machine-readable inventory of every container image, component, and remote template your pipeline touches, essential for auditing your software supply chain.
What it enforces
Plumber ships with a default policy you can customize in .plumber.yaml. It covers:
- Image Governance: Allowed registries, forbidden tags, and mandatory digest pinning.
-
Pipeline Integrity: Ensures includes are up-to-date and prevents the use of forbidden versions (like
mainorHEAD). - Security Hardening: Detects Docker-in-Docker (DinD) usage on shared runners and prevents unsafe variable expansion (mapped to OWASP CICD-SEC-1).
- Setting Verification: Confirms branch protection is active and sensitive variable overrides are blocked.
More controls are available and documented at getplumber.io/docs/cli#available-controls.
What the output looks like
Running plumber analyze gives you a detailed, actionable report right in your terminal:
Analyzing project: mygroup/my-api
Branch: main
Config: .plumber.yaml
CONTROLS
✓ containerImageMustNotUseForbiddenTags
✗ containerImageMustComeFromAuthorizedSources
• job "build": image "node:18" is not from an authorized registry
• job "test": image "python:3.11" is not from an authorized registry
✓ branchMustBeProtected
✗ pipelineMustNotIncludeHardcodedJobs
• job "lint" is hardcoded and not sourced from an include or component
✓ includesMustBeUpToDate
✗ includesMustNotUseForbiddenVersions
• include "gitlab.com/myorg/templates/security@main" uses forbidden version "main"
✓ pipelineMustIncludeComponent
✓ pipelineMustIncludeTemplate
SUMMARY
Controls passed: 5 / 8
Compliance score: 62%
Threshold: 100%
Status: FAILED
Report written to: plumber-report.json
The team gets a clear score plus exact failing controls. Findings are actionable and mapped to docs. plumber-report.json can be archived in CI for audits and trend tracking.
Example 1: Install and first run
Install (via Homebrew):
brew tap getplumber/plumber
brew install plumber
Token Setup: Create a GitLab token with read_api and read_repository.
Note: The token needs Maintainer permissions. GitLab restricts access to branch protection and repository settings to Maintainer roles or higher, and Plumber needs this visibility to verify your project posture.
export GITLAB_TOKEN=glpat_xxxxxxxxxxxxxxxxxxxx
Generate and Analyze:
# Create a local .plumber.yaml to customize your rules
plumber config generate
# Run the analysis
plumber analyze
Tip: Plumber auto-detects your GitLab URL and project from your git remote.
Example 2: Local vs. Remote Analysis
A key technical advantage is that Plumber uses your local .gitlab-ci.yml but compares it against remote project settings. This allows you to catch security regressions before you even git push.
If you want to focus on specific issues (e.g., during a migration), you can target specific controls:
plumber analyze --controls containerImageMustNotUseForbiddenTags,branchMustBeProtected
Example 3: Continuous Feedback in GitLab CI
You can automate these checks by adding the Plumber component to your pipeline. This provides feedback directly in your Merge Requests.
workflow:
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
when: never
- if: $CI_COMMIT_BRANCH
- if: $CI_COMMIT_TAG
include:
- component: gitlab.com/getplumber/plumber/plumber@v0.1.30
inputs:
mr_comment: true
badge: true
With mr_comment: true, Plumber will post results as a comment on the MR, ensuring no one merges a pipeline that weakens your security posture.
Why use a Linter for Governance?
Plumber is about policy enforcement. While scanners like Trivy or Grype look for vulnerabilities inside your code and images, Plumber ensures the framework holding your CI/CD together is solid. It ensures your security scanners are actually running and that your repository settings aren't leaving the back door open.
Feedback
If you maintain GitLab at scale, what would you want a pipeline linter to enforce that generic CI UI or scanners do not cover well? I am especially curious about policy ideas that fit real-world developer workflows.
Links
- Repo: github.com/getplumber/plumber
- Docs: getplumber.io/docs/cli
- Component: GitLab CI Catalog
Top comments (0)