CI/CD secrets masked from logs
Why it matters
CI/CD pipeline logs are often stored for months and accessible to every team member, contractor, and sometimes even public viewers on open-source projects. A secret printed to a GitHub Actions log — even for one second before a run is cancelled — is captured and searchable. CWE-532 (Insertion of Sensitive Information into Log File) and OWASP A09 (Security Logging and Monitoring Failures) both classify this as a security failure. Rotating a secret that appeared in a CI log is non-trivial because you cannot know who has read or downloaded the log before you noticed the exposure.
Severity rationale
High because CI logs are broadly accessible within an organization and often retained for months, giving a long window for secret exfiltration after accidental exposure.
Remediation
Reference secrets through your CI platform's native secret store, never through shell variables or hard-coded strings in workflow files. Platform-native references are automatically masked in log output.
GitHub Actions — inject via the secrets context:
jobs:
deploy:
steps:
- name: Deploy
env:
STRIPE_SECRET: ${{ secrets.STRIPE_SECRET_KEY }}
run: npm run deploy
GitLab CI — declare variables as masked in project Settings → CI/CD → Variables. Verify masking worked by checking a completed run: the value should appear as *** in every log line.
Detection
-
ID:
ci-logs-masked -
Severity:
high -
What to look for: Check CI/CD configuration (.github/workflows, .gitlab-ci.yml, .circleci, etc.) for secret masking settings. In GitHub Actions, this is
secretscontext andenv. In GitLab CI, look forsecretsor variable masking. In CircleCI, check forEnvironment Variablesand masking flags. -
Pass criteria: Count all CI/CD workflow files and enumerate secret references in each. Report the count of masked vs unmasked secrets even on pass. The CI/CD platform is configured to mask known secrets from logs with at least 100% of secret variables using the platform's masking mechanism (e.g., GitHub Actions using
secrets.STRIPE_KEYrather than hardcoding). -
Fail criteria: CI/CD pipelines print secrets in logs, or no masking configuration found for secret variables.
-
Skip (N/A) when: The project does not use CI/CD (local builds only, though this is uncommon in production).
-
Detail on fail: Specify which CI/CD platform and what's missing. Example:
"GitHub Actions workflow uses env secrets but does not configure masking"or"GitLab CI logs show API keys in pipeline output". -
Remediation: Configure secret masking in your CI/CD platform:
GitHub Actions:
jobs: build: runs-on: ubuntu-latest steps: - name: Deploy env: STRIPE_SECRET: ${{ secrets.STRIPE_SECRET_KEY }} run: npm run deployGitLab CI:
deploy: script: - npm run deploy variables: STRIPE_SECRET: $CI_JOB_TOKEN # masked by defaultVerify masking is working by checking a successful pipeline run — the secret value should show as
***in logs.
External references
- cwe · CWE-532 — Insertion of Sensitive Information into Log File
- owasp:2021 · A09 — Security Logging and Monitoring Failures
- nist:rev5 · AU-9 — Protection of Audit Information
Taxons
History
- 2026-04-18·v1.0.0·Initial import from environment-security·automated