GitHub Actions provides access to all repository secrets through the secrets context. Using toJSON(secrets) serializes and exposes the complete set of repository secrets to a workflow step.

Why is this an issue?

When the full secrets context is serialized using toJSON(secrets) or a similar expression and passed to a job or step, every secret in the repository becomes accessible in that context. This increases the attack surface: any vulnerability in the job or step — including a compromised third-party action — can compromise all secrets rather than just the ones the job actually needs.

What is the potential impact?

Excessive secret exposure

A single vulnerability in one workflow step can expose secrets that are entirely unrelated to that step’s purpose. Repository-wide credentials such as deployment keys, cloud provider tokens, or third-party API keys may all be extracted by an attacker who can influence only a small part of the workflow.

How to fix it

Code examples

Noncompliant code example

name: Example

on:
  pull_request:
    branches: [ main ]

jobs:
  main:
    runs-on: ubuntu-latest

    steps:
      - name: Example Step
        env:
          SECRETS: ${{ toJSON(secrets) }} # Noncompliant
        run: |
          example-command "$SECRETS"

Compliant solution

name: Example

on:
  pull_request:
    branches: [ main ]

jobs:
  main:
    runs-on: ubuntu-latest

    steps:
      - name: Example Step
        env:
          API_KEY: ${{ secrets.API_KEY }}
        run: |
          example-command "$API_KEY"

Resources

Documentation

Standards