gha-security-review

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
<!-- Attack patterns and real-world examples sourced from the HackerBot Claw campaign analysis by StepSecurity (2025): https://www.stepsecurity.io/blog/hackerbot-claw-github-actions-exploitation -->
<!-- 攻击模式和真实案例来自StepSecurity 2025年发布的HackerBot Claw活动分析:https://www.stepsecurity.io/blog/hackerbot-claw-github-actions-exploitation -->

GitHub Actions Security Review

GitHub Actions安全审查

Find exploitable vulnerabilities in GitHub Actions workflows. Every finding MUST include a concrete exploitation scenario — if you can't build the attack, don't report it.
This skill encodes attack patterns from real GitHub Actions exploits — not generic CI/CD theory.
查找GitHub Actions工作流中的可利用漏洞。每一项发现都必须包含具体的利用场景——如果你无法构建攻击路径,就不要上报该问题。
本技能收录了来自真实GitHub Actions漏洞利用的攻击模式,而非通用的CI/CD理论知识。

Scope

审查范围

Review the workflows provided (file, diff, or repo). Research the codebase as needed to trace complete attack paths before reporting.
审查提供的工作流(文件、diff或仓库)。上报前需根据需要研究代码库,追踪完整的攻击路径。

Files to Review

需审查的文件

  • .github/workflows/*.yml
    — all workflow definitions
  • action.yml
    /
    action.yaml
    — composite actions in the repo
  • .github/actions/*/action.yml
    — local reusable actions
  • Config files loaded by workflows:
    CLAUDE.md
    ,
    AGENTS.md
    ,
    Makefile
    , shell scripts under
    .github/
  • .github/workflows/*.yml
    — 所有工作流定义文件
  • action.yml
    /
    action.yaml
    — 仓库内的复合action
  • .github/actions/*/action.yml
    — 本地可复用action
  • 工作流加载的配置文件:
    CLAUDE.md
    AGENTS.md
    Makefile
    .github/
    目录下的shell脚本

Out of Scope

超出范围的内容

  • Workflows in other repositories (only note the dependency)
  • GitHub App installation permissions (note if relevant)
  • 其他仓库中的工作流(仅需注明依赖关系即可)
  • GitHub App安装权限(若相关可注明)

Threat Model

威胁模型

Only report vulnerabilities exploitable by an external attacker — someone without write access to the repository. The attacker can open PRs from forks, create issues, and post comments. They cannot push to branches, trigger
workflow_dispatch
, or trigger manual workflows.
Do not flag vulnerabilities that require write access to exploit:
  • workflow_dispatch
    input injection — requires write access to trigger
  • Expression injection in
    push
    -only workflows on protected branches
  • workflow_call
    input injection where all callers are internal
  • Secrets in
    workflow_dispatch
    /
    schedule
    -only workflows
仅上报可被外部攻击者利用的漏洞——外部攻击者指没有仓库写入权限的人员。攻击者可从fork仓库提交PR、创建issue、发表评论,但无法推送代码到分支、触发
workflow_dispatch
或其他手动工作流。
不要标记需要写入权限才能利用的漏洞:
  • workflow_dispatch
    输入注入 — 需要写入权限才能触发
  • 受保护分支上仅
    push
    触发的工作流中的表达式注入
  • 调用方均为内部的
    workflow_call
    输入注入
  • workflow_dispatch
    /
    schedule
    触发的工作流中的密钥

Confidence

置信度

Report only HIGH and MEDIUM confidence findings. Do not report theoretical issues.
ConfidenceCriteriaAction
HIGHTraced the full attack path, confirmed exploitableReport with exploitation scenario and fix
MEDIUMAttack path partially confirmed, uncertain linkReport as needs verification
LOWTheoretical or mitigated elsewhereDo not report
For each HIGH finding, provide all five elements:
  1. Entry point — How does the attacker get in? (fork PR, issue comment, branch name, etc.)
  2. Payload — What does the attacker send? (actual code/YAML/input)
  3. Execution mechanism — How does the payload run? (expression expansion, checkout + script, etc.)
  4. Impact — What does the attacker gain? (token theft, code execution, repo write access)
  5. PoC sketch — Concrete steps an attacker would follow
If you cannot construct all five, report as MEDIUM (needs verification).

仅上报置信度的发现,不要上报理论上存在的问题。
置信度判定标准处理方式
已追踪完整攻击路径,确认可利用上报时附带利用场景和修复方案
攻击路径部分确认,存在不确定环节标记为待验证后上报
理论存在风险或已在其他环节被缓解不上报
针对每一项高置信度发现,需提供以下全部五个要素:
  1. 入口点 — 攻击者如何入侵?(fork提交的PR、issue评论、分支名等)
  2. Payload — 攻击者发送的内容是什么?(实际代码/YAML/输入内容)
  3. 执行机制 — Payload如何被执行?(表达式展开、拉取代码+运行脚本等)
  4. 影响 — 攻击者可获得什么权限?(令牌窃取、代码执行、仓库写入权限)
  5. PoC概要 — 攻击者需执行的具体步骤
