TL;DR
- 场景 :GitHub 在
actions/checkout v7中默认拦截pull_request_target/workflow_run事件里常见的 Fork PR Head/Merge Checkout 模式,但组织里的旧工作流、第三方 Action、Artifact 与手工 Git 操作并不在新保护覆盖范围内。 - 结论:Pwn Request 的本质是"高权限事件 × 攻击者可控内容 × 执行路径 × 可滥用资源"四个条件同时闭合,新保护只关掉了其中一个常见入口,信任边界必须由组织自己重建。
- 产出:一份覆盖事件边界、权限矩阵、版本治理、组织扫描、双阶段重构、Policy 与发布前检查的实战指南,可直接用于 2026-07-20 前后的工作流审计与迁移。
版本矩阵
| 功能 | 状态 | 说明 |
|---|---|---|
actions/checkout v7 2024-06-18 默认拦截 pull_request_target / workflow_run 中 Fork Checkout |
✅ 已验证 | 2024-06-18 发布,2026-06-29 腾讯新闻等多源报道确认其针对 Pwn Request 强化 |
allow-unsafe-pr-checkout: true 作为显式豁免 |
✅ 已验证 | v7 更新日志明确:必须显式声明才能跳过拦截 |
浮动主版本(如 @v4)可在 Tag 更新后解析到新实现 |
✅ 已验证 | Renovate 在 2026-06-18 自动升级到 @v7 的 PR 已存在 |
| 固定 Commit SHA、Minor 或 Patch 不会自动获得回移 | ✅ 已验证 | GitHub 官方文档与本文工程推导一致 |
pull_request 与同仓库 PR 的常规路径不在此次行为变化范围 |
✅ 已验证 | GitHub Docs 关于 pull_request_target 安全的说明 |
workflow_run、issue_comment、Artifact、Cache、手工 git fetch 也在 Pwn Request 攻击面内 |
✅ 已验证 | TeamPCP 蠕虫 5-10/5-11 攻击链 + Grafana 5-16 事件均涉及 |
| 2026-07-20 为"7-15 编辑注调整后的回移日期" | ⚠️ 待官方公告核验 | 文章内部标注,未取得 GitHub 官方 changelog 直接确认 |
| v1 不在本次回移范围 | ⚠️ 待验证 | 文章说法,未在官方文档中独立确认 |
| TeamPCP / Shai-Hulud 攻击波及 ~160 个 npm 包,@tanstack/react-router 周下载 ~1200 万 | ✅ 已验证 | Socket 5-11 通报 + TanStack/Mistral AI 复盘文章 |
| Grafana 2026-05-16 被 CoinbaseCartel 窃取 4 个私有仓库 | ✅ 已验证 | Grafana 5-17 X 公告 + CSDN 深度复盘 |
| GitHub 自身约 3800 个内部仓库因恶意 VSCode 扩展被 TeamPCP 窃取 | ✅ 已验证 | 搜狐 2026-05-21 报道,与 Pwn Request 是不同事件 |
npm ci --ignore-scripts 只能减少一类风险 |
✅ 已验证 | 通用供应链最佳实践,本文采用 |
| OIDC Subject 限制 Repo/Ref/Environment 作为防御手段 | ✅ 已验证 | GitHub Docs OIDC 配置通用做法 |

pull_request_target 不是一个"更强的 pull_request"。它运行在基础仓库的可信上下文中,通常可以获得基础仓库的 GITHUB_TOKEN、Secret 和默认分支 Cache。如果工作流又把未经审查的 Fork PR 代码拉进来执行,外部贡献者就可能把自己的代码放进高权限执行环境。这类组合被称为 Pwn Request。
GitHub 的新默认保护会让 actions/checkout 拒绝一批常见危险 Checkout 模式,但它不是通用沙箱,也不会识别手工 git fetch、gh pr checkout、第三方 Checkout、恶意 Artifact、issue_comment 或所有 workflow_run 组合。正确的迁移不是"关闭报错",而是重建信任边界:低权限工作流处理不可信代码,高权限工作流只处理可信元数据,并通过最小化、可验证的通道交接。
核心结论
- 风险由四个条件同时构成:高权限事件、攻击者可控内容、执行路径、可滥用资源。缺少任何一个条件,攻击链都不完整。
actions/checkout的新默认行为是防呆,不是完整安全边界。组织仍需扫描手工 Git、第三方 Action、Artifact、Cache 和脚本执行。- 浮动主版本可能自动获得回移;固定 SHA、Minor 或 Patch 不会自动获得。固定 SHA 应与持续升级机制组合,而不是被简单视为"更安全"或"更危险"。
- 最稳妥的模式是双阶段:
pull_request在只读、无 Secret 环境中构建测试;高权限阶段只读取小型、严格验证的元数据,不执行 Fork 产物。 - 7 月 20 日后的工作流失败可能是保护正常生效。绕过检查或设置
allow-unsafe-pr-checkout: true会恢复原风险,必须经过显式安全评审。

