Skip to main content

CI/CD provider

ISSUE-301 Critical Quick CLI CI/CD Secrets

Secret leak in pipeline configuration

Control: Pipeline must not leak secrets in configuration · Config key: pipelineMustNotLeakSecretsInConfig

📋 What is this?

The resolved pipeline configuration contains a pattern that matches a hardcoded secret (API token, private key, password, or other credential) embedded directly in the YAML. Plumber resolves the full merged .gitlab-ci.yml (with every include: followed) and pipes the result through [gitleaks](https://gitleaks.io); any high-confidence match against the built-in rule catalog — or a custom rule set provided via gitleaksConfigPath — surfaces as an ISSUE-301 finding. The detected value never leaves the scanner: each finding's preview carries a redacted form with the first and last four characters visible and the middle replaced with asterisks.

⚠️ Impact

Secrets committed to pipeline configuration are exposed to everyone with read access to the repository, appear in version history forever (rotation is the only fix, not deletion), and are forwarded to every runner that executes the pipeline. A leaked API key can enable unauthorised access to cloud services, data exfiltration, billing abuse, or lateral movement into production systems.

🔧 How to fix

Revoke and rotate the exposed secret immediately. Remove it from the YAML, then inject it securely as a masked, protected CI/CD variable (Settings > CI/CD > Variables) and reference it as $MY_SECRET in the pipeline. For production workloads, prefer an external secrets manager (HashiCorp Vault, AWS Secrets Manager, Doppler) over GitLab-managed variables.

✗ Before Secrets hardcoded in .gitlab-ci.yml are visible to every project member and live in the commit history forever.
# .gitlab-ci.yml — ❌ Hardcoded secrets (CRITICAL)
deploy:
stage: deploy
script:
- export AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
- export AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
- aws s3 sync . s3://my-bucket
api-call:
variables:
API_TOKEN: "ghp_exampleTokenHardcodedHere123"
script:
- curl -H "Authorization: token $API_TOKEN" https://api.example.com
✓ After Secrets stay in protected variables; the YAML is safe to share.
# .gitlab-ci.yml — ✅ Secrets injected via CI/CD variables
deploy:
stage: deploy
script:
# AWS credentials injected from protected CI/CD variables
- aws s3 sync . s3://my-bucket
api-call:
script:
- curl -H "Authorization: token $API_TOKEN" https://api.example.com
# In GitLab: Settings > CI/CD > Variables
# Add: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, API_TOKEN
# Set Protected: true, Masked: true for each
# .plumber.yaml
gitlab:
controls:
pipelineMustNotLeakSecretsInConfig:
enabled: true
# gitleaksPath: /usr/local/bin/gitleaks # default: $PATH
# gitleaksConfigPath: .gitleaks.toml # default: built-in rules

💡 Tips

  • Detection is opt-in: the control requires [gitleaks](https://github.com/gitleaks/gitleaks) on $PATH (or set gitleaksPath). If the binary is missing, plumber emits a warning and the control routes through the SKIPPED lane rather than failing the run or silently passing.
  • If a secret was ever committed, treat it as compromised and rotate it immediately — deletion does not remove it from history.
  • Use a custom .gitleaks.toml via gitleaksConfigPath to narrow rules to your stack, or to allowlist legitimate matches (e.g. dummy values in test pipelines).
  • Pair with pre-commit hooks (gitleaks protect --staged) so the next leak is caught before it lands in git.

⚙️ Configuration

This control is configured in .plumber.yaml under the gitlab section:

gitlab:
  controls:
    pipelineMustNotLeakSecretsInConfig:
      enabled: true

See the CLI documentation for the full configuration reference.