- SAST
- 供应链安全
- CI/CD
【免费下载链接】zizmor
Static analysis for GitHub Actions (and more)
本文以 zizmor 仓库的 crates/README.md 为骨架,逐一梳理其 Rust 工作区中 10 个 crate 的定位、实现细节与相互依赖关系。zizmor 是一个面向 GitHub Actions(及 Dependabot、pre-commit 配置)的静态分析工具,它把“解析 YAML / 解析表达式 / 建模 / 审计 / 输出报告”拆解成多个可独立发布的小型 crate。读完本文,你将掌握整个仓库的模块边界、每个 crate 的入口文件与关键实现,以及如何基于这些 crate 二次开发自己的安全分析工具。
一、工作区总览:一个 workspace,十个 crate
仓库根目录的 Cargo.toml 定义了名为zizmor的 Cargo workspace,采用resolver = "2",共包含 10 个成员:
[workspace] resolver = "2" members = [ "crates/github-actions-expressions", "crates/github-actions-models", "crates/pre-commit-models", "crates/subfeature", "crates/tree-sitter-iter", "crates/yamlpatch", "crates/yamlpath", "crates/zizmor", "crates/zizmor-dev", "crates/zizmor-sarif", ]工作区还统一了所有 crate 的版本号(version = "1.30.1")、Rust 版本要求(rust-version = "1.97.0")、edition(2024)与许可证(MIT)。其中 crates/zizmor 是核心可执行程序,其余 crate 大多作为其依赖存在,例如 crates/zizmor/Cargo.toml 的[dependencies]中直接引用了github-actions-expressions、github-actions-models、pre-commit-models、subfeature、yamlpatch、yamlpath、zizmor-sarif等内部 crate,并外挂了tree-sitter、tree-sitter-bash、tree-sitter-powershell、tree-sitter-yaml、regex、clap、tokio、tower-lsp-server等依赖。
crates/README.md 给出的 crate 索引表如下(链接已转换为仓库根目录相对路径):
| Crate | 说明 |
|---|---|
| zizmor | zizmorCLI 与核心审计功能 |
| zizmor-dev | 供zizmor测试与基准测试使用的共享辅助工具 |
| subfeature | Subfeature 处理 API |
| yamlpath | 保留格式的 YAML 特性(feature)提取 |
| yamlpatch | 保留注释与格式的 YAML 补丁操作 |
| github-actions-models | GitHub Actions 工作流、action 及相关组件的非官方高质量数据模型 |
| github-actions-expressions | GitHub Actions 表达式的解析器与库 |
| tree-sitter-iter | 用于 tree-sitter CST 的极简前序遍历迭代器 |
| zizmor-sarif | 供zizmor使用的极简 SARIF 2.1.0 数据模型 |
| pre-commit-models | pre-commit 的非官方高质量数据模型 |
整体架构按职责可以划分为五层:CLI 与审计引擎层(zizmor)、YAML 解析层(yamlpath / yamlpatch / subfeature)、语法树迭代层(tree-sitter-iter 及 tree-sitter 各语法)、领域建模层(github-actions-models / github-actions-expressions / pre-commit-models)、输出层(zizmor-sarif / zizmor 内置的多格式输出)。
二、核心层:zizmor——CLI 与审计引擎
crates/zizmor 是整个项目的“大脑”,其src目录结构清晰反映了职责划分:
- CLI 入口:cli.rs 基于
clap定义命令行参数,main.rs 负责程序启动与参数分发; - 审计规则:audit/ 目录下按规则粒度拆分了 50+ 个审计模块,例如
unpinned_uses(未固定版本引用)、excessive_permissions(权限过大)、template_injection(模板注入)、cache_poisoning(缓存投毒)、dangerous_triggers(危险触发器)、unpinned_images(未固定镜像)等,每个规则对应一个独立的.rs文件,便于单独阅读与测试; - 领域模型桥接:models.rs 与 models/ 负责把 YAML 文档映射为可审计的数据结构;
- 输出格式:output/ 支持
plain(人类可读文本)、github(GitHub 注解)、json/v1、sarif以及fix(自动修复建议)等多种报告形式; - 注册表与在线能力:registry.rs、github.rs 承担 Action 注册表查询、GitHub 远程数据获取等任务;
- LSP 支持:lsp.rs 基于
tower-lsp-server提供语言服务器协议能力,该功能通过默认启用的lspfeature 引入(见 Cargo.toml 的[features])。
zizmor 的 feature 设计也值得关注(见 crates/zizmor/Cargo.toml):default = ["lsp"];crater-tests、gh-token-tests、online-tests、tty-tests均为测试专用 feature,其中online-tests需要GH_TOKEN才能执行依赖 GitHub 的在线审计;schemafeature 通过schemars生成zizmor.yml的 JSON Schema。
与审计规则一一对应的集成测试位于 crates/zizmor/tests/integration/audit,每个规则都配有真实/合成的test-data用例,例如 crates/zizmor/tests/integration/test-data/unpinned-uses.yml、crates/zizmor/tests/integration/test-data/template-injection.yml 等,可作为理解各审计规则触发条件的现成样例。
三、YAML 解析层:yamlpath 与 yamlpatch
GitHub Actions 工作流是 YAML 文件,而 zizmor 需要在保留原始格式的前提下分析它们,这正是 yamlpath / yamlpatch 存在的原因。
3.1 yamlpath:保留格式的 YAML 特性提取
crates/yamlpath/README.md 解释了其核心动机:常规做法是把 YAML 解析成文档对象再解释,但该解析过程是破坏性的——它会抹掉注释和精确格式。而安全工具的用户习惯以“第几行第几列”来理解问题,而不是“文档层级中的某个子对象”。yamlpath 正是为了弥合这两个视角的鸿沟:让程序先基于文档视图操作,再把结果“翻译”回人类可理解的原始输入坐标。
实现层面,yamlpath 底层依赖tree-sitter与tree-sitter-yaml(见 crates/yamlpath/Cargo.toml),其核心实现在 src/lib.rs。仓库还提供了丰富的测试用例集:crates/yamlpath/tests/testcases 下覆盖了锚点(anchors-basic.yml、anchors-nested.yml)、注释、指令、flow 风格、带引号的键、键缺失等场景,对应的集成测试见 crates/yamlpath/tests/integration_test.rs。
注意:正如其 README 强调的,yamlpath 不是 JSONPath / jq 之类的完整查询语言替代品,它只负责“保留格式的特性提取”。
3.2 yamlpatch:保留注释与格式的补丁操作
yamlpatch 在 yamlpath 之上提供“外科手术式”修改能力,核心诉求是:修改后仍保留注释、缩进、块/流式风格、单/多行样式等人类可读要素,避免传统“解析—改模型—重新序列化”方案对版本控制与人工评审的破坏。
其支持的操作(见 crates/yamlpatch/README.md 与 crates/yamlpatch/tests/unit_tests.rs):
- Replace:替换指定路径上的值;
- Add:向映射中新增键值对;
- Remove:删除键或元素;
- MergeInto:把值合并进已有映射;
- Append:向块序列追加条目;
- ReplaceComment:替换与特性关联的注释;
- EmplaceComment:插入或更新特性关联的注释;
- RewriteFragment:重写字符串值中的局部片段(对模板类场景尤其有用)。
每项操作都以“尽力保留文档格式与结构”为原则,单元测试 crates/yamlpatch/tests/unit_tests.rs 验证了这些行为的正确性。在 zizmor 中,fix输出模式(output/fix.rs)正是依托这类补丁能力向用户呈现可执行的修复建议。
3.3 subfeature:特性/子特性抽象
crates/subfeature 提供“subfeature”处理 API。其 README 给出的定义:subfeature 是 feature 的子集,而 feature 是 zizmor 对“YAML 文档中一个语法相关提取片段”的术语。该 crate 提供创建 subfeature 并将其与父 feature 匹配的 API,核心实现在 crates/subfeature/src/lib.rs。简单理解:yamlpath 从原始文档中提取出一个个“feature”(带位置信息的片段),subfeature 则负责在这些 feature 之上做更细粒度的子集划分与匹配,为审计规则定位“某一行”级别的证据提供支撑。
四、语法树迭代层:tree-sitter-iter
tree-sitter-iter 是一个极简工具库:为 tree-sitter 的 CST(具体语法树)提供前序遍历迭代器。其 README 给出了完整用法:
use tree_sitter_iter::TreeIter; let tree: tree_sitter::Tree = parse(); // Your parsing logic here. for node in TreeIter::new(&tree) { println!("Node kind: {}", node.kind()); }由于TreeIter实现了标准Iteratortrait,可以自由组合迭代器方法,例如只筛选特定类型的节点:
for node in TreeIter::new(&tree).filter(|n| n.kind() == "call") { // Do something with each "call" node. }从性能角度看,README 明确指出:tree-sitter-iter 的空间与时间复杂度等价于使用TreeCursorAPI 手动遍历,即“与手动使用 TreeCursor 完全一致,但提供了更符合人体工程学的迭代器接口”。该库被 zizmor 用于遍历tree-sitter-bash、tree-sitter-powershell、tree-sitter-yaml生成的语法树(依赖关系见 crates/zizmor/Cargo.toml),是template_injection、insecure_commands等需要分析脚本片段的审计规则的技术底座。
五、领域建模层:三个“模型”crate
5.1 github-actions-models
crates/github-actions-models/README.md 将其定位为“GitHub Actions 工作流、action 与 Dependabot 配置文件的非官方高质量数据模型”。其诞生背景是:从 JSON Schema 自动生成模型“无论从表达力还是工具缺陷角度都行不通”,因此改为手工维护高质量模型。
源码结构印证了这一分工:
- src/lib.rs 为 crate 根;
- src/action.rs 建模 action(含输入/输出描述,测试样例见 crates/github-actions-models/tests/sample-actions);
- src/workflow/ 建模工作流的 event、job 与整体结构(含 event.rs、job.rs、mod.rs);
- src/dependabot/ 建模 Dependabot 配置(v2.rs);
- src/common/expr.rs 承载与表达式相关的通用类型。
该 crate 的集成测试收集了大量来自真实 GitHub 仓库的样例工作流(见 crates/github-actions-models/tests/sample-workflows),这些样例带有指向原始仓库的注释,并沿用其各自的许可证条款。
5.2 github-actions-expressions
github-actions-expressions 是 GitHub Actions 表达式的解析器与库,其 README 列出的关键特性为:
- 对 GitHub Actions 表达式进行忠实解析(faithful parsing);
- 带 span 的 AST 节点(方便定位源码位置,与 yamlpath 的“行列视角”哲学一脉相承);
- 对常量表达式提供有限的求值支持(如
fromJSON、toJSON、字符串函数等)。
实现层面,src/lib.rs 组织起 lexer.rs(词法)、parser.rs(语法)、literal.rs(字面量)、op.rs(运算符)、call.rs(函数调用)、identifier.rs(标识符)、context.rs(求值上下文)等模块。
其测试数据非常系统(crates/github-actions-expressions/tests/testdata):覆盖运算符优先级(operators_precedence.json)、大小写不敏感(operators_case_insensitive.json)、类型强制转换(coerce_boolean.json/coerce_number.json/coerce_string.json)、字符串函数(startsWith.json/endsWith.json/contains.json)、fromJSON/toJSON、索引与点操作(op_dot.json/op_idx.json/op_idx_star.json)、逻辑运算(op_and.json/op_or.json/op_not.json)、比较运算(op_eq.json/op_ne.json/op_gt.json/op_gte.json/op_lt.json/op_lte.json)以及语法错误用例(syntax-errors.json)。配套测试见 crates/github-actions-expressions/tests/languageservices_kat.rs。zizmor 的unsound_condition、unsound_contains、unsound_ternary等审计规则,正是借助该库对工作流中的条件表达式做语义分析(参见 crates/zizmor/src/audit 下的对应模块)。
5.3 pre-commit-models
pre-commit-models 提供 pre-commit 配置与 hook 定义的非官方高质量数据模型,是 zizmor 审计范围从 GitHub Actions 扩展到 pre-commit 生态的桥梁。其结构如下:
- src/lib.rs crate 根;
- src/config.rs 建模
.pre-commit-config.yaml配置; - src/hooks.rs 建模
.pre-commit-hooks.yamlhook 定义; - 测试样例见 crates/pre-commit-models/tests/sample-configs 与 crates/pre-commit-models/tests/sample-hooks,配套测试 test_config.rs、test_hooks.rs。
zizmor 的forbidden_uses、unpinned_uses等审计规则同样支持 pre-commit 场景(测试数据可见 crates/zizmor/tests/integration/test-data/forbidden-uses/pre-commit)。
六、输出层:zizmor-sarif 与多格式报告
zizmor-sarif 是“极简 SARIF 2.1.0 数据模型”,只覆盖 zizmor 当前会输出的字段(见 crates/zizmor-sarif/README.md)。它服务于 zizmor 的 SARIF 输出模式,实现在 output/sarif.rs,使 zizmor 的审计结果可以被 GitHub Code Scanning 等 SARIF 消费者直接使用;仓库内对应的端到端快照测试见 crates/zizmor/tests/integration/e2e/snapshots/integration__e2e__sarif_zizmor_properties.snap。
除了 SARIF,zizmor 的输出层(crates/zizmor/src/output)还提供:
plain(plain.rs):带颜色的终端可读报告;github(github.rs):GitHub Actions 注解格式;json/v1(json/v1.rs):结构化 JSON,其快照见 crates/zizmor/tests/integration/e2e/snapshots/integration__e2e__json_v1__json_v1.snap;fix(fix.rs):借助 yamlpatch 的补丁能力输出修复建议。
七、测试与基准辅助层:zizmor-dev
zizmor-dev 是“共享给 zizmor 集成测试与基准测试的辅助工具”。它的 README 明确警告:该 crate 不提供任何稳定或受保证的接口,其功能仅适用于完整源码检出环境,也就是说它是纯开发期依赖,不会进入生产链路。zizmor 的[dev-dependencies]中确实以路径方式引用它(见 crates/zizmor/Cargo.toml)。仓库根目录的 bench/ 目录(含common.py、conftest.py及多个基准测试用例)与 crates/zizmor/tests/integration 便是其典型使用场景。
八、构建、测试与二次开发
整个工作区用标准 Cargo 命令即可操作:
# 构建全部 crate(含 zizmor CLI) cargo build --release # 运行全部单元测试与集成测试 cargo test仓库通过 rust-toolchain.toml 与 Cargo.toml 中的rust-version = "1.97.0"固定了工具链要求,并使用 mise.toml 管理开发环境;测试基础设施还包括 Makefile 与 pyproject.toml(Python 侧的基准测试工具链)。
对于希望基于这些 crate 二次开发的场景,可以从“按需取用”的角度选型:
- 只想解析 GitHub Actions 表达式 → 直接用 github-actions-expressions(解析器 + span 感知 AST);
- 需要在不丢失注释/格式的前提下分析 YAML → 用 yamlpath;
- 需要程序化修改 YAML 且保留人读要素 → 用 yamlpatch;
- 需要遍历 tree-sitter 语法树 → 用 tree-sitter-iter;
- 需要生成 SARIF 报告 → 用 zizmor-sarif;
- 需要完整的 GitHub Actions / pre-commit 领域模型 → 用 github-actions-models 与 pre-commit-models。
九、小结
zizmor 的 crate 拆分遵循了清晰的“解析 → 建模 → 分析 → 输出”分层:yamlpath / yamlpatch / subfeature 负责“保留格式地读懂 YAML”,tree-sitter-iter 与各类 tree-sitter 语法负责“读懂脚本片段”,github-actions-expressions 负责“读懂表达式”,github-actions-models / pre-commit-models 提供领域数据结构,zizmor 本体完成审计并借助 zizmor-sarif 等输出模块呈现结果,zizmor-dev 则为这套体系提供测试与基准支撑。每个 crate 都配有针对性的单元测试与真实样例,既可以作为 zizmor 自身的构建单元,也可以作为独立的 Rust 库被其他安全/静态分析项目复用。
- SAST
- 供应链安全
- CI/CD
【免费下载链接】zizmor
Static analysis for GitHub Actions (and more)
相关推荐
DBX Rust crates 工作区架构全解:12 个模块化 crate 的职责划分、依赖边界与构建验证
DBX Rust crates 工作区架构全解:12 个模块化 crate 的职责划分、依赖边界与构建验证 本指南以 crates/README.md http
数据库开发者工具桌面应用CLIMCP 服务AI 应用Redux-Saga源码架构:包结构与模块职责解析
Redux Saga源码架构:包结构与模块职责解析 Redux Saga作为Redux生态中处理异步操作的核心中间件,其源码架构采用模块化设计,通过合理的包结构
前端Windows Terminal 源码导读:仓库代码组织规则、目录架构与各模块文件职责详解
Windows Terminal 源码导读:仓库代码组织规则、目录架构与各模块文件职责详解 本文基于仓库中的 代码组织规范文档 https://link.git
桌面应用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考