1. 事件边界:GitHub 改变了什么
GitHub 在 2026 年 6 月 18 日发布公告,并在 7 月 15 日的编辑注中把回移日期调整到 7 月 20 日。公告的核心变化是:
actions/checkout@v7默认拒绝pull_request_target中常见的 Fork PR Head/Merge Checkout 模式。- GitHub 计划把同类保护回移到除 v1 外的其他受支持主版本。
- 使用
actions/checkout@v4这类浮动主版本的工作流会在 Tag 更新后解析到新实现。 - 固定到具体 Commit SHA、Minor 或 Patch 的工作流不会自动获得回移。
pull_request和同仓库 PR 的常规路径不在此次行为变化范围内。- 可以使用
allow-unsafe-pr-checkout: true显式退出保护,但这不是推荐迁移方案。
该公告证明的是"计划、设计与覆盖范围",不证明某一时刻所有 CDN、Tag、Runner 和缓存都已完成传播。发布文章或执行组织审计时,应同时记录:Action 引用、解析 SHA、Runner 镜像、运行时间、错误文本和仓库策略。
2. pull_request 与 pull_request_target 的权限差异
| 维度 | pull_request(Fork PR) |
pull_request_target |
|---|---|---|
| Workflow 来源 | PR 合并上下文/工作流规则 | Base 默认分支上的 Workflow |
| 默认执行信任 | 低信任 | 基础仓库高信任上下文 |
GITHUB_TOKEN |
通常只读 | 可能按 Workflow/仓库设置获得写权限 |
| 仓库 Secret | 通常不向 Fork PR 暴露 | 可用,取决于 Job 与环境配置 |
| 默认分支 Cache | 低权限任务不应假设可信 | 可能读写,形成跨运行持久化通道 |
| 典型用途 | 构建、测试不可信贡献 | Label、评论、指派、元数据自动化 |
| 是否应执行 Fork 代码 | 可以,但必须保持低权限 | 不应直接执行 |
pull_request_target 的合理用途是对 PR 元数据做高权限动作:添加标签、检查作者、写评论、更新 Project、决定是否允许后续任务。它的错误用途是先进入高权限上下文,再把 Fork 的 Head 拉下来构建。

3. Pwn Request 的成立条件
可以把攻击链写成一个乘法模型:
text
高权限基础仓库上下文
× 攻击者可控的 Fork PR 内容
× Checkout / Artifact / 配置进入工作区
× Build、Test、Install、Script 等执行路径
× Token、Secret、Cache、Artifact 或网络出口
= 可利用的 Pwn Request
Checkout 本身通常只是把文件写入磁盘。真正执行攻击者代码的动作可能是:
npm install、npm ci的生命周期脚本;pip install .、setup.py、PEP 517 Build Backend;make test、Gradle/Maven Plugin、Cargo Build Script;- 读取 PR 中的 Shell、Python、JavaScript 或配置并执行;
- 测试框架自动加载 Plugin;
- Docker Build 中的
RUN; - 从低权限工作流下载 Artifact 后解压并执行;
- 从攻击者可影响的 Cache 恢复可执行内容。
因此,"只 Checkout、不执行"与"Checkout 后运行任意项目命令"必须分开判断。

