跳到主要内容

PR/MR 流水线安全

本文档介绍 pull_request 和 pull_request_target 两种触发事件的安全差异,帮助选择正确的 Fork PR 流水线配置。

Pull Request(PR)流水线需要执行来自外部贡献者(fork 仓库)的代码,存在安全风险。需要理解 pull_requestpull_request_target 两种触发事件的安全差异,并选择合适的配置。

配置说明

pull_request vs pull_request_target 安全差异

维度pull_requestpull_request_target
执行代码来源fork 仓库的代码(PR 提交)目标仓库的代码(主分支上的 workflow 版本)
ATOMGIT_TOKEN 权限仅 read 权限(安全限制)按 permissions 声明的完整权限
Secrets 可访问性fork 来源不可访问项目 Secret可访问项目 Secret
workflow 文件版本使用 fork 中的 workflow 版本使用目标分支中的 workflow 版本
适用场景安全的代码检查、lint、构建需要写权限的部署、发布、评论
checkout默认代码来源PR预合并分支base分支

pull_request 事件详解

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

permissions:
repository: read # pull_request 下 ATOMGIT_TOKEN 仅 read
pr: read # 不可写操作 PR

stages:
check:
name: 代码检查
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

安全机制

  • 代码来源:执行 fork 仓库 PR 分支上的代码,包括 PR 提交者修改的 workflow 文件。
  • 权限限制:ATOMGIT_TOKEN 仅拥有 read 权限,无法推送代码、修改 PR、操作项目资源。
  • Secret 隔离:来自 fork 的 workflow 不可读取项目级和组织级 Secret。

机制效果:恶意 PR 提交者可以修改 workflow 文件内容,但由于无 Secret 和写权限,攻击范围有限——无法窃取凭证、无法修改仓库。

pull_request_target 事件详解

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

permissions:
repository: write # pull_request_target 下可拥有写权限
pr: write # 可评论/操作 PR

stages:
build:
name: 构建
jobs:
Build:
name: build-and-report
runs-on: [ubuntu-latest, x64, medium]
steps:
- name: Checkout source code
uses: checkout # 此处默认检出基础仓库的默认分支
- 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 ✅"}'

安全机制

  • 代码来源:workflow 文件使用目标仓库(main 分支)的版本,而非 fork 中的版本。这是关键差异——PR 提交者无法修改执行逻辑。
  • 权限范围:ATOMGIT_TOKEN 拥有 permissions 声明的完整权限,可写仓库、操作 PR。
  • Secret 可访问:可读取项目级和组织级 Secret。

pull_request_target 事件的风险

pull_request_target 触发的工作流使用基仓库的 ATOMGIT_TOKEN 和 Secret 运行,若直接检出 fork 代码,攻击者可在 Makefile、构建脚本等中注入恶意命令,利用基仓库的凭证执行或通过三方请求将私密信息传出,即"pwn 请求"攻击。

默认保护pull_request_target 场景下 checkout 插件默认检出基仓库的目标分支,而非 PR 分支。由于目标分支中仅包含受信任的代码,不涉及 fork 代码,因此可以安全地授予敏感信息和读/写令牌。

风险场景:手动将 checkout 指向 PR 的源分支(如 ref: ${{ atomgit.event.pull_request.head.sha }})会破坏此保护,导致 fork代码在基仓库凭证环境中执行。

类似的易受攻击模式还包括:将 repository 设为 fork 仓库、使用预合并分支对应commit SHA(如ref: ${{ atomgit.event.merge_commit_sha }})、使用预合并分支(如 refs/merge-requests/N/merge)。

pull_request_target 事件下代码检出的内置保护机制

当触发场景为 pull_request_target,且来源为 fork 仓库的 PR 时,出于安全考虑,以下检出默认会被拦截

场景匹配维度拦截原因示例
A. ref 为PR的源分支或预合并分支ref 匹配 refs/merge-requests/N/(head|merge)预合并分支包含fork代码,可能导致"pwn 请求"ref: refs/merge-requests/13/merge
B. 仓库全名匹配 fork head用户传入仓库 == pull_request.head.repo.full_name检出 fork 仓库的任意分支代码,可能导致"pwn 请求"repository: .../i-am-fork-group/fork-test.git
C. commit SHA 匹配commit[atomgit.event.pull_request.head.sha, atomgit.event.merge_commit_sha] 列表中,为PR的源分支或预合并分支对应的commit SHA使用预合并分支对应的commit SHA检出代码,包含fork代码,可能导致"pwn 请求"ref: bd16c8c2...

关闭 pull_request_target 事件下的内置保护机制

审查风险后,在 checkout 步骤设置 allow-unsafe-pr-checkout: true

注意:检出前请充分审查 fork 代码对应PR场景的安全性,fork 代码将在基仓库的 token 和 secrets 环境中执行,存在安全风险

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

permissions:
repository: write # pull_request_target 下可拥有写权限
pr: write # 可评论/操作 PR

stages:
build:
name: 构建
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 # 显示配置允许不安全的pr检出
ref: refs/merge-requests/N/merge # 当显示配置allow-unsafe-pr-checkout: true后,允许检出预合并分支
- 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 ✅"}'