如果你无法凑齐全部五个要素,标记为中置信度(待验证)上报。

Step 1: Classify Triggers and Load References

步骤1:分类触发器并加载参考资料

For each workflow, identify triggers and load the appropriate reference:
Trigger / PatternLoad Reference
pull_request_target
references/pwn-request.md
issue_comment
with command parsing
references/comment-triggered-commands.md
${{ }}
in
run:
blocks
references/expression-injection.md
PATs / deploy keys / elevated credentials
references/credential-escalation.md
Checkout PR code + config file loading
references/ai-prompt-injection-via-ci.md
Third-party actions (especially unpinned)
references/supply-chain.md
permissions:
block or secrets usage
references/permissions-and-secrets.md
Self-hosted runners, cache/artifact usage
references/runner-infrastructure.md
Any confirmed finding
references/real-world-attacks.md
Load references selectively — only what's relevant to the triggers found.
针对每个工作流,识别触发器并加载对应的参考资料:
触发器/模式加载的参考资料
pull_request_target
references/pwn-request.md
带命令解析的
issue_comment
references/comment-triggered-commands.md
run:
块中的
${{ }}
表达式
references/expression-injection.md
PAT/部署密钥/高权限凭证
references/credential-escalation.md
拉取PR代码+加载配置文件
references/ai-prompt-injection-via-ci.md
第三方action(尤其是未固定版本的)
references/supply-chain.md
permissions:
块或密钥使用
references/permissions-and-secrets.md
自托管runner、缓存/制品使用
references/runner-infrastructure.md
任何已确认的发现
references/real-world-attacks.md
有选择地加载参考资料——仅加载与发现的触发器相关的内容即可。

Step 2: Check for Vulnerability Classes

步骤2:检查漏洞类型

Check 1: Pwn Request

检查1:Pwn请求

Does the workflow use
pull_request_target
AND check out fork code?
  • Look for
    actions/checkout
    with
    ref:
    pointing to PR head
  • Look for local actions (
    ./.github/actions/
    ) that would come from the fork
  • Check if any
    run:
    step executes code from the checked-out PR
