比較兩個原始碼快照(git 提交、標籤或目錄)的 Trailmark 程式碼圖,找出安全相關的結構變更。可偵測文字 diff 遺漏的新攻擊路徑、複雜度變化、爆炸半徑增長、污點傳播變更以及權限邊界修改。適用於比較提交或標籤間的程式碼、分析結構演化、偵測攻擊面增長、審查稽核快照間的變更,或找出文字 diff 遺漏的安全相關變更。
Graph Evolution
在兩個原始碼快照上建立 Trailmark 程式碼圖,並計算結構化 diff。找出文字層級 diff 遺漏的安全相關變更:新攻擊路徑、複雜度變化、爆炸半徑增長、污點傳播變更以及權限邊界修改。
使用時機
- 比較兩個 git refs 以了解結構上的變化
- 稽核一系列提交中與安全相關的演化
- 偵測程式碼變更所產生的新攻擊路徑
- 找出爆炸半徑或複雜度默默增長的函式
- 識別重構過程中的污點傳播變更
- 發布前的結構比較(標籤對標籤或分支對分支)
不應使用時機
- 逐行程式碼審查(請使用
differential-review進行文字 diff 分析) - 單一快照分析(請直接使用
trailmark技能) - 從單一快照產生圖表(請使用
diagramming-code技能) - 突變測試分類(請使用
genotoxic技能)
應拒絕的合理化藉口
| 合理化藉口 | 錯誤原因 | 應採取的動作 |
|---|---|---|
| 「我們只需要結構 diff,跳過預分析」 | 沒有預分析,你會遺漏污點變更、爆炸半徑增長和權限邊界變化 | 在兩個快照上執行 engine.preanalysis() |
| 「文字 diff 就能涵蓋變更」 | 文字 diff 遺漏新攻擊路徑、傳遞性複雜度變化和子圖成員變更 | 使用結構 diff 來補充文字 diff |
| 「只有新增的節點才重要」 | 移除的安全函式和變更的權限邊界同樣危險 | 審查移除和修改,而不只是新增 |
| 「低嚴重性的結構變更可以忽略」 | INFO 層級的變更(如移除死碼)可能掩蓋了安全檢查的移除 | 分類每一項變更,審查移除是否被新功能取代 |
| 「一個快照的圖就夠比較了」 | 單一快照分析無法偵測演化——你需要前後兩個快照 | 務必建立並匯出兩個圖 |
| 「工具沒安裝,我手動比較」 | 手動比較會遺漏圖分析能捕捉到的東西 | 先安裝 trailmark |
前置需求
trailmark 必須已安裝。如果 uv run trailmark 失敗,請執行:
uv pip install trailmark
不要退而求其次使用「手動比較」或閱讀原始檔來取代執行 trailmark。必須安裝工具並以程式化方式使用。如果安裝失敗,請回報錯誤。
快速開始
# 比較兩個 git refs(例如標籤、分支、提交)
# 1. 在每個快照建立圖
# 2. 對兩者執行預分析
# 3. 計算結構 diff
# 4. 產生報告
# 逐步操作:請參閱下方工作流程
決策樹
├─ 需要了解每個指標的意義?
│ └─ 閱讀:references/evolution-metrics.md
│
├─ 需要報告輸出格式?
│ └─ 閱讀:references/report-format.md
│
├─ 已經有兩個圖的 JSON 匯出?
│ └─ 跳到階段 3(執行原生 diff + graph_diff.py)
│
└─ 從兩個 git refs 開始?
└─ 從階段 1 開始
工作流程
Graph Evolution 進度:
- [ ] 階段 1:建立快照(git worktrees)
- [ ] 階段 2:在兩個快照上建立圖 + 預分析
- [ ] 階段 3:計算結構 diff
- [ ] 階段 4:解讀 diff 並產生報告
- [ ] 階段 5:清理 worktrees
階段 1:建立快照
使用 git worktrees 取得每個 ref 的乾淨副本,而不影響工作目錄。
# 為 worktrees 建立暫存目錄
BEFORE_DIR=$(mktemp -d)
AFTER_DIR=$(mktemp -d)
# 建立 worktrees(從 repo 根目錄執行)
git worktree add "$BEFORE_DIR" {before_ref}
git worktree add "$AFTER_DIR" {after_ref}
如果是比較兩個目錄而非 git refs,則跳過此階段,直接在階段 2 中使用目錄路徑。
階段 2:建立圖並執行預分析
為兩個快照建立 Trailmark 圖,並對每個圖執行預分析。預分析會計算爆炸半徑、污點傳播、權限邊界和進入點列舉。
from trailmark.query.api import QueryEngine
def build_and_export(target_dir, output_path, language="auto"):
"""建立圖、執行預分析、匯出 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:計算結構 Diff
執行兩者:
- Trailmark 的原生結構 diff,用於節點、邊和進入點
- 插件的
graph_diff.py輔助工具,用於子圖成員變更
使用階段 2 中相同的 work_dir:
trailmark diff --json "{before_dir}" "{after_dir}" > "{work_dir}/trailmark_diff.json" || \
uv run trailmark diff --json "{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"
如果任一 diff 命令失敗或寫入空的 JSON 檔案,請停止並回報錯誤,而不是繼續到階段 4。
原生 Trailmark diff 包含:
| 鍵 | 內容 |
|---|---|
summary_delta |
節點/邊/進入點數量的變化 |
nodes.added |
新增的函式、類別、方法 |
nodes.removed |
刪除的函式、類別、方法 |
nodes.modified |
CC、參數、行範圍有變化的函式 |
edges.added |
新的呼叫/繼承/匯入關係 |
edges.removed |
刪除的關係 |
entrypoints |
新增、移除和修改的進入點 |
子圖 diff 包含:
| 鍵 | 內容 |
|---|---|
subgraphs |
每個子圖的成員變更(tainted、high_blast_radius 等) |
階段 4:解讀 Diff 並產生報告
讀取兩個 diff JSON 檔案,並產生以安全為重點的 markdown 報告。
完整的範本請參閱 references/report-format.md。
解讀優先順序(從高到低):
- 新的污點路徑 — 進入
tainted子圖的節點,特別是如果它們也出現在指向敏感函式的新增邊中 - 權限邊界變更 — 來自原生進入點/邊 diff 以及子圖 diff 的新增或移除信任轉換
- 攻擊面增長 — 新的進入點,特別是來自
trailmark_diff.json的untrusted_external - 爆炸半徑增加 — 進入
high_blast_radius的節點 - 複雜度飆升 — 在污點或進入點可達的節點上 CC 增加 > 3
- 結構新增 — 新的節點和邊(需要審查)
- 結構移除 — 驗證被移除的安全函式已被取代
將結構變更與 git diff {before_ref}..{after_ref} 交叉比對,為發現結果添加上下文。
嚴重性分類:
| 嚴重性 | 結構訊號 |
|---|---|
| CRITICAL | 新的污點路徑通往敏感函式,移除驗證邊界 |
| HIGH | 新的進入點 + 高爆炸半徑,污點節點上 CC 大幅增加 |
| MEDIUM | 新的跨信任邊界邊,CC 適度增加 |
| LOW | 新增節點但無法從進入點到達 |
| INFO | 死碼移除,複雜度降低 |
詳細的指標定義請參閱 references/evolution-metrics.md。
階段 5:清理
在報告寫入後移除 git worktrees:
git worktree remove "{before_dir}"
git worktree remove "{after_dir}"
Diff 參考
trailmark diff --json BEFORE AFTER
uv run {baseDir}/scripts/graph_diff.py [OPTIONS]
使用 trailmark diff 來:
- 節點/邊的變更
- 新增/移除/修改的進入點
- 人類可讀的結構 diff 報告
使用 graph_diff.py 來:
- 從
engine.preanalysis()衍生的子圖成員變更 tainted、high_blast_radius、privilege_boundary及相關集合
| 參數 | 預設值 | 說明 |
|---|---|---|
--before |
必要 | 「之前」圖 JSON 的路徑 |
--after |
必要 | 「之後」圖 JSON 的路徑 |
--indent |
2 |
JSON 輸出縮排 |
graph_diff.py 輸入格式:來自 engine.to_json() 的 Trailmark JSON 匯出。
graph_diff.py 輸出:節點、邊和子圖的 JSON 結構 diff。
品質檢查清單
在交付報告之前:
- [ ] 兩個圖都成功建立(檢查摘要)
- [ ] 對兩個快照都執行了預分析
- [ ] 原生 Trailmark diff 已計算且非空(
trailmark_diff.json) - [ ] 子圖 diff 已計算且非空(
subgraph_diff.json) - [ ] 所有子圖變更都已解讀(污點、爆炸半徑等)
- [ ] 關鍵發現包含證據(節點 ID、邊 diff)
- [ ] 所有發現都已指定嚴重性等級
- [ ] 透過 git diff 交叉比對添加上下文
- [ ] Worktrees 已清理(或暫存目錄已移除)
- [ ] 報告已寫入
GRAPH_EVOLUTION_*.md
整合
trailmark 技能:
階段 2 使用 trailmark API 進行圖建立和預分析。所有 trailmark 查詢模式都適用於任一快照的引擎。
differential-review 技能:
使用 graph-evolution 進行結構分析,使用 differential-review 進行逐行程式碼審查。兩者互補——graph-evolution 找出文字 diff 遺漏的攻擊路徑,而 differential-review 提供 git blame 上下文和微觀對抗性分析。
trailmark-review-gate 技能:
在分支、拉取請求、修復提交或發布 diff 需要 PASS/WARN/FAIL/UNKNOWN 結構審查封包時,在 graph-evolution 之後使用 trailmark-review-gate。該閘門對 graph-evolution 輸出套用確定性審查規則;它不取代人工審查。
genotoxic 技能:
如果 graph-evolution 顯示新的高 CC 污點節點,將它們提供給 genotoxic 進行突變測試分類。
diagramming-code 技能:
產生前後圖表以視覺化結構變更。使用專注於變更節點的 call-graph 或 data-flow 圖。
支援文件
- references/evolution-metrics.md —
每個結構指標的意義及其對安全的重要性 - references/report-format.md —
報告範本、嚴重性分類和範例發現






