mcpField
G
graph-evolution

graph-evolution

github.com/trailofbits安全7.9k3.9k 次安装今天更新
代码仓库

关于 graph-evolution

graph-evolution 是基于 Trailmark 的代码图演变分析技能,面向比较代码快照并审查安全相关变化的团队。它结合原生结构差异和预分析子图差异,发现文本差异不易呈现的攻击路径、污点与权限变化。

主要功能

  1. 01构建并预分析两个代码快照
  2. 02比较节点、边与入口点
  3. 03检测子图成员变化
  4. 04分析污点传播与爆炸半径
  5. 05生成安全导向的差异报告

适用场景

  1. 01比较两个 Git 引用的结构变化
  2. 02审计一段提交中的安全演变
  3. 03发布前检查攻击面与权限边界变化
  4. 04定位复杂度或污点传播增加的函数

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/trailofbits/skills --skill graph-evolution

安装后在 Claude Code 中输入/graph-evolution确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

agents/
openai.yaml
assets/
trail-of-bits-mark.svg
references/
evolution-metrics.md
report-format.md
scripts/
graph_diff.py

详细介绍

Graph Evolution

在两个源代码快照上构建 Trailmark 代码图,并计算结构差异。揭示文本级差异可能遗漏的安全相关变化:新的攻击路径、复杂度变化、影响范围扩大、污点传播变化,以及权限边界调整。

适用场景

  • 比较两个 git 引用,了解结构发生了哪些变化
  • 审计一段提交范围内与安全相关的演变
  • 检测代码变更带来的新攻击路径
  • 找出影响范围或复杂度悄然增长的函数
  • 识别重构前后的污点传播变化
  • 发布前进行结构比较(标签与标签之间,或分支与分支之间)

不适用场景

  • 逐行代码审查(使用 differential-review 进行文本差异分析)
  • 单快照分析(直接使用 trailmark 技能)
  • 根据单个快照生成图表(使用 diagramming-code 技能)
  • 变异测试分类处理(使用 genotoxic 技能)

应当避免的错误认知

错误认知错误原因必须采取的操作
“我们只需要结构差异,跳过预分析”没有预分析,就会遗漏污点变化、影响范围扩大和权限边界变化对两个快照都运行 engine.preanalysis()
“文本差异涵盖了所有变化”文本差异会遗漏新的攻击路径、传递性复杂度变化和子图成员变化使用结构差异作为文本差异的补充
“只有新增节点才重要”移除安全函数和变化的权限边界同样危险审查删除和修改,不要只审查新增项
“可以忽略低严重性结构变化”INFO 级变化(如死代码移除)可能掩盖安全检查的移除对每项变化进行分类,并检查被移除的功能是否有替代实现
“一个快照的图就足以进行比较”单快照分析无法检测演变——需要变更前后的两个快照始终构建并导出两个图
“工具未安装,我会手动比较”手动比较会遗漏图分析能够发现的问题先安装 trailmark
“差异结果为空,所以结构没有变化”trailmark diff 默认将 --language 设为 python,对于其他目标语言会以空数组退出且返回码为 0,因此,无论代码未变化还是语言设置错误,差异结果看起来都一样显式传入 --language 并重新运行,然后再判断是否没有变化

前置条件

必须安装 trailmark。如果 uv run trailmark 运行失败,请执行:

uv tool install trailmark
# Python snippets: uv run --with trailmark python -   (a tool env is not importable)

不要以“手动比较”或阅读源文件来代替运行 trailmark。必须安装工具并以编程方式使用。如果安装失败,请报告错误。


快速开始

# Compare two git refs (e.g., tags, branches, commits)
# 1. Build graphs at each snapshot
# 2. Run pre-analysis on both
# 3. Compute structural diff
# 4. Generate report

# Step-by-step: see Workflow below

决策树

├─ Need to understand what each metric means?
│  └─ Read: references/evolution-metrics.md
│
├─ Need the report output format?
│  └─ Read: references/report-format.md
│
├─ Already have two graph JSON exports?
│  └─ Jump to Phase 3 (run native diff + graph_diff.py)
│
└─ Starting from two git refs?
   └─ Start at Phase 1

工作流