4. 一个典型危险工作流
yaml
name: preview-pr
on:
pull_request_target:
types: [opened, synchronize, reopened]
permissions:
contents: write
pull-requests: write
jobs:
preview:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci
- run: npm test
- run: ./scripts/deploy-preview.sh
env:
PREVIEW_TOKEN: ${{ secrets.PREVIEW_TOKEN }}
危险点不是 pull_request_target 单独存在,而是:
- Job 处于 Base 高权限上下文;
ref指向攻击者控制的 Head SHA;npm ci会执行攻击者可修改的依赖和生命周期脚本;- Job 拥有写 Token 和部署 Secret;
- 后续脚本还有网络出口和外部平台权限。
新 actions/checkout 保护可能拒绝此类常见模式。把输入改成手工 git fetch 并不能消除风险,只是绕过一个检测点。

5. 版本固定的真实含义
| 引用方式 | 可复现性 | 是否自动得到回移 | 主要风险 | 推荐控制 |
|---|---|---|---|---|
actions/checkout@v4 |
中 | 可能 | Tag 变化会改变行为 | 记录解析 SHA;发布前回归测试 |
@v4.2.2 |
较高 | 否 | 安全修复不会自动进入 | Dependabot/Renovate + SLA |
@<commit-sha> |
最高 | 否 | 长期不更新会滞留漏洞 | SHA Allowlist + 自动升级 PR |
@v1 |
低且过旧 | 不在本次回移 | 缺少新保护和新运行时支持 | 迁移到受支持版本 |
"固定 SHA"解决供应链可复现性,不解决版本陈旧。组织应要求第三方 Action 固定 SHA,同时设置更新机器人、变更审查和最大滞留时间。

6. 组织级扫描方法
6.1 快速文本搜索
bash
rg -n --glob '.github/workflows/*.{yml,yaml}' \
'pull_request_target|workflow_run|issue_comment|gh pr checkout|git fetch|actions/checkout|allow-unsafe-pr-checkout|secrets\.|permissions:' .
该命令只能做候选召回。YAML 可以使用 Anchor、Reusable Workflow、表达式和多行字符串,最终仍需结构化解析与人工确认。
6.2 GitHub Code Search 查询
text
org:YOUR_ORG path:.github/workflows pull_request_target
org:YOUR_ORG path:.github/workflows "github.event.pull_request.head.sha"
org:YOUR_ORG path:.github/workflows "gh pr checkout"
org:YOUR_ORG path:.github/workflows "allow-unsafe-pr-checkout"
需要分别检查主仓库、模板仓库、Archived Repository、Fork、Reusable Workflow 和默认分支之外的活动分支。
6.3 风险评分
建议按以下规则评分:
| 信号 | 分值 |
|---|---|
触发器包含 pull_request_target |
+4 |
Checkout 指向 head.sha、head.ref 或 Merge Ref |
+5 |
使用 gh pr checkout / 手工拉 Fork |
+5 |
| Job 使用仓库或环境 Secret | +4 |
permissions 有写权限 |
+3 |
| 运行项目脚本、Build、Test、Install | +4 |
| 读写 Cache | +2 |
| 下载并执行 Artifact | +4 |
使用 allow-unsafe-pr-checkout: true |
+6 |
| 只有 Label/Comment,且无 Checkout/执行 | -4 |
评分只是排序,不是漏洞证明。一个 curl 下载并执行外部脚本的工作流,即使未命中规则也可能高危。
本内容包提供 tools/gh_actions_audit.py,用于候选工作流的本地静态扫描。
7. 安全重构:把不可信计算与高权限动作拆开
7.1 第一阶段:低权限构建和测试
yaml
name: pr-ci
on:
pull_request:
types: [opened, synchronize, reopened]
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci --ignore-scripts
- run: npm test
这仍然执行攻击者代码,因此不能提供 Secret、写 Token 或高权限环境。--ignore-scripts 只能减少一类风险,测试本身仍是任意代码执行。
7.2 第二阶段:只处理可信元数据
yaml
name: pr-metadata
on:
pull_request_target:
types: [opened, edited, labeled, unlabeled]
permissions:
contents: read
pull-requests: write
issues: write
jobs:
label:
runs-on: ubuntu-latest
steps:
- name: Apply label from trusted policy
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
gh pr edit "$PR_NUMBER" \
--repo "$GITHUB_REPOSITORY" \
--add-label "needs-review"
该 Job 不 Checkout Fork,不读取 PR 中的脚本,不把 PR 标题/正文拼接进 Shell 命令。所有不可信文本通过参数或 API 传递,并避免 eval。
7.3 需要把测试结果反馈到 PR 时
安全优先级从高到低:
- 使用 GitHub 原生 Check Run/Status,由低权限 Job 直接写其允许写入的状态。
- 让高权限 Job 通过 GitHub API 查询低权限 Workflow 的状态,而不是下载可执行 Artifact。
- 只传递固定 Schema 的小型 JSON,例如
{run_id, conclusion, commit_sha},验证来源、Workflow、Repository、Branch、SHA、大小和字段。 - 不把低权限 Job 生成的脚本、二进制、Node Module、Python Package 或压缩包交给高权限 Job 执行。
一个可接受的高权限评论 Job 应把 run_id 当索引,再向 GitHub API 获取结论;不要信任 Artifact 内自报的"成功"。

