-
Notifications
You must be signed in to change notification settings - Fork 11
Description
Is your feature request related to a problem? Please describe.
When a Plumber control fails, the CLI displays factual data (image name, tag, branch, job…) but no error code, no documentation link, and no remediation guidance. Users see something like:
• Job 'build' uses forbidden tag 'latest' (image: alpine:latest)
But they have to figure out on their own why this is a problem and how to fix it. There is no way to look up this specific issue in the documentation, no stable identifier to reference in team discussions or CI logs, and no URL pointing to a fix.
Compare with Popeye K8s, which assigns a numeric code to every finding (e.g., 100: Untagged docker image in use) and links to a codes reference page. This makes findings searchable, trackable, and actionable.
Describe the solution you'd like
1. Error codes for every control
Assign a stable, unique error code to each control. Format: PLB-XXXX where the prefix groups controls by category:
| Code | Control | Description |
|---|---|---|
PLB-1001 |
containerImageMustNotUseForbiddenTags |
Container image uses a forbidden/mutable tag |
PLB-1002 |
containerImageMustComeFromAuthorizedSources |
Container image from unauthorized registry |
PLB-1003 |
containerImageMustNotUseForbiddenTags (digest mode) |
Container image not pinned by digest |
PLB-2001 |
pipelineMustNotIncludeHardcodedJobs |
Job is hardcoded instead of included |
PLB-2002 |
includesMustBeUpToDate |
Include is outdated |
PLB-2003 |
includesMustNotUseForbiddenVersions |
Include uses a forbidden version/ref |
PLB-2004 |
pipelineMustIncludeComponent |
Required component is missing |
PLB-2005 |
pipelineMustIncludeComponent (overridden) |
Required component is overridden |
PLB-2006 |
pipelineMustIncludeTemplate |
Required template is missing |
PLB-2007 |
pipelineMustIncludeTemplate (overridden) |
Required template is overridden |
PLB-3001 |
branchMustBeProtected |
Branch is not protected |
PLB-3002 |
branchMustBeProtected (non-compliant) |
Branch protection settings non-compliant |
PLB-4001 |
pipelineMustNotEnableDebugTrace |
Debug trace variable enabled in pipeline |
2. Documentation URL per control
Add a helpUri field linking to the controls reference page on getplumber.io. Each control links to its specific anchor:
https://getplumber.io/docs/use-plumber/controls#containerImageMustNotUseForbiddenTags
This is also critical for the SARIF output feature (#91) which expects helpUri on each reportingDescriptor.
3. Terminal output enhancement
When displaying issues, include the error code and a clickable doc link:
Container Images — containerImageMustNotUseForbiddenTags
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Issues:
• [PLB-1001] Job 'build' uses forbidden tag 'latest' (image: alpine:latest)
• [PLB-1001] Job 'test' uses forbidden tag 'dev' (image: python:dev)
→ How to fix: https://getplumber.io/docs/use-plumber/controls#containerImageMustNotUseForbiddenTags
4. JSON output enhancement
Add code and helpUri to each issue in the JSON output:
{
"imageForbiddenTagsResult": {
"issues": [
{
"code": "PLB-1001",
"link": "alpine:latest",
"tag": "latest",
"job": "build",
"helpUri": "https://getplumber.io/docs/use-plumber/controls#containerImageMustNotUseForbiddenTags"
}
]
}
}Implementation Hints
These are just ideas. Feel free to change the implementation.
Data model changes (control/types.go)
Add a common interface or embed a base struct in each issue type:
// Base issue fields — embed in all issue structs
type IssueBase struct {
Code string `json:"code"`
HelpURI string `json:"helpUri"`
}
// Example usage
type GitlabPipelineImageIssueTag struct {
IssueBase
Link string `json:"link"`
Tag string `json:"tag"`
Job string `json:"job"`
}Error code registry (new file: control/codes.go)
Centralize all codes with their messages and doc URLs:
package control
const DocsBaseURL = "https://getplumber.io/docs/use-plumber/controls"
type ErrorCode struct {
Code string
Control string
Description string
HelpURI string
}
var Codes = map[string]ErrorCode{
"PLB-1001": {
Code: "PLB-1001",
Control: "containerImageMustNotUseForbiddenTags",
Description: "Container image uses a forbidden/mutable tag",
HelpURI: DocsBaseURL + "#containerImageMustNotUseForbiddenTags",
},
// ... etc
}Terminal output changes (cmd/analyze.go)
In outputText(), prefix each issue with its code and add the helpUri line after the issues list:
// Before (current):
fmt.Printf(" • Job '%s' uses forbidden tag '%s'\n", issue.Job, issue.Tag)
// After:
fmt.Printf(" • [%s] Job '%s' uses forbidden tag '%s'\n", issue.Code, issue.Job, issue.Tag)
// After all issues for this control:
fmt.Printf("\n → How to fix: %s\n", codes["PLB-1001"].HelpURI)Files touched
control/types.go— addIssueBasestruct, embed in all issue typescontrol/codes.go— (new) centralized error code registrycontrol/controlGitlabImageMutable.go— populateCodeandHelpURIwhen creating issuescontrol/controlGitlabImageUntrusted.go— samecontrol/controlGitlabProtectionBranchProtectionNotCompliant.go— samecontrol/controlGitlabPipelineOriginHardcodedJobs.go— samecontrol/controlGitlabPipelineOriginOutdated.go— samecontrol/controlGitlabPipelineOriginVersion.go— samecontrol/controlGitlabPipelineOriginRequiredComponents.go— samecontrol/controlGitlabPipelineOriginRequiredTemplates.go— samecontrol/controlGitlabPipelineDebugTrace.go— samecmd/analyze.go— updateoutputText()to display codes + doc links
Describe alternatives you've considered
- No codes, only doc links: Simpler, but codes make findings searchable in logs and CI artifacts (grep
PLB-1001) - Popeye-style numeric-only codes (100, 101...): Works but
PLB-XXXXis more readable and avoids collision with HTTP status codes or exit codes - Per-issue remediation text in CLI: Too verbose for terminal; better to link to the doc page which has full examples
Additional context
- Related documentation issue: [FEAT] Add remediation guidance to Controls documentation — error reference with fix instructions getplumber.io#76 (adds remediation guidance to the controls doc page — the target of
helpUrilinks) - Related SARIF issue: [FEAT] Add --format sarif output for integration with GitLab Security Dashboard #91 — SARIF
reportingDescriptorrequireshelpUriper rule, which this feature provides - The Popeye K8s model (codes.md) proves this pattern works at scale (60+ codes). Plumber has 9 controls / ~13 codes today — much more manageable
- Error codes also enable future features: filtering by code (
--skip-codes PLB-1001), Prometheus metrics per code, Grafana dashboards