Graph Evolution Progress:
- [ ] Phase 1: Create snapshots (git worktrees)
- [ ] Phase 2: Build graphs + pre-analysis on both snapshots
- [ ] Phase 3: Compute structural diff
- [ ] Phase 4: Interpret diff and generate report
- [ ] Phase 5: Clean up worktrees
阶段 1:创建快照

使用 git worktree 获取每个引用的干净副本,而不影响工作树。

# Create temp directories for worktrees
BEFORE_DIR=$(mktemp -d)
AFTER_DIR=$(mktemp -d)

# Create worktrees (run from repo root)
git worktree add "$BEFORE_DIR" {before_ref}
git worktree add "$AFTER_DIR" {after_ref}

如果比较的是两个目录而不是 git 引用,请跳过此阶段,并在阶段 2 中直接使用目录路径。

阶段 2:构建代码图并运行预分析

为两个快照构建 Trailmark 代码图,并分别运行预分析。预分析会计算影响范围、污点传播、权限边界,并枚举入口点。

from trailmark.query.api import QueryEngine

def build_and_export(target_dir, output_path, language="auto"):
    """Build graph, run pre-analysis, export JSON."""
    engine = QueryEngine.from_directory(target_dir, language=language)
    engine.preanalysis()
    json_str = engine.to_json()
    with open(output_path, "w") as f:
        f.write(json_str)
    return engine.summary()

import tempfile, os
work_dir = tempfile.mkdtemp(prefix="trailmark_evolution_")
before_json = os.path.join(work_dir, "before_graph.json")
after_json = os.path.join(work_dir, "after_graph.json")

before_summary = build_and_export(
    "{before_dir}", before_json
)
after_summary = build_and_export(
    "{after_dir}", after_json
)

检查摘要输出,确认两个代码图均成功构建。如果任一构建失败,请使用显式语言名称或逗号分隔的语言列表重新运行,而不要使用 auto。

阶段 3:计算结构差异

运行两项:

  1. Trailmark 原生结构差异工具,用于节点、边和入口点
  2. 插件的 graph_diff.py 辅助工具,用于子图成员变更

使用阶段 2 中的同一个 work_dir,并传入阶段 2 构建时使用的同一个 --language 值。trailmark diff 默认将该标志设为 python,因此对于任何其他目标语言,默认设置都会以状态码 0 退出,并写入空数组,而不会报告不匹配。

trailmark diff --json --language auto "{before_dir}" "{after_dir}" > "{work_dir}/trailmark_diff.json" || \
  uv run trailmark diff --json --language auto "{before_dir}" "{after_dir}" > "{work_dir}/trailmark_diff.json"

uv run {baseDir}/scripts/graph_diff.py \
    --before "{before_json}" \
    --after "{after_json}" > "{work_dir}/subgraph_diff.json"

如果阶段 2 使用了显式语言,或使用了逗号分隔的语言列表而不是 auto,请在此处使用相同的值。

如果任一差异命令失败,或写入了空 JSON 文件,请停止并报告错误,不要继续阶段 4。

如果 trailmark_diff.json 中的 nodes、edges 和 entrypoints 数组全部为空,说明要么结构没有变化,要么两个快照解析出的图几乎为空。请根据阶段 2 的图摘要判断:如果任一快照的节点数为 0,或相对于目标而言少得不合理,就说明解析未能识别代码——请显式指定语言集合(rust、solidity、python,rust),然后重新运行。两个快照的节点数都正常且差异为空,则说明结构确实稳定。

原生 Trailmark 差异包含:

键内容
summary_delta节点/边/入口点数量的变化
nodes.added新增的函数、类、方法
nodes.removed已删除的函数、类、方法
nodes.modifiedCC、参数或代码行范围发生变化的函数
edges.added新增的调用/继承/导入关系
edges.removed已删除的关系
entrypoints新增、删除和修改的入口点

子图差异包含:

键内容
subgraphs各子图的成员变更(tainted、high_blast_radius 等)
阶段 4:解读差异并生成报告

读取两个差异 JSON 文件,并生成一份以安全为重点的 Markdown 报告。 完整模板请参见 references/report-format.md。

