首页
/ awesome-copilot 的 GitHub Actions Expert 智能体实战指南:构建安全加固的 CI/CD 工作流

awesome-copilot 的 GitHub Actions Expert 智能体实战指南:构建安全加固的 CI/CD 工作流

2026-09-08 20:11:16作者:卓炯娓

GitHub Actions Expert 是 awesome-copilot 仓库(社区贡献的 GitHub Copilot 指令、智能体、技能与配置集合)中面向 CI/CD 的专业智能体定义,其定位是帮助团队设计安全优先、资源高效、运行可靠的 GitHub Actions 工作流,重点覆盖 Action 提交 SHA 固定(pinning)、OIDC 免密钥认证、permissions 最小权限与供应链安全。读完本文你将掌握一整套可直接落地的 GitHub Actions 安全加固方法论,并了解在 Copilot 生态中如何通过该智能体(agents/github-actions-expert.agent.md)与其配套的 github-actions-hardeninggithub-actions-efficiency 技能组合实现“从创建到审计再到优化”的闭环。

智能体定位与能力边界

GitHub Actions Expert 是一个通过 YAML frontmatter 声明元数据的自定义智能体,核心描述为"专注安全 CI/CD 工作流、Action 固定、OIDC 认证、最小权限与供应链安全的 GitHub Actions 专家"。

从定义文件头可以看出其被授予的工具能力:

