# CI/CD secrets masked from logs

- **Pattern:** `ab-001259` (`environment-security.secrets-management.ci-logs-masked`)
- **Severity:** high
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001259
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001259)

## 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:
```yaml
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 `secrets` context and `env`. In GitLab CI, look for `secrets` or variable masking. In CircleCI, check for `Environment Variables` and 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_KEY` rather 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:**
  ```yaml
  jobs:
    build:
      runs-on: ubuntu-latest
      steps:
        - name: Deploy
          env:
            STRIPE_SECRET: ${{ secrets.STRIPE_SECRET_KEY }}
          run: npm run deploy
  ```

  **GitLab CI:**
  ```yaml
  deploy:
    script:
      - npm run deploy
    variables:
      STRIPE_SECRET: $CI_JOB_TOKEN  # masked by default
  ```

  Verify masking is working by checking a successful pipeline run — the secret value should show as `***` in logs.

## External references

- cwe CWE-532
- owasp A09
- nist AU-9

Taxons: cryptography-and-secrets, operational-readiness

HTML version: https://auditbuffet.com/patterns/ab-001259