8. workflow_run 不是自动安全
workflow_run 可以让一个有 Secret/写 Token 的 Workflow 在低权限 Workflow 完成后运行,因此很适合做权限拆分。但它同样可能被误用:
text
低权限 Fork Workflow
↓ 上传攻击者控制 Artifact
高权限 workflow_run
↓ 下载、解压、执行 Artifact
Secret / Write Token 泄露
安全交接必须满足:
- 高权限 Workflow 固定在默认分支;
- 校验触发 Workflow 的名称、仓库、事件和结论;
- 校验
head_repository、head_sha与目标 PR; - Artifact 设大小上限、文件类型 Allowlist、禁止符号链接和路径穿越;
- 只解析数据,不执行内容;
- 需要签名时使用高权限 Workflow 能独立验证、攻击者无法伪造的签名体系;
- 不让低权限 Workflow 决定高权限 Job 的命令、路径、Action 版本或环境名称。
9. Secret、Token、Cache 与 Artifact 的风险矩阵
| 资源 | 攻击者目标 | 常见失败模式 | 控制 |
|---|---|---|---|
GITHUB_TOKEN |
写代码、Release、Issue、PR | Workflow 默认权限过宽 | 顶层 permissions: {},Job 按需开启 |
| Repository Secret | 外部服务接管 | Secret 直接注入执行不可信代码的 Job | Fork Job 无 Secret;Environment 审批 |
| OIDC | 获取云临时凭据 | id-token: write 与不可信代码同 Job |
Subject/Repo/Branch 条件;独立部署 Job |
| Cache | 持久化恶意工具链 | 高权限 Job 恢复低信任 Cache | 信任域分离 Key;高权限 Job 不恢复 Fork Cache |
| Artifact | 跨 Job 注入 | 高权限 Job 执行低权限 Artifact | 固定 Schema、小体积、只解析、来源校验 |
| Self-hosted Runner | 横向移动 | Fork 代码进入长期存活机器 | 不对公共 Fork 开放;一次性隔离 Runner |
| Deployment Token | 外部平台写权限 | 预览部署脚本由 Fork 控制 | 受信部署器 + 声明式输入 + 人工审批 |
10. Fork PR 预览环境的安全方案
直接执行 Fork 代码的预览本质上需要不可信计算。可采用:
text
Fork PR
↓ 低权限构建
一次性隔离 Runner / 无 Secret / 受限网络
↓ 生成内容摘要与不可执行构建产物
受信部署服务
↓ 策略校验、病毒扫描、大小限制、内容类型限制
临时 Preview Namespace
↓ TTL、独立域名、无生产 Cookie、无内网访问
Review URL
关键不是"Preview 是否自动",而是构建和部署凭据不共处于同一信任域。对于静态站点,可以让构建 Job 生成纯静态文件,并在部署器侧执行严格文件类型检查;对于服务端代码,应使用隔离 Namespace、短期凭据、网络策略和自动销毁。
11. 恶意 Fork 回归测试
在隔离仓库执行,禁止放入真实 Secret。
测试用例
- Fork 修改
package.json,加入preinstall,只创建标记文件。 - Fork 修改测试脚本,打印权限和环境变量名称,不打印值。
- Fork 尝试写入仓库分支,确认 Token 权限被拒绝。
- Fork 尝试访问一个专用无敏感性的 Canary Secret,确认不存在。
- Fork 尝试污染 Cache,确认高权限 Job 不恢复该 Cache。
- Fork 上传带路径穿越、符号链接、超大文件的 Artifact,确认高权限解析器拒绝。
- 对浮动主版本、固定 Patch、固定 SHA 分别运行,记录解析 SHA和错误行为。
记录模板
yaml
repository: org/security-sandbox
run_started_at: 2026-07-20T00:00:00Z
runner_image: ubuntu-24.04
checkout_ref: actions/checkout@v4
resolved_sha: "..."
event: pull_request_target
fork: true
expected: blocked-before-checkout
observed_status: "..."
error_excerpt: "..."
secrets_present: false
write_token_test: denied
12. 组织级 Policy
最低基线:
- 默认
permissions: contents: read或显式空权限; - 公共 Fork 不得进入持久化 Self-hosted Runner;
pull_request_target需要 CODEOWNERS 安全审查;- 禁止
allow-unsafe-pr-checkout,除非有到期豁免; - 第三方 Action 固定 SHA并自动更新;
- 低信任与高信任 Cache Namespace 分离;
workflow_run高权限 Job 禁止执行上游 Artifact;- OIDC Subject 限制 Repo、Ref、Environment;
- 组织级事件/Actor 执行保护用于限制低信任触发器;
- Workflow 变更必须经过专门 Reviewer。
示例 Rego 风格策略:
rego
package actions.security
deny[msg] {
input.on.pull_request_target
some job
step := input.jobs[job].steps[_]
step.uses == "actions/checkout@v4"
contains(step.with.ref, "pull_request.head")
msg := sprintf("%s checks out fork code in pull_request_target", [job])
}
deny[msg] {
input.on.pull_request_target
some job
input.jobs[job].permissions.contents == "write"
msg := sprintf("%s has contents:write under pull_request_target", [job])
}
真实实现需要先把 YAML 规范化,处理字符串/数组触发器、Anchor、Reusable Workflow 和缺省权限。
13. 发布前检查表
- 枚举
pull_request_target、workflow_run、issue_comment与 Reusable Workflow。 - 识别所有 Fork Head/Merge Checkout,包括手工 Git 和第三方 Action。
- 识别所有 Build、Test、Install、Docker Build、脚本和可执行 Artifact。
- 识别 Job 的 Token、Secret、OIDC、Environment、Cache、Artifact 和网络权限。
- 把不可信计算迁移到
pull_request低权限 Job。 - 高权限 Job 只处理固定 Schema 元数据。
- 将顶层权限收紧,并在 Job 层按需开放。
- 检查 Checkout 的版本固定和自动升级策略。
- 在隔离仓库执行恶意 Fork 回归测试。
- 记录 7 月 20 日解析 SHA、错误文本和 Runner 版本。
- 对临时豁免设置 Owner、原因、到期日和补偿控制。
14. 常见错误
错误一:把事件从 pull_request_target 改成 pull_request,但继续注入 Secret
Fork PR 默认拿不到仓库 Secret,这通常会直接失败。正确做法是重新设计部署与评论流程,而不是想办法重新暴露 Secret。
错误二:设置 allow-unsafe-pr-checkout: true
这只关闭防护,不改变信任模型。除非工作流后续不执行任何攻击者内容,并经过逐步证明,否则不应使用。
错误三:把 Checkout 改成 git fetch
检测消失,风险仍在。
错误四:认为固定 SHA 可以永久不升级
固定 SHA需要明确的安全更新 SLA。
错误五:高权限 Job 只"解析"Artifact,但使用了不安全解压器或反序列化
路径穿越、符号链接、压缩炸弹、Pickle/YAML 不安全加载都可能把数据通道重新变成代码通道。
15. 限制与待验证
本文没有证明:
- 2026-07-20 所有
actions/checkout受支持 Tag 已在所有环境完成传播; - GitHub 的默认检测覆盖所有 Pwn Request;
- 任意组织的 Token/Secret 行为都与公开仓库默认值相同;
- 一次静态扫描可以替代威胁建模和动态测试。
实际发布前应按 experiments/github-actions-enforcement-test-plan.md 完成隔离验证。
16. 延伸章节
- 组织级 GitHub Actions Workflow Scanner 与 Policy。
- Fork PR Preview 的一次性 Runner 和网络隔离。
- Actions Cache Poisoning 与 Artifact 供应链。
- Coding Agent 自动生成 Workflow 的安全门禁。
- Reusable Workflow 的身份、输入与 Secret 继承。
参考来源
- GH-ACT-01:GitHub Changelog,Safer
pull_request_targetdefaults for GitHub Actions checkout。 - GH-ACT-02:GitHub Docs,Securely using
pull_request_target。 - GH-ACT-03:GitHub Docs,Events that trigger workflows。
- GH-ACT-04:GitHub Docs,Workflow execution protections。
- GH-ACT-05:
actions/checkoutRepository。
错误速查卡
| 症状 | 根因 | 定位 | 修复 |
|---|---|---|---|
7-20 后 actions/checkout 报"Refused to checkout"或类似阻断 |
v7 默认拒绝 pull_request_target 中 Fork Head/Merge Checkout |
查看 Job 错误文本 + 解析 actions/checkout 的 Resolved SHA |
拆分为低权限 pull_request + 高权限元数据 Job,而不是关掉检查 |
把 pull_request_target 改成 pull_request 后 Secret 注入失败 |
Fork PR 拿不到仓库 Secret 是设计行为 | 看 Job 是否依赖 secrets.* |
重构为 API 查询 Check Run,不要绕回暴露 Secret |
浮动 @v4 解析到 v7 后行为变化 |
Tag 升级把新默认行为带进来 | 对比 actions/checkout 不同 Tag 之间的差异 |
记录 Resolved SHA,建立 Release 前回归测试 |
| 固定 SHA 之后没拿到 v7 保护 | 浮动 Tag 才会自动回移,固定 SHA 不会 | 看 Release 是否覆盖到 Patch/Tag | 用 Dependabot/Renovate + SLA + 最大滞留时间,而不是不升级 |
| Workflow 从低权限 Artifact 还原"成功"后被攻击 | 高权限 Job 信任 Artifact 内自报结论 | 看 Artifact 来源、签名、Schema | 高权限 Job 通过 run_id 调 GitHub API 重新查询,不解析 Artifact |
7-20 后工作流一直失败,加 allow-unsafe-pr-checkout: true 后恢复 |
这是新保护在生效,不是配置错误 | 比对加豁免前后的错误 | 走显式安全评审,带 Owner、原因、到期日和补偿控制 |
git fetch 之后 Pwn Request 仍可利用 |
actions/checkout 拦截的不是 Git 本身,而是常见模式 |
看 Workflow 实际执行了哪些脚本 | 信任边界要按"是否执行 Fork 产物"判定,而不是按"是否用了 checkout Action" |
npm ci 即使有 --ignore-scripts 仍然有风险 |
--ignore-scripts 只能减少生命周期脚本,不能消除任意代码执行 |
看 Job 是否执行 npm test / 项目脚本 |
把构建/测试放到一次性隔离 Runner,关键步骤才回高权限 Job |
| 高权限 Job "只解析"Artifact 仍被攻击 | 不安全解压器或反序列化把数据通道变代码通道 | 看依赖、路径、符号链接处理 | 文件类型 Allowlist、路径穿越校验、签名验证、不用 Pickle/YAML.load |
| OIDC 凭据意外泄露 | id-token: write 与不可信代码同 Job |
看 Job 是否同时有 pull_request 触发器和 id-token: write |
OIDC Subject 限制 Repo/Ref/Environment,部署独立 Job |
| Self-hosted Runner 被污染 | 公共 Fork 代码进入长期存活机器 | 看 Runner 是否对 Fork 开放 | Fork 用一次性隔离 Runner,Self-hosted 仅限受信 PR |
| 7-15 编辑注调整到 7-20 后的回移日期 | 文章自带标注,未取得 GitHub 官方 changelog 独立确认 | 看 GitHub 官方 Changelog 公告 | 关注组织实际解析的 SHA、Runner 镜像与错误文本,不依赖具体日历 |
| 评分命中但人工判定"安全" | 评分只是排序,不是漏洞证明 | 看 Workflow 是否实际执行不可信内容 | 用恶意 Fork 回归测试做动态验证,而不是只看规则分 |
| Cache 在高权限 Job 中出现意外二进制 | 低权限 Job 写入了可执行内容到 Cache | 看 Cache Key 与写入者 | 高权限 Job 不恢复 Fork Cache,Key 按信任域分离 |
permissions: write 与 pull_request_target 共存 |
Job 拿到高权限 Token 又可被攻击者触发 | 看权限声明位置与默认行为 | 顶层 permissions: {} 空权限,Job 按需开启 |