checkout 拒绝 fork PR:Refusing to check out fork pull request code
checkout拒绝在高权限pull_request_target检出fork代码。按任务需要迁移到普通PR只读测试,或保留受信任脚本处理元数据,核对实际输入和权限。
工作流在 actions/checkout 步骤退出,报 Refusing to check out fork pull request code from a 'pull_request_target' workflow。同仓库 PR 正常,fork PR 失败时,先看事件和检出目标:这是 checkout 对高权限上下文读取 fork PR 代码的保护,不能按网络或 token 权限不足处理。
本文按 actions/checkout 7.0.1 的检查逻辑说明。使用 @v4 等可移动主版本标签的旧工作流,也可能在标签更新后收到相同保护;记录任务实际下载的版本或 SHA,不要仅凭 YAML 里的主版本号推断实现没变。
工作流来自主仓库,不代表后续代码也可信
pull_request_target 使用目标仓库的受信任工作流,并能取得该仓库上下文中的令牌和秘密。如果 checkout 又把工作目录切成 fork 的 head,下一步执行的构建脚本、测试、依赖安装或本地 action 就可能来自外部提交。
例如,工作流文件本身没有变化,npm install 的生命周期脚本或测试读取的配置仍可能改变执行行为。只检查 YAML 中有没有直接运行陌生 shell 命令,覆盖不了这条路径。
核对事件名、checkout 的 repository 和 ref,以及后续每一步实际读取的目录。不要把全部事件 payload 或环境变量打印进日志,里面可能有不应公开的数据。
测试 fork 代码,优先放到普通 PR 任务
若任务只需要编译或测试外部修改,可以迁移到 pull_request,保留只读权限、fork 审批和秘密隔离。例如下面只展示检出与输入核对:
on:
pull_request:
permissions:
contents: read
jobs:
inspect:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7.0.1
with:
persist-credentials: false
- run: git rev-parse HEAD
默认检出通常是 PR 的合并测试提交,不是源分支最新版本的随意名称。迁移原测试步骤前,确认项目要测试的输入;若有意改测源 SHA,明确记录这个改变。
这段不添加仓库秘密或写令牌。测试依赖私有服务时,应先重整任务与资源授权,不能为让 fork 代码运行直接补发高权限身份。自托管 Runner 还要隔离内部资源和跨任务残留,单纯把 permissions 改为 read 并不能消除机器上的其他权限。
留在 target 的任务只处理受信任代码
打标签、分类和其他确需仓库写入的自动化,可继续在经过审查的 target 工作流里处理元数据,使用受信任分支的脚本。外部标题、分支名等值作为数据传入,不能直接拼入 shell 源码。
确有高权限、只读分析 fork 文件的需求时,要审查整个数据读取流程:分析器来自哪里,会不会加载外部配置、插件、依赖或启动脚本,读取路径能否逃出预期目录。把目录命名为 pr-code,或声称“只解析文本”,都不自动证明流程不会执行不可信内容。
checkout 的显式风险开关只能在这种具体审查后讨论,不能作为本报错的通用修复。降级动作、手工 git fetch、gh pr checkout 或下载外部 artifact 来避开检查,仍可能把同样的代码带进高权限任务。
检出保护与事件政策分别排查
checkout 7.0.1 会依据 fork 身份、PR 引用和 payload 中的提交等信息做检查,并覆盖相关 workflow_run 场景;它不是对所有外部代码来源的完整拦截器。默认的受信任 self-checkout 与手工指定 fork 的检出也有区别。
如果任务连运行记录都没有,应另查组织、仓库及 GitHub 当前的 Actions 事件政策,不要按这个“已启动并在 checkout 报错”的流程处理。编辑 fork 中的工作流文件,也不会直接改变 target 使用的主仓库定义。
修改后分别验证 fork PR 和同仓库 PR 的实际输入、所需检查与权限。外部代码任务没有秘密,写入任务只执行受信任脚本,才能确认迁移保留了原功能和必要边界。一次扫描变绿不足以证明所有 fork 路径都已覆盖。


