When using GitHub Actions, certain workflow triggers such as pull_request_target
and workflow_run
execute with the
repository’s privileges. This creates a security risk when these workflows check out code from a forked repository and then use this untrusted code as
input for subsequent steps.
If code from the fork is used as an input inside the workflow, they could exploit this to extract sensitive information, manipulate the repository,
or execute arbitrary commands with the permissions of the repository. Special considerations should be taken into account on organizations and
repositories created before 2023 when the default permissions of
the GITHUB_TOKEN
were changed to read-only.
Ask Yourself Whether
- Is the checked out code executed directly as part of some steps in the workflow?
- Can the checked out code indirectly affect the execution of the workflow? For example, does your workflow use configuration files to
download packages from package repositories?
There is a risk if you answered yes to any of these questions.
Recommended Secure Coding Practices
If the flow does not require write access to the repository nor access to its secrets, the recommended approach is to avoid using triggers that
cause execution using elevated privileges.
If this is not feasible, the workflow can be split into two parts:
- An unprivileged job that gets triggered by
pull_request
that performs checks that do not require elevated permissions, and
- A privileged job that is triggered by
workflow_run
on completion of the first job, that performs the actions requiring elevated
permissions
When doing this, it is important to make sure that the privileged workflow does not process input from the untrusted workflow.
Sensitive Code Example
The following workflow executes the build.sh
script from the fork using the privileges of the repository:
name: Example
on:
pull_request_target:
jobs:
main:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }} # Sensitive
- name: Build
run: |
./build.sh
Compliant Solution
If elevated permissions are not needed, the simplest way to fix this is to use pull_request
as the trigger.
name: Example
on:
pull_request:
jobs:
main:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Build
run: |
./build.sh
See
Documentation
Standards
Articles & blog posts