Skip to main content

CI/CD provider

ISSUE-301CriticalQuickCLIRemovedCI/CD Secrets

Secret leak in pipeline configuration

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

This control was removed from Plumber and no longer produces findings: secret detection is out of scope for the product, and the integration executed an external binary (gitleaks) during analysis. See the security advisory GHSA-w2xj-4v44-6rqr and issue #310 for details. The ISSUE-301 code is retired and will not be reused.

📋 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 resolved the full merged .gitlab-ci.yml (with every include: followed) and piped the result through gitleaks; any high-confidence match against the rule catalog surfaced as an ISSUE-301 finding. The detected value never left the scanner: each finding's preview carried 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.

✗ BeforeSecrets 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
✓ AfterSecrets 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

💡 Tips

  • If a secret was ever committed, treat it as compromised and rotate it immediately; deletion does not remove it from history.
  • 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.