GitHub Actions workflows should not interpolate secrets directly into run: command text. When a job includes third-party actions,
direct interpolation can leak secret values through shared runner artifacts, such as process arguments and temporary step script files.
In GitHub Actions, third-party actions and run: steps of the same job execute on the same runner and can observe shared runtime
artifacts. When a secret is expanded directly in a run: command using ${{ secrets.SECRET_NAME }}, its value is substituted
into the generated command before execution. This can expose the secret in places such as process arguments and the temporary script file created for
the step. A compromised or malicious third-party action in the same job can read these artifacts and exfiltrate the secret. Passing secrets through
environment variables reduces this exposure by avoiding direct interpolation of secret values into the command text.
If a third-party action in the same job is compromised or malicious, it can silently exfiltrate credentials by reading the runner’s process list
while a run: step executes. The attacker gains access to those credentials and can use them to access protected systems, escalate
privileges, or pivot to other parts of the infrastructure.
Pass secrets to steps through environment variables instead of expanding them directly as command-line arguments.
name: Example
on:
pull_request:
jobs:
main:
steps:
- uses: actions/checkout@v4
- uses: some-org/some-tool@v1
- name: example
run: |
example-command "${{ secrets.EXAMPLE_SECRET }}" # Noncompliant
name: Example
on:
pull_request:
jobs:
main:
steps:
- uses: actions/checkout@v4
- uses: some-org/some-tool@v1
- name: example
env:
SECRET: ${{ secrets.EXAMPLE_SECRET }}
run: |
example-command "$SECRET"