Skip to content

[FEAT] New control: pipelineMustNotOverrideSecurityVariables — detect SECURE_ANALYZERS_PREFIX override #83

@stephrobert

Description

@stephrobert

Is your feature request related to a problem? Please describe.

GitLab security scanners (SAST, DAST, Secret Detection, etc.) use predefined CI/CD variables to configure their behavior. An attacker who can modify the .gitlab-ci.yml can override these variables to:

  • Neutralize scanners: Set SECURE_ANALYZERS_PREFIX to a registry serving fake scanner images that always report "0 vulnerabilities"
  • Exclude paths from scanning: Set SAST_EXCLUDED_PATHS to cover the entire codebase
  • Disable scanners entirely: Set SECRET_DETECTION_DISABLED: "true" or SAST_DISABLED: "true"

This attack is particularly insidious because the pipeline continues to run and appears successful — security checks pass, but no actual scanning occurs.

Describe the solution you'd like

Add a new control pipelineMustNotOverrideSecurityVariables that:

  • Scans global variables: and per-job variables: for known GitLab security variables
  • Reports when these variables are redefined in the CI configuration file (they should only be set in GitLab CI/CD Settings as protected variables)
  • Provides a configurable list of protected variable names

Configuration in .plumber.yaml

controls:
  pipelineMustNotOverrideSecurityVariables:
    enabled: true
    # Variables that must not be defined in .gitlab-ci.yml
    # They should only be set in GitLab CI/CD Settings > Variables
    protectedVariables:
      - SECURE_ANALYZERS_PREFIX
      - SAST_DISABLED
      - SAST_EXCLUDED_PATHS
      - SAST_EXCLUDED_ANALYZERS
      - SECRET_DETECTION_DISABLED
      - SECRET_DETECTION_EXCLUDED_PATHS
      - CONTAINER_SCANNING_DISABLED
      - DAST_DISABLED
      - DEPENDENCY_SCANNING_DISABLED
      - LICENSE_SCANNING_DISABLED

Implementation Hints

These are just ideas. Feel free to change the implementation.

  1. Data source: The PipelineOriginData collector provides the merged CI config that contains all variables: blocks (global and per-job). The raw YAML may need to be inspected rather than the merged result, to distinguish variables set in the file vs. inherited from GitLab settings.
  2. New control file: Create control/controlGitlabPipelineSecurityVariables.go.
  3. Logic:
    • Parse the CI configuration's variables: sections (global + per-job)
    • For each variable, check if its key matches any entry in protectedVariables
    • If found, create an issue with the variable name, its value, and the location (global or job name)
  4. Compliance: 0% if any protected variable is overridden, 100% otherwise.
  5. Nuance: Some variables like SECURE_ANALYZERS_PREFIX might legitimately be set to the project's own registry. Consider allowing an allowedValues map for specific variables in a future iteration.

Files Touched

  • control/controlGitlabPipelineSecurityVariables.go (new control)
  • control/types.go (add result field to AnalysisResult)
  • control/task.go (wire the new control in RunAnalysis())
  • configuration/plumberconfig.go (add config struct and getter)
  • .plumber.yaml (add default config section with sensible defaults)
  • cmd/analyze.go (add output formatting)

Why It's Valuable

This attack can completely neutralize an organization's security scanning investment while maintaining a false sense of security ("all pipelines are green"). It's especially dangerous because it doesn't trigger any visible error — the scanners simply don't scan. This complements the existing containerImageMustComeFromAuthorizedSources control which detects untrusted registries but doesn't specifically target the scanner override pattern.

Metadata

Metadata

Assignees

No one assigned

    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