name: 'GitHub Actions Expert'
description: 'GitHub Actions specialist focused on secure CI/CD workflows, action pinning, OIDC authentication, permissions least privilege, and supply-chain security'
tools: ['github/*', 'search/codebase', 'edit/editFiles', 'execute/runInTerminal', 'read/readFile', 'search/fileSearch']
  • github/*:读写仓库工作流、创建/审阅 PR 等 GitHub 域操作;
  • search/codebasesearch/fileSearch:定位 .github/workflows/*.yml 及各文件依赖关系;
  • edit/editFilesexecute/runInTerminal:产出并验证工作流修改;
  • read/readFile:读取工作流、官方文档与仓库上下文。

其使命一句话概括:设计安全优先、资源高效、可靠自动化的工作流,每条工作流都应遵循最小权限、不可变 Action 引用并集成全面安全扫描。该智能体在设计上假设可读取/搜索整个代码库、可执行终端命令进行本地校验(如 actionlint),这与 docs/README.agents.md 中"通过 VS Code Chat 界面或 CCA 分配使用自定义智能体"的通用激活方式一致。

仓库中的配套资源

skills/github-actions-hardening/SKILL.md 中存在一个面向"审计 .github/workflows/*.yml 安全风险"的姊妹技能,它专门推理通用 linter 发现不了的 Actions 威胁模型——不可信输入脚本注入、特权触发器执行 fork 代码、可变 Action 引用、令牌越权——可作为审查/加固时的方法论支撑;skills/github-actions-efficiency/SKILL.md 则关注如何削减 CI 分钟数与成本。若需要更全面的 CI/CD 最佳实践说明,可参考 instructions/github-actions-ci-cd-best-practices.instructions.md

动工之前:澄清问题清单

智能体在创建或修改工作流前,会按三类主题澄清需求,避免"拍脑袋写 YAML":

分类 澄清要点
工作流目的与范围 工作流类型(CI、CD、安全扫描、发布管理);触发器(push、PR、schedule、手动)与目标分支;目标环境与云厂商;审批要求
安全与合规 安全扫描需求(SAST、依赖审查、容器扫描);合规约束(SOC2、HIPAA、PCI-DSS);密钥管理与 OIDC 可用性;供应链安全要求(SBOM、签名)
性能 预期时长与缓存需求;自托管还是 GitHub 托管运行器;并发要求

这三类问题的答案直接决定后续每一条原则的落地方式——例如合规约束会推导出必须生成 SBOM 与签名,而并发要求则决定 concurrency 配置取向。

安全优先三支柱

1. 权限最小化:从 contents: read 开始

默认在 workflow 顶层只授予 contents: read,仅在确有需要的 job 上按需提升,且只授予完成任务的最小权限。

这一点与配套技能 permissions-and-tokens.md 的底层分析完全一致:每次工作流运行都会自动获得一个 GITHUB_TOKEN,其权限范围就是被攻破时的爆炸半径。如果一个工作流完全没有 permissions: 块,它会继承仓库/组织默认值——在较老或宽松的仓库中,该默认值可能是"对大多数 scope 可读/写"。一次注入命令或一个恶意依赖,就能以该令牌推送代码、发布 Release 或批准 PR。

因此推荐"顶层收紧、逐 job 放行"的模式:

# 顶层默认拒绝一切
permissions: {}

jobs:
  build:
    permissions:
      contents: read        # 该 job 仅需 checkout
    runs-on: ubuntu-latest
    steps: [ ... ]

  comment:
    permissions:
      contents: read
      pull-requests: write  # 该 job 需要发评论,其余一律不给
    runs-on: ubuntu-latest
    steps: [ ... ]

常见的权限 scope 包括 contentspull-requestsissuesactionspackagesid-tokendeploymentschecksstatuses,每个取值为 readwritenone。审查时需要标记的高风险项包括:缺失 permissions: 块、出现 permissions: write-all、job 中声明了从未使用的 write scope、把本应只出现在单个 job 的 write 放在了顶层。

2. Action 固定:用完整提交 SHA 替代可变引用

这是该智能体安全理念的核心,原文给出明确规则:

  • 一律将 Action 固定到完整长度的提交 SHA,以保证安全性与不可变性,例如:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
  • 绝不使用可变引用,如 @main@latest 或仅主版本标签(如 @v4)。原因是标签可被仓库所有者或攻击者静默移动到指向恶意提交,从而发起供应链攻击,在 CI/CD 管线中执行任意代码。
  • 提交 SHA 不可变:一旦确定便无法更改或重定向,能对"到底运行什么代码"提供密码学级保证。
  • 在 SHA 旁追加版本注释(如 # v4.3.1),方便人类快速识别固定版本。
  • 该规则适用于所有 Action,包括第一方 actions/*;尤其要覆盖你无法控制标签变动的第三方 Action。
  • 使用 Dependabot 或 Renovate 自动化 SHA 更新。

skills/github-actions-hardening/references/supply-chain.md 将这条原则上升为分级规则:第三方 Action(任何非 actions/*github/* 的发布者)必须固定 SHA,使用标签或分支记为 HIGH;@main/@master 无论发布者是谁都视为 HIGH(等同未版本化的"latest");第一方 actions/* 固定 SHA 是加固推荐(仅用标签时记为 LOW);始终保留 # vX.Y.Z 尾注供人与 Dependabot 识别预期版本。其背景陈述也值得注意:现实中已出现过流行 Action 的标签被重新指向窃取密钥代码的真实事件,这正是"禁止可变标签"并非教条而是教训的原因。

3. Secrets:只走环境变量,绝不落日志

  • 仅通过环境变量访问 Secrets;
  • 绝不记录日志或写入 outputs;
  • 生产环境使用环境专属 Secrets;
  • 优先 OIDC 而非长期凭证。

配套的 permissions-and-tokens.md 补充了更细的密钥卫生规则:只在需要的 job 中引用 Secrets;绝不在处理 Secrets 的步骤中 echo 或开启 set -x 追踪;不把 Secrets 传入未固定、未审查的第三方 Action;牢记 fork 的 pull_request 运行不会获得任何 Secrets——不要为了"修复"这一点而切换到 pull_request_target(详情见下文触发器一节)。

OIDC:用短期令牌替换长期云凭证

消除长期凭证是云上认证的第一选择:

  • AWS:为 GitHub OIDC provider 配置带信任策略的 IAM Role;
  • Azure:使用 workload identity federation(工作负载身份联合);
  • GCP:使用 workload identity provider(工作负载身份提供方);
  • 前提:工作流需要 id-token: write 权限。

permissions-and-tokens.md 给出了原因与完整可运行示例:把静态云密钥(如 AWS_ACCESS_KEY_ID)存为仓库 Secrets,意味着泄露之后在手动轮换前永久有效;而 OIDC 下工作流请求的是云端信任的短期令牌,作用域限定到该仓库/分支,几分钟内过期。示例(AWS):

permissions:
  id-token: write     # 请求 OIDC 令牌所必需
  contents: read
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: aws-actions/configure-aws-credentials@<sha>
        with:
          role-to-assume: arn:aws:iam::123456789012:role/my-ci-role
          aws-region: us-east-1
      # 不再需要 AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY Secrets

同一模式适用于 Azure(azure/login)、GCP(google-github-actions/auth)、HashiCorp Vault 等。云端一侧应将信任策略作用域限定到具体仓库乃至具体分支/环境,避免 fork 或其他仓库冒用该角色。

并发控制:按场景选择取消策略

concurrency 是防止构建/部署相互踩踏的关键配置:

  • 阻止并发部署:cancel-in-progress: false(让正在进行的部署跑完,排队后续);
  • 取消过期的 PR 构建:cancel-in-progress: true
  • concurrency.group 控制并行执行粒度。

该要点在 skills/github-actions-efficiency/references/actions.md 中被列为"最常被忽略的低风险浪费来源"第二位——缺少 concurrency 取消机制意味着同一 PR 的多次 push 会各自排队全量执行,白烧 CI 分钟。

触发器与特权模型:理解信任边界

真正的 GitHub Actions 安全核心问题是:外部贡献者能否触发此工作流?若能,它拿到什么令牌与 Secrets? 不同触发器答案完全不同。以下信任矩阵来自 skills/github-actions-hardening/references/triggers-and-privilege.md

触发器 谁能触发 GITHUB_TOKEN 可用 Secrets 风险
push 仓库协作者 读/写 低——受信作者
pull_request(同仓库分支) 协作者 读/写
pull_request(来自 fork) 任何人 只读 天然低——即使恶意代码也偷不到东西
pull_request_target 任何有 fork 的人 读/写 ——在基仓库上下文中运行
workflow_run 在另一工作流之后触发 读/写
issue_commentissues 任何人 读/写

其中的"陷阱"在于:fork 的 pull_request 之所以安全,是因为 GitHub 故意削减令牌并扣留 Secrets。维护者发现"fork PR 上 Secrets 不生效"后,常切换到 pull_request_target 以取回 Secrets——结果是把写令牌和全部 Secrets 交到了任意贡献者手中。

pull_request_target 之所以危险,在于它签出的是基仓库的工作流定义(fork 无法改变运行内容),却以完整权限运行。真正的高危场景是工作流随后显式签出fork 的代码并执行它

# 危险——以写令牌 + Secrets 执行远程代码
on: pull_request_target
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@<sha>
        with:
          ref: ${{ github.event.pull_request.head.sha }}   # fork 的代码
      - run: npm install && npm test                        # 执行 fork 的代码与脚本

npm install 本身就会执行 PR 中任意的 lifecycle scripts;在 pull_request_target 下,这些脚本能读取 secrets.* 并用写令牌提交代码。

安全解法是"双工作流模式":无特权工作流运行不可信代码;特权工作流只消费可信的输出。

# 1) 无特权:运行不可信代码,无 Secrets,只读令牌
name: PR Build
on: pull_request
permissions:
  contents: read
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@<sha>
      - run: npm ci && npm run build
      - uses: actions/upload-artifact@<sha>
        with: { name: pr, path: dist/ }
# 2) 特权:由前一个工作流触发,绝不运行 fork 代码
name: PR Comment
on:
  workflow_run:
    workflows: ["PR Build"]
    types: [completed]
permissions:
  pull-requests: write
jobs:
  comment:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/download-artifact@<sha>   # 仅作为数据处理,绝不执行
      # 用受信令牌发布结果——但绝不执行工件

配套规则:将 pull_request_targetworkflow_runissue_commentissues 视为特权触发器;在特权工作流中绝不签出并执行 PR/fork 代码;若只需基于元数据打标签、评论或分诊则无妨;凡是能用 pull_request 解决的,优先使用它安全默认值(只读 + 无 Secrets)。同样的范式也适用于工件与缓存投毒:不可信 PR 构建上传的工件是不可信数据,特权 workflow_run 可下载但只能当数据处理(解压时校验路径,谨防 ../ 路径穿越条目),且不要信任低权限运行填充的缓存。

脚本注入防护:把 ${{ }} 当作注入点

配套技能 skills/github-actions-hardening/references/injection.md 提出了一个核心洞察:${{ <expr> }} 由 runner 在 shell 执行之前展开到脚本文本中。所以 run: echo "Title: ${{ github.event.issue.title }}" 不是传参,而是把攻击者可控文本直接拼进 shell 命令。一个标题为 "; <attacker-command> # 的 issue 会被拼接进脚本并执行。凡是包含外部贡献者可影响数据的 ${{ }},都应视为代码注入点。

攻击者可控的典型上下文包括:github.event.issue.title/.bodygithub.event.pull_request.title/.body.head.ref/.head.labelgithub.head_refgithub.event.comment.bodygithub.event.review.bodygithub.event.commits.*.messagegithub.event.pages.*.page_name 等——一个名为 $(<attacker-command>) 的分支或上述标题,在被插值进 run: 步骤时就会变成 shell。

脆弱模式

# VULNERABLE
- run: |
    echo "Reviewing PR: ${{ github.event.pull_request.title }}"
    git checkout ${{ github.head_ref }}

安全模式——经 env: 中转:把不可信值绑定为环境变量,再引用(并加引号的)shell 变量。shell 变量是数据,不会被重新解析为工作流语法:

# SAFE
- env:
    PR_TITLE: ${{ github.event.pull_request.title }}
    HEAD_REF: ${{ github.head_ref }}
  run: |
    echo "Reviewing PR: $PR_TITLE"
    git checkout "$HEAD_REF"

${{ }} 现在只出现在 env: 一侧、以"赋值"而非"拼入命令"的方式使用;shell 变量要始终加引号("$PR_TITLE")以防分词与通配符展开。actions/github-script 同样如此:不要向 script: 插值 ${{ }},改为经环境变量传递并读取 process.env。自定义 Action 的 with: 输入只有在 Action 自身不再把输入拼进 shell 时才安全,不确定时一律走 env:,或先做白名单校验(例如分支名应匹配 ^[A-Za-z0-9._/-]+$)。另一个细节:actions/checkout 默认会把令牌写入 .git/config(供后续 push),若 job 之后要运行不可信代码,应设置 persist-credentials: false

安全扫描加固:五件套

智能体要求工作流覆盖五类安全扫描(github-actions-ci-cd-best-practices.instructions.md 同样强调这些最佳实践):

  • 依赖审查(Dependency Review):在 PR 上扫描存在漏洞的依赖变更;
  • CodeQL 分析:在 push、PR 与 schedule 上运行 SAST 静态分析;
  • 容器扫描:用 Trivy 或同类工具扫描镜像;
  • SBOM 生成:生成软件物料清单,提供供应链透明性;
  • Secrets 扫描:开启并启用 push protection(推送保护)。

缓存与优化

  • 优先使用内置缓存能力(setup-nodesetup-python 等);
  • actions/cache 缓存依赖;
  • 使用有效缓存键(lockfile 的哈希作为 key);
  • 配置 restore-keys 作为回退。

github-actions-efficiency 给出了更完整的优化审计顺序:先度量再动手——用 gh run list --limit 10gh run view <run_id> --log-failed 查看失败日志,并 rg 检索 on:|concurrency:|paths:|paths-ignore:|strategy:|matrix:|cache: 定位浪费源;随后从"缺依赖缓存、缺并发取消、触发器过宽、跨文件/job 重复覆盖、每次变更都跑昂贵 job"这五类常见浪费入手。矩阵广度要与决策匹配:发布或明确兼容性验证用全矩阵,普通代码变更用单一最新版本腿;格式化/自动修复这类回写分支的 job 应做成 opt-in(PR 标签如 ci:formatworkflow_dispatch 手动触发或显式评论命令),且用标签触发时要同时监听 labeled 与通常的 unlabeled 事件。

工作流校验

  • 用 actionlint 做工作流 lint;
  • 校验 YAML 语法;
  • 在 fork 中先行测试,再在主干仓库启用。

在效能审计语境下(见 skills/github-actions-efficiency/SKILL.md),还强调尽量做活体验证:手动触发一次 workflow_dispatch;用两次快速更新验证过期运行取消;用一个"仅含忽略文件或仅含工作流变更"的增量提交验证路径门控;并在 UI 中确认重型 job 确实被跳过,而非假设。

工作流安全核对清单

以下是智能体合并前逐条核对的可执行清单(可直接作为团队 PR 模板):

  • [ ] Action 固定到完整提交 SHA 并带版本注释(如 uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
  • [ ] 权限遵循最小权限(默认 contents: read
  • [ ] Secrets 仅经环境变量访问
  • [ ] 云认证使用 OIDC
  • [ ] 已配置并发控制
  • [ ] 已实现缓存
  • [ ] 工件保留期设置合理
  • [ ] PR 上启用依赖审查
  • [ ] 已配置安全扫描(CodeQL、容器、依赖)
  • [ ] 已用 actionlint 校验工作流
  • [ ] 生产环境启用环境保护(environment protection)
  • [ ] 已启用分支保护规则
  • [ ] Secrets 扫描开启并带推送保护
  • [ ] 无硬编码凭证
  • [ ] 第三方 Action 来自可信来源

十五条最佳实践汇总

  1. 将 Action 固定到完整提交 SHA 并带版本注释(@<sha> # vX.Y.Z),绝不使用可变标签或分支;
  2. 遵循最小权限;
  3. 绝不记录 Secrets 日志;
  4. 云端访问优先 OIDC;
  5. 实现并发控制;
  6. 缓存依赖;
  7. 设置工件保留策略;
  8. 扫描漏洞;
  9. 合并前校验工作流;
  10. 生产环境使用环境保护;
  11. 启用 Secrets 扫描;
  12. 生成 SBOM 保障透明性;
  13. 审计第三方 Action;
  14. 用 Dependabot 保持 Action 更新(在 supply-chain.md 中给出了启用方式:.github/dependabot.yml 中配置 package-ecosystem: github-actionsschedule.interval: weekly,Dependabot 能识别 # vX.Y.Z 注释并自动提升 SHA,以可审查 PR 的形式送达更新);
  15. 先在 fork 中测试。

重要提醒

  • 默认权限应为只读;
  • OIDC 优于静态凭证;
  • 用 actionlint 校验工作流;
  • 绝不跳过安全扫描;
  • 持续监控工作流失败与异常。

延伸阅读

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
899
5.82 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
920
1.85 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.8 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
532
596
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
521
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
392