解读优先级(从高到低):

  1. 新增受污染路径——进入 tainted 子图的节点,尤其是同时出现在指向敏感函数的新增边中的节点
  2. 权限边界变更——结合原生入口点/边差异和子图差异,识别新增或移除的信任转换
  3. 攻击面扩大——从 trailmark_diff.json 中识别新增入口点,尤其是 untrusted_external
  4. 影响范围扩大——进入 high_blast_radius 的节点
  5. 复杂度激增——受污染节点或可从入口点到达的节点的 CC 增幅 > 3
  6. 结构新增——新增节点和边(需要审查)
  7. 结构移除——确认已移除的安全函数是否已被替换

将结构变更与 git diff {before_ref}..{after_ref} 交叉比对,为发现结果补充源代码层面的背景。

严重性分类:

严重性结构信号
严重新增通往敏感函数的受污染路径,移除认证边界
高新增入口点且影响范围扩大,受污染节点的 CC 大幅增加
中新增跨越信任边界的边,CC 适度增加
低新增节点,但无法从入口点到达
信息移除死代码,复杂度降低

详细的指标定义请参见 references/evolution-metrics.md。

阶段 5:清理

报告写入后,移除 Git 工作树:

git worktree remove "{before_dir}"
git worktree remove "{after_dir}"

Diff 参考

trailmark diff --json --language auto BEFORE AFTER
uv run {baseDir}/scripts/graph_diff.py [OPTIONS]

trailmark diff --language 默认为 python。对于其他语言的目标,该默认设置仍会以状态码 0 退出,并输出格式正确、nodes、edges 和 entrypoints 数组均为空的 JSON,因此请务必传入该标志:auto 会检测并合并目标下找到的所有受支持语言;单个名称(rust、solidity)或逗号分隔的列表(python,rust)则会指定明确的语言集合。如果快照中没有可解析的内容,auto 会明确报错 No supported languages detected under <path>,这正是你想要的结果。先确认语言,之后空 diff 才能作为没有任何变化的证据。

使用 trailmark diff 进行:

  • 节点/边变更
  • 新增/移除/修改的入口点
  • 易读的结构差异报告

使用 graph_diff.py 进行:

  • 从 engine.preanalysis() 得出的子图成员变更
  • tainted、high_blast_radius、privilege_boundary 及相关集合
参数默认值描述
--before必填“之前”图 JSON 的路径
--after必填“之后”图 JSON 的路径
--indent2JSON 输出缩进

graph_diff.py 输入格式:来自 engine.to_json() 的 Trailmark JSON 导出内容。 graph_diff.py 输出:节点、边和子图的 JSON 结构差异。


质量检查清单

交付报告前:

  • 两个图均成功构建(检查摘要)
  • 已对两个快照运行预分析
  • 已计算原生 Trailmark diff(trailmark_diff.json);如果结果为空,则两个快照的 Phase 2 节点计数均非零,因此空结果意味着结构稳定
  • 已计算子图 diff,且结果非空(subgraph_diff.json)
  • 已解读所有子图变更(tainted、爆炸半径等)
  • 关键发现均包含证据(节点 ID、边差异)
  • 已为所有发现指定严重级别
  • 已通过 git diff 交叉引用补充源代码上下文
  • 已清理工作树(或移除临时目录)
  • 报告已写入 GRAPH_EVOLUTION_*.md

集成

trailmark skill: Phase 2 使用 trailmark API 构建图并进行预分析。 所有 trailmark 查询模式均可用于任一快照的引擎。

differential-review skill: 使用 graph-evolution 进行结构分析,使用 differential-review 进行逐行代码审查。两者相辅相成:graph-evolution 能发现文本 diff 无法捕捉的攻击路径,而 differential-review 则提供 git blame 上下文和微观对抗性分析。

trailmark-review-gate skill: 当分支、拉取请求、修复提交或发布 diff 需要生成 PASS/WARN/FAIL/UNKNOWN 结构审查包时,在 graph-evolution 之后使用 trailmark-review-gate。该门禁会对 graph-evolution 输出应用确定性的审查规则;它不能取代人工审查。

genotoxic skill: 如果 graph-evolution 发现新的高 CC tainted 节点,请将其交给 genotoxic 进行变异测试分诊。

diagramming-code skill: 生成前后对比图,以可视化结构变更。 使用聚焦于变更节点的 call-graph 或 data-flow 图。


支持文档

评论

支持 Markdown