Skip to content

[FEAT] Add error codes and documentation links to control findings (Popeye-style codes.md) #92

@stephrobert

Description

@stephrobert

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 — add IssueBase struct, embed in all issue types
  • control/codes.go — (new) centralized error code registry
  • control/controlGitlabImageMutable.go — populate Code and HelpURI when creating issues
  • control/controlGitlabImageUntrusted.go — same
  • control/controlGitlabProtectionBranchProtectionNotCompliant.go — same
  • control/controlGitlabPipelineOriginHardcodedJobs.go — same
  • control/controlGitlabPipelineOriginOutdated.go — same
  • control/controlGitlabPipelineOriginVersion.go — same
  • control/controlGitlabPipelineOriginRequiredComponents.go — same
  • control/controlGitlabPipelineOriginRequiredTemplates.go — same
  • control/controlGitlabPipelineDebugTrace.go — same
  • cmd/analyze.go — update outputText() 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-XXXX is 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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions