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

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:

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