跳到主要内容

PR/MR Pipeline Security

This document introduces the security differences between the pull_request and pull_request_target trigger events, helping to choose the correct fork PR pipeline configuration.

PR pipelines need to execute code from external contributors (fork repositories), which poses a security risk. It is necessary to understand the security differences between pull_request and pull_request_target events and choose the appropriate configuration.

Configuration Description

Security Differences Between pull_request and pull_request_target

Dimensionpull_requestpull_request_target
Code SourceCode from the fork repository (PR submission)Code from the target repository (workflow version on the main branch)
ATOMGIT_TOKEN PermissionsRead-only permissions (security restrictions)Full permissions as declared in permissions
Secrets AccessibilitySecrets of the project are not accessible from the fork sourceAccessible to project secrets
Workflow File VersionUses the workflow version in the forkUses the workflow version in the target branch
Use CaseSecure code checks, linting, and buildingDeployment, release, and commenting requiring write permissions
Default Checkout SourcePR pre-merged branchBase branch

Detailed Explanation of pull_request Event

# .gitcode/workflows/pr-check.yml
on:
pull_request:
branches: [main]

permissions:
repository: read # ATOMGIT_TOKEN has only read access under pull_request
pr: read # Cannot perform write operations on PR

stages:
check:
name: Code Check
jobs:
lint:
name: lint-and-test
runs-on: [ubuntu-latest, x64, small]
steps:
- name: Run lint
run: npm run lint
- name: Run npm
run: npm test

Security Mechanism:

  • Code Source: Executes code from the fork repository's PR branch, including workflow files modified by the PR submitter.
  • Permission Restrictions: ATOMGIT_TOKEN has only read permissions, unable to push code, modify PR, or operate project resources.
  • Secret Isolation: Workflow from fork cannot access project-level and organization-level secrets.

Mechanism Effect: Malicious PR submitters can modify workflow file content, but due to lack of secrets and write permissions, the attack scope is limited — they cannot steal credentials or modify the repository.

Detailed Explanation of pull_request_target Event

# .gitcode/workflows/pr-build.yml
on:
pull_request_target:
branches: [main]

permissions:
repository: write # pull_request_target has write permissions
pr: write # Can comment/operate PR

stages:
build:
name: Build
jobs:
Build:
name: build-and-report
runs-on: [ubuntu-latest, x64, medium]
steps:
- name: Checkout source code
uses: checkout # By default, it checks out the default branch of the base repository
- name: build
run: make build
- name: Run script
run: |
curl -X POST "https://atomgit.com/api/v5/repos/${{ atomgit.repository }}/pulls/${{ atomgit.event.pull_request.number }}/comments" \
-H "Authorization: token $ATOMGIT_TOKEN" \
-d '{"body": "Build succeeded ✅"}'

Security Mechanism:

  • Code Source: The workflow file uses the version from the target repository (main branch), not the version from the fork. This is the key difference — the PR submitter cannot modify the execution logic.
  • Permission Scope: ATOMGIT_TOKEN has full permissions as declared, allowing writing to the repository and operating the PR.
  • Secret Accessibility: Project-level and organization-level secrets are accessible.

Risks of pull_request_target Event

The workflow triggered by pull_request_target runs using the base repository's ATOMGIT_TOKEN and secrets. If it directly checks out fork code, attackers can inject malicious commands into Makefile, build scripts, etc., and use the base repository's credentials to execute or send private information through third-party requests, i.e., a "pwn request" attack.

Default Protection: In the pull_request_target scenario, the checkout plugin defaults to checking out the target branch of the base repository, not the PR branch. Since the target branch contains only trusted code and does not involve fork code, it is safe to grant sensitive information and read/write tokens.

Risk Scenario: Manually pointing the checkout to the PR source branch (e.g., ref: ${{ atomgit.event.pull_request.head.sha }}) breaks this protection, leading to fork code executing in the base repository's credential environment.

Similar vulnerable patterns also include: setting repository to the fork repository, using the commit SHA of the merged branch (e.g., ref: ${{ atomgit.event.merge_commit_sha }}), or using the merged branch (e.g., refs/merge-requests/N/merge).

Built-in Protection Mechanisms for Code Checkout Under pull_request_target Event

When the trigger scenario is pull_request_target and the source is a PR from a fork repository, for security reasons, the following checkouts are defaultly blocked:

ScenarioMatching DimensionBlocking ReasonExample
A. ref is the PR source branch or merged branchref matches refs/merge-requests/N/(head|merge)Merged branch contains fork code, which may lead to "pwn request" attacksref: refs/merge-requests/13/merge
B. Repository Full Name Matches Fork HeadUser-provided repository == pull_request.head.repo.full_nameChecking out code from any branch of the fork repository may lead to "pwn request" attacksrepository: .../i-am-fork-group/fork-test.git
C. Commit SHA Matchescommit is in the list [atomgit.event.pull_request.head.sha, atomgit.event.merge_commit_sha], which corresponds to the commit SHA of the PR source branch or merged branchChecking out code using the commit SHA of the merged branch, which contains fork code, may lead to "pwn request" attacksref: bd16c8c2...

Disabling Built-in Protection Mechanism Under pull_request_target Event

After reviewing the risks, set allow-unsafe-pr-checkout: true in the checkout step.

Note: Fully review the safety of the fork code in the PR context before checking out. The fork code will be executed in the base repository's token and secrets environment, which poses a security risk.

# .gitcode/workflows/pr-build.yml
on:
pull_request_target:
branches: [ main ]

permissions:
repository: write # pull_request_target has write permissions
pr: write # Can comment/operate PR

stages:
build:
name: Build
jobs:
Build:
name: build-and-report
runs-on: [ ubuntu-latest, x64, medium ]
steps:
- name: Checkout source code
uses: checkout
with:
allow-unsafe-pr-checkout: true # Explicitly configure to allow unsafe PR checkout
ref: refs/merge-requests/N/merge # After explicitly configuring allow-unsafe-pr-checkout: true, it allows checking out the merged branch
- name: build
run: make build
- name: Run script
run: |
curl -X POST "https://atomgit.com/api/v5/repos/${{ atomgit.repository }}/pulls/${{ atomgit.event.pull_request.number }}/comments" \
-H "Authorization: token $ATOMGIT_TOKEN" \
-d '{"body": "Build succeeded ✅"}'