关于 trailmark-structural
trailmark-structural 是用于 Trailmark 代码库的安全结构分析技能,面向需要完整预分析数据的审计任务。它运行全部四项预分析,并兼容不同 Trailmark 版本的可用功能。
主要功能
- 01构建 Trailmark 代码图
- 02运行全部四项预分析
- 03提取热点与污点数据
- 04检查爆炸半径和特权边界
- 05输出子图与代理节点信息
适用场景
- 01为安全审计排序代码热点
- 02分析指定目标的结构数据
- 03检查未解析调用与子图关系
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ trailofbits/ skills --skill trailmark-structural安装后在 Claude Code 中输入/trailmark-structural确认已加载。
附带文件agents/openai.yamlassets/trail-of-bits-mark.svg
详细介绍
Trailmark 结构分析
构建 Trailmark 图,并运行 engine.preanalysis() 以计算全部四个预分析阶段。核心工作流兼容 v0.2;仅在确认方法可用后才包含 v0.4 专属细节。较新的版本会丰富相同的输出(0.5.0+ 会为攻击面条目添加 attributes 键,并从 .trailmark/links.toml 添加 proxy.external:* 节点),且无需更改工作流。
适用场景
- Vivisect Phase 1 需要完整的结构数据(热点、污点、影响范围、权限边界)
- 针对特定目标范围执行详细的预分析阶段
- 生成复杂度和污点数据,以便确定审计优先级
- 安装了 Trailmark 0.4.0+ 时,检查代理/未解析调用的数量、子图边或类型引用摘要
不适用场景
- 只需要快速概览(改用
trailmark-summary) - 临时查询代码图(直接使用主
trailmark技能) - 目标是单个小文件,结构分析无法带来价值
应拒绝的理由
| 理由 | 错误之处 | 必须采取的操作 |
|---|---|---|
| “摘要分析已经足够” | 摘要会跳过污点、影响范围和权限边界数据 | 需要详细数据时,运行完整的结构分析 |
| “一个阶段就够了” | 各阶段会相互交叉引用——只分析污点而不分析影响范围会遗漏关键节点 | 运行全部四个阶段 |
| “工具未安装,我会手动分析” | 手动分析会遗漏工具能够捕获的信息 | 报告“未安装 trailmark”并返回 |
| “阶段输出为空意味着阶段失败” | 对某些代码库,部分阶段不会产生数据(例如,没有权限边界) | 无论如何都返回完整输出 |
| “v0.4 字段一定存在” | 用户可能仍安装着 Trailmark 0.2.x | 查询 v0.4 专属方法前,使用 hasattr() 探测 |
用法
目标目录通过 args 参数传入。
执行
步骤 1:检查 trailmark 是否可用。
trailmark analyze --help 2>/dev/null || \
uv run trailmark analyze --help 2>/dev/null
如果两个命令都无法运行,报告“未安装 trailmark”
并返回。不要运行 pip install、uv pip install、
git clone 或任何安装命令。用户必须自行安装
trailmark。
可选:记录版本:
trailmark --version 2>/dev/null || uv run trailmark --version 2>/dev/null || true
如果缺少此命令,不要因此失败;使用下方的 API 功能探测。
步骤 2:使用 Trailmark 的解析 API 检测语言。
python3 - "{args}" <<'PY'
import json
import sys
try:
from trailmark.parse import detect_languages # canonical location since 0.3.x
except ModuleNotFoundError:
# v0.2.x predates trailmark.parse; the same function lives in query.api
from trailmark.query.api import detect_languages
print(json.dumps(detect_languages(sys.argv[1])))
PY
如果导入失败,使用 uv run --with trailmark python - "{args}" 重新运行相同的代码段。
如果结果为 [],报告“Trailmark 未在目标目录中找到受支持的语言”
并返回。
步骤 3:通过 QueryEngine 运行完整的结构分析。
使用 python3 运行此代码段。如果导入失败,使用
uv run --with trailmark python - "{args}" 重新运行相同的代码段。
python3 - "{args}" <<'PY'
import json
import sys
try:
from trailmark.parse import detect_languages # canonical location since 0.3.x
except ModuleNotFoundError:
# v0.2.x predates trailmark.parse; the same function lives in query.api
from trailmark.query.api import detect_languages
from trailmark.query.api import QueryEngine
target = sys.argv[1]
languages = detect_languages(target)
engine = QueryEngine.from_directory(target, language="auto")
preanalysis = engine.preanalysis()
def summarize_subgraph(name: str, limit: int = 25) -> dict[str, object]:
nodes = engine.subgraph(name)
summary = {
"count": len(nodes),
"sample_ids": [node["id"] for node in nodes[:limit]],
}
if hasattr(engine, "subgraph_edges"):
summary["edge_count"] = len(engine.subgraph_edges(name))
return summary
graph = json.loads(engine.to_json())
nodes = graph.get("nodes", {})
proxy_nodes = [
node_id for node_id, node in nodes.items()
if node.get("kind") == "proxy" or node.get("origin") == "proxy"
]
payload = {
"languages": languages,
"summary": engine.summary(),
"preanalysis": preanalysis,
"attack_surface": engine.attack_surface()[:25],
"hotspots": engine.complexity_hotspots(10)[:25],
"proxy_nodes": proxy_nodes[:25],
"subgraphs": {
name: summarize_subgraph(name)
for name in engine.subgraph_names()
},
}
if hasattr(engine, "type_references"):
payload["type_reference_samples"] = {
node_id: engine.type_references(node_id)[:10]
for node_id in list(nodes)[:25]
}
print(json.dumps(payload, indent=2))
PY
步骤 4:验证输出。
输出应包含:
languagessummarypreanalysishotspots(可能为空)proxy_nodes(在 v0.2.x 中为空,或在没有未解析调用时为空;在 0.5.0+ 中可能包含.trailmark/links.toml中声明的proxy.external:*条目)subgraphs,包含计数和示例 ID
在 Trailmark 0.5.0+ 中,attack_surface 条目可能带有 attributes
对象(例如 solidity_visibility、solidity_overridden_by)。原样传递该对象——下游使用者会用它为入口点排序。
对于某些代码库,部分子图可能没有节点(这是 正常现象)。无论如何都返回完整的 JSON 数据。