关于 flowcheck
flowcheck 静态审计 GitHub Actions 工作流中的权限过宽、风险触发器和机密暴露问题,适用于仓库维护者。它提供逐文件、作业和行号的证据,并生成逐作业的最小权限方案。
主要功能
- 01审计工作流权限与触发器
- 02按文件、作业和行号报告发现
- 03推断各作业的最小权限
- 04输出强化后的权限块
- 05解析 YAML 而不运行工作流
适用场景
- 01合并前检查工作流安全
- 02查找特权上下文中的拉取请求代码检出
- 03制定更精简的令牌权限
- 04无需联网审计仓库
MCP 相关
其 MCP 服务提供 `audit_workflows` 生成审计报告,以及 `harden_plan` 返回逐作业权限方案和相关发现。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
2收录的工具列表 · 更新于 2 天前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| path | string | 是 | 仓库根目录、.github/workflows 目录或单个工作流文件 |
详细介绍
flowcheck
默认的 GitHub 令牌可以写入你的仓库。大多数工作流从不检查这一点。
每个 GitHub Actions 工作流都会使用一个 GITHUB_TOKEN。使用旧版设置创建的仓库会让该令牌几乎拥有所有内容的写入权限,而没有 permissions: 块的工作流会继承仓库的默认权限。再加上在特权上下文中检出拉取请求代码的触发器形式、整包传递给可复用工作流的机密,以及由 PR 运行写入、再由发布运行读取的缓存,工作流文件本身就成了攻击面。2025 年 tj-actions 遭入侵事件表明,受信任的操作可能迅速变质;多年来,“pwn request”分析也展示了在 pull_request_target 中检出 PR 分支 HEAD 的同类风险。
flowcheck 会审计仓库中的每个工作流,检查最小权限问题,按文件、作业和行展示证据,并根据各作业步骤实际执行的操作推断出一段强化后的权限配置块,供你直接粘贴。
存在的原因
GitHub Actions 是去年已确认的开源供应链攻击面中第二大的,仅次于 npm。这些问题很少有多么罕见:缺少权限配置块、一个 write-all、在 pull_request_target 下检出 ${{ github.event.pull_request.head.sha }},或是自托管运行器响应 PR 触发器。这些问题都能在 YAML 中静态发现,无需运行任何内容。flowcheck 使用真正的解析器读取 YAML(包括锚点、合并键和块标量),绝不执行被扫描仓库中的任何内容;Agent 还可以通过 MCP 调用它,让审计能够在贡献和发布工作流中运行,而不只是手动运行。
同类工具关注的是不同问题:actionpin 检查操作引用是否固定到 SHA。flowcheck 检查权限和触发器是否遵循最小权限原则。两者不共享任何代码。
检查内容
| 规则 | 严重性 | 含义 |
|---|---|---|
FC-PERMS-WRITE-ALL | 严重(顶层)、高(作业级) | permissions: write-all,令令牌的所有权限范围都可写。 |
FC-PR-HEAD-CHECKOUT | 严重 | pull_request_target、workflow_run 或 issue_comment 触发器与检出 PR 头部所指提交的配置结合使用。特权上下文,运行不受信任的代码。 |
FC-NO-TOP-PERMISSIONS | 高或中 | 缺少顶层 permissions: 块。作业会继承仓库默认令牌,而许多仓库的默认令牌具有全部写权限。 |
FC-JOB-GRANT-BROAD | 中(id-token 为高) | 作业(或它继承的顶层块)授予了该作业中没有步骤需要的写权限范围;根据推断出的需求进行判定。 |
FC-SECRETS-INHERIT | 高或中 | 可复用工作流调用使用 secrets: inherit,将仓库的所有密钥交给被调用的工作流。 |
FC-SELF-HOSTED-PR | 高 | 包含可由 PR 触发的触发器的工作流使用了 self-hosted 运行器。在你的机器和网络上运行分支代码。 |
FC-SECRETS-ON-PR | 中 | pull_request 工作流中引用了密钥。同一仓库的 PR 分支在合并前运行时可以使用密钥。 |
FC-CACHE-POISON | 中 | PR 触发器下的工作流包含缓存步骤,且同一工作流还有特权(写入)使用方:被投毒的缓存条目可能会在之后的特权运行中执行。 |
FC-ARTIFACT-WORKSPACE | 中 | actions/upload-artifact 上传了整个工作区(path: .、** 或未指定路径),其中通常包含 .git、配置文件和泄露到磁盘的密钥材料。 |
FC-PERMS-READ-ALL | 低 | 使用 permissions: read-all,而不是显式列出权限范围。 |
FC-NO-TIMEOUT | 低 | 作业未设置 timeout-minutes。平台默认值为 360 分钟。 |
FC-NO-CONCURRENCY | 低 | 具有部署特征的作业(发布、推送镜像或使用 environment)在作业或工作流级别未设置并发保护。 |
FC-DISPATCH-INPUT-INJECTION | 低 | workflow_dispatch 输入被直接插值到 run 步骤中,使自由文本成为 Shell 命令。 |
FC-PARSE-ERROR | 中 | 工作流 YAML 解析失败。 |
判定规则:
- EXPOSED:至少有一项严重或高危发现。退出代码为 1。
- REVIEW:只有中危或低危发现。退出代码为 1。
- HARDENED:没有发现。退出代码为 0。
- 用法错误以代码 2 退出。如果读取方提前关闭输出管道,进程会以 141 退出,绝不会返回 0。
加固方案
对于每个作业,flowcheck 会推断其步骤所需的最低权限:
| 步骤类型 | 推断的权限 |
|---|---|
actions/checkout | contents: read |
创建发布(gh release create、softprops/action-gh-release) | contents: write |
推送镜像(启用了推送的 docker/build-push-action、docker push、buildx --push) | packages: write |
写入 PR(gh pr create、gh pr comment、标签器、过期项处理) | pull-requests: write |
写入议题(gh issue comment 等) | issues: write |
Pages 部署(actions/deploy-pages) | pages: write、id-token: write |
| OIDC 登录(AWS、Azure、Google 身份验证操作,脚本中显式使用 OIDC) | id-token: write |
构建来源证明(actions/attest-build-provenance) | attestations: write、id-token: write |
| 完全不使用令牌 | permissions: {} |
audit 会在发现项旁打印方案;harden 会打印每个作业的权限块和顶层权限并集块。不会向你的文件写入任何内容:方案只会输出,你需要自行粘贴。
用法
CLI
# Audit the current repo
npx @reneturcios/flowcheck audit
# Audit another path, machine readable
npx @reneturcios/flowcheck audit ~/src/my-repo --json
# Just the hardening plan
npx @reneturcios/flowcheck harden ~/src/my-repo
输出示例:
flowcheck audit .: EXPOSED
workflows: 2, jobs: 3, findings: 2 (critical: 1, high: 1, medium: 0, low: 0)
[CRITICAL] .github/workflows/pr.yml:14 FC-PR-HEAD-CHECKOUT (job inspect) Privileged trigger context (pull_request_target) combined with a checkout of the PR head (...)
[HIGH] .github/workflows/pr.yml:1 FC-NO-TOP-PERMISSIONS No top-level permissions block. ...
EXPOSED: at least one critical or high finding. ...
审计无需凭据或网络。仓库规模上限明确可见,绝不会静默生效:每次运行最多处理 500 个工作流文件,每个文件最大为 2 MiB;达到上限时,报告中的 caps 字段和一条说明会指出触发了哪个上限,以及哪些内容未被扫描。
MCP 服务
添加到 MCP 客户端配置中:
{
"mcpServers": {
"flowcheck": {
"command": "npx",
"args": ["-y", "@reneturcios/flowcheck", "mcp"]
}
}
}
工具:audit_workflows(path) 以 JSON 格式返回完整审计报告。harden_plan(path) 返回每个作业的加固权限块、顶层权限并集块,以及它们所解决的发现项。
明确说明限制
- 权限推断采用启发式方法。它会识别上表中的步骤形式,以及常见的
ghCLI 和脚本模式。未知的第三方操作可能需要比计划建议的更多或更少权限;该计划只是供人工审核的起点,并非证明。 - 对于不可见的使用方,宽泛授权问题会经过校准:当作业包含一个无法从工作流文件中看出令牌用途的步骤(未知的第三方操作、本地复合操作,或
actions/github-script步骤;后者可运行任意 JavaScript,且经常会委托给仓库文件),该问题会以低严重性报告,并附上相应说明,而不是中或高严重性。在 v0.1.0 中,flowcheck 不会跟踪本地复合操作文件,也不会跟踪步骤委托执行的脚本。 - PR 头部检出规则需要显式的 ref 表达式指向头部(
pull_request.head、head_ref、workflow_run.head)。在pull_request_target下,不指定 ref 的检出操作会检出基础分支,这是安全的形式,不会被标记。不会跟踪通过环境变量或输出拼接而成的间接头部检出。 - 自托管检测会查找
runs-on中字面上的self-hosted标签;未使用该标签命名的运行器组无法识别。 - 可复用工作流作业仅根据调用位置进行规划;被调用工作流自身的步骤属于单独的审计目标。
- 矩阵生成的作业按原样审计一次,不会针对每种矩阵组合分别审计。
- secrets 规则会统计
pull_request下的引用;不会尝试证明某个特定 secret 是否会传递给某个特定步骤。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。