工作流是否使用了
pull_request_target
且拉取了fork的代码?
  • 查找
    ref:
    指向PR HEAD的
    actions/checkout
    步骤
  • 查找可能来自fork的本地action(
    ./.github/actions/
  • 检查是否有
    run:
    步骤执行了拉取的PR中的代码

Check 2: Expression Injection

检查2:表达式注入

Are
${{ }}
expressions used inside
run:
blocks in externally-triggerable workflows?
  • Map every
    ${{ }}
    expression in every
    run:
    step
  • Confirm the value is attacker-controlled (PR title, branch name, comment body — not numeric IDs, SHAs, or repository names)
  • Confirm the expression is in a
    run:
    block, not
    if:
    ,
    with:
    , or job-level
    env:
可被外部触发的工作流的
run:
块中是否使用了
${{ }}
表达式?
  • 枚举所有
    run:
    步骤中的每一个
    ${{ }}
    表达式
  • 确认表达式的值是攻击者可控的(PR标题、分支名、评论内容,而非数字ID、SHA或仓库名)
  • 确认表达式位于
    run:
    块中,而非
    if:
    with:
    或任务级别的
    env:

Check 3: Unauthorized Command Execution

检查3:未授权命令执行

Does an
issue_comment
-triggered workflow execute commands without authorization?
  • Is there an
    author_association
    check?
  • Can any GitHub user trigger the command?
  • Does the command handler also use injectable expressions?
issue_comment
触发的工作流是否在未授权的情况下执行命令?
  • 是否有
    author_association
    校验?
  • 任意GitHub用户都可以触发该命令吗?
  • 命令处理逻辑是否同时使用了可注入的表达式?

Check 4: Credential Escalation

检查4:凭证提权

Are elevated credentials (PATs, deploy keys) accessible to untrusted code?
  • What's the blast radius of each secret?
  • Could a compromised workflow steal long-lived tokens?
高权限凭证(PAT、部署密钥)是否可被不可信代码访问?
  • 每个密钥的影响范围有多大?
  • 被攻陷的工作流是否可以窃取长期有效的令牌?

Check 5: Config File Poisoning

检查5:配置文件投毒

Does the workflow load configuration from PR-supplied files?
  • AI agent instructions:
    CLAUDE.md
    ,
    AGENTS.md
    ,
    .cursorrules
  • Build configuration:
    Makefile
    , shell scripts
工作流是否从PR提供的文件中加载配置?
  • AI Agent指令:
    CLAUDE.md
    AGENTS.md
    .cursorrules
  • 构建配置:
    Makefile
    、shell脚本

Check 6: Supply Chain

检查6:供应链风险

Are third-party actions securely pinned?
第三方action是否被安全地固定了版本?

Check 7: Permissions and Secrets

检查7:权限与密钥

Are workflow permissions minimal? Are secrets properly scoped?
工作流权限是否遵循最小权限原则?密钥的作用域是否合理?

Check 8: Runner Infrastructure

检查8:Runner基础设施

Are self-hosted runners, caches, or artifacts used securely?
自托管runner、缓存、制品的使用是否安全?

Safe Patterns (Do Not Flag)

安全模式(不要标记)

Before reporting, check if the pattern is actually safe:
PatternWhy Safe
pull_request_target
WITHOUT checkout of fork code
Never executes attacker code
${{ github.event.pull_request.number }}
in
run:
Numeric only — not injectable
${{ github.repository }}
/
github.repository_owner
Repo owner controls this
${{ secrets.* }}
Not an expression injection vector
${{ }}
in
if:
conditions
Evaluated by Actions runtime, not shell
${{ }}
in
with:
inputs
Passed as string parameters, not shell-evaluated
Actions pinned to full SHAImmutable reference
pull_request
trigger (not
_target
)
Runs in fork context with read-only token
Any expression in
workflow_dispatch
/
schedule
/
push
to protected branches
Requires write access — outside threat model
Key distinction:
${{ }}
is dangerous in
run:
blocks (shell expansion) but safe in
if:
,
with:
, and
env:
at the job/step level (Actions runtime evaluation).
上报前,先检查对应模式是否确实是安全的:
模式安全原因
不拉取fork代码的
pull_request_target
永远不会执行攻击者的代码
run:
块中的
${{ github.event.pull_request.number }}
仅为数字,无法注入
${{ github.repository }}
/
github.repository_owner
由仓库所有者控制
${{ secrets.* }}
不是表达式注入的攻击向量
if:
条件中的
${{ }}
由Actions runtime求值,而非shell执行
with:
输入中的
${{ }}
作为字符串参数传递,不会被shell求值
固定到完整SHA的action不可变的引用
pull_request
触发器(非
_target
后缀)
在fork上下文中运行,仅持有只读令牌
workflow_dispatch
/
schedule
/向受保护分支
push
触发的工作流中的任意表达式
需要写入权限,不在威胁模型范围内
关键区别
${{ }}
run:
块中是危险的(会触发shell展开),但在任务/步骤级别的
if:
with:
env:
中是安全的(由Actions runtime求值)。

Step 3: Validate Before Reporting

步骤3:上报前校验

Before including any finding, read the actual workflow YAML and trace the complete attack path:
  1. Read the full workflow — don't rely on grep output alone
  2. Trace the trigger — confirm the event and check
    if:
    conditions that gate execution
  3. Trace the expression/checkout — confirm it's in a
    run:
    block or actually references fork code
  4. Confirm attacker control — verify the value maps to something an external attacker sets
  5. Check existing mitigations — env var wrapping, author_association checks, restricted permissions, SHA pinning
If any link is broken, mark MEDIUM (needs verification) or drop the finding.
If no checks produced a finding, report zero findings. Do not invent issues.
上报任何发现前,请阅读完整的工作流YAML文件,追踪完整的攻击路径:
  1. 阅读完整工作流 — 不要仅依赖grep输出
  2. 追踪触发器 — 确认触发事件,检查控制执行的
    if:
    条件
  3. 追踪表达式/代码拉取逻辑 — 确认它位于
    run:
    块中,或确实引用了fork的代码
  4. 确认攻击者可控 — 验证对应值是外部攻击者可以设置的
  5. 检查现有防护措施 — 环境变量封装、
    author_association
    校验、权限限制、SHA版本固定
如果任何环节不成立,标记为中置信度(待验证)或放弃上报该发现。
如果所有检查都没有发现问题,上报零漏洞。不要虚构问题。

Step 4: Report Findings

步骤4:上报发现

markdown
undefined
markdown
undefined

GitHub Actions Security Review

GitHub Actions安全审查

Findings

发现的漏洞

[GHA-001] [Title] (Severity: Critical/High/Medium)

[GHA-001] [漏洞标题](严重程度:严重/高/中)

  • Workflow:
    .github/workflows/release.yml:15
  • Trigger:
    pull_request_target
  • Confidence: HIGH — confirmed through attack path tracing
  • Exploitation Scenario:
    1. [Step-by-step attack]
  • Impact: [What attacker gains]
  • Fix: [Code that fixes the issue]
  • 工作流:
    .github/workflows/release.yml:15
  • 触发器:
    pull_request_target
  • 置信度: 高 — 已通过攻击路径追踪确认
  • 利用场景:
    1. [分步攻击步骤]
  • 影响: [攻击者可获得的权限]
  • 修复方案: [修复问题的代码]

Needs Verification

待验证

[MEDIUM confidence items with explanation of what to verify]
[中置信度项,附带需要验证的内容说明]

Reviewed and Cleared

已审查无风险

[Workflows reviewed and confirmed safe]

If no findings: "No exploitable vulnerabilities identified. All workflows reviewed and cleared."
[已审查并确认安全的工作流]

如果没有发现漏洞,返回:"未发现可利用的漏洞。所有工作流已审查通过。"