关于 trailmark
Trailmark 是静态代码分析技能和命令行工具,可将代码解析为包含函数、类型、调用关系和元数据的图。它面向多语言项目的安全审查,提供图查询、预分析以及连接代码组件的可选配置。
主要功能
- 01构建代码结构与调用关系图
- 02查询路径、切片和入口点
- 03执行污点与权限边界预分析
- 04支持多种语言解析器
- 05通过配置连接仓库边界
- 06为图添加审计注释
适用场景
- 01追踪用户输入到敏感函数
- 02按复杂度确定审计优先级
- 03梳理攻击面和入口点
- 04连接 FFI 或 RPC 边界两侧的代码
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ trailofbits/ skills --skill trailmark安装后在 Claude Code 中输入/trailmark确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
agents/openai.yamlassets/trail-of-bits-mark.svgreferences/preanalysis-passes.mdquery-patterns.md详细介绍
Trailmark
将源代码解析为由函数、类、调用和语义元数据组成的有向图,用于安全分析。
适用场景
- 映射从用户输入到敏感函数的调用路径
- 查找复杂度热点,以确定审计优先级
- 识别攻击面和入口点
- 理解陌生代码库中的调用关系
- 为多语言项目准备安全审查或审计
- 为代码单元添加由 LLM 推断的注释(假设、前置条件)
- 导入外部二进制分析图,以关联源代码和二进制视图
- 查询传递切片、入口点路径、子图边或类型引用
- 为某个可疑函数或候选发现生成图证据
- 在变异测试(genotoxic skill)或绘图前进行预分析
不适用场景
- 调用图没有价值的单文件脚本(直接阅读文件)
- 非基于代码生成的架构图(使用
diagramming-codeskill 或手动绘制) - 变异测试分诊(使用内部调用 trailmark 的 genotoxic skill)
- 运行时行为分析(trailmark 是静态分析工具,不是动态分析工具)
应当避免的借口
| 借口 | 错误原因 | 必须采取的行动 |
|---|---|---|
| “我直接手动阅读源文件就行” | 手动阅读会遗漏调用路径、影响范围和污点数据 | 安装 trailmark 并使用其 API |
| “快速查询不需要预分析” | 影响范围、污点和权限数据只有在 preanalysis() 之后才可用 | 始终先运行 engine.preanalysis(),再交由其他 skill 处理 |
| “图太大了,我抽样查看” | 抽样会遗漏跨模块攻击路径 | 构建完整图;使用子图查询聚焦分析 |
| “不确定的边无关紧要” | 类型混淆漏洞往往隐藏在动态分派中 | 在安全结论中考虑 uncertain 边 |
| “只分析一种语言就够了” | 多语言代码库存在容易聚集漏洞的 FFI 边界 | 对每个组件使用正确的 --language 标志 |
| “只有复杂度热点值得检查” | 污点路径上低复杂度的函数也是高价值目标 | 结合复杂度、污点和影响范围数据进行分析 |
| “文档提到了受版本限制的方法,所以我可以随处调用” | 许多环境仍安装着 Trailmark 0.2.x | 使用 v0.4+/v0.5+ 功能前,先检查已安装版本或探测功能是否可用 |
安装
必需: 如果找不到 trailmark,请先安装 CLI,再进行其他操作:
uv tool install trailmark
安装工具只会提供 CLI,不会让 import trailmark 变得可解析。
请使用 uv run --with trailmark python - 运行此 skill 中的 Python 代码片段;这是解决代码片段导入错误或 ModuleNotFoundError 的方法,而不是安装操作本身。
切勿用“手动验证”、“手动分析”或手动阅读源文件来代替运行 trailmark。必须安装该工具并以编程方式使用。如果安装失败,请向用户报告错误,不要悄悄改为手动阅读代码。
版本门控
Trailmark 0.4.0 扩展了图模型和查询接口,0.5.0 则新增了 SQL 解析器、仓库链接配置和更丰富的入口点元数据。 使用标记为 v0.4+ 或 v0.5+ 的功能前,请检查已安装的 版本:
trailmark --version 2>/dev/null || uv run trailmark --version 2>/dev/null
请按数值而非字典序比较报告的版本。0.4.0 或更高版本
表示完整的 v0.4 功能集可用。版本命令本身是在 0.2.2 中新增的,
因此如果命令失败,说明安装版本早于 0.2.2,或完全未安装
trailmark——请使用 trailmark analyze --help 加以区分。以编程方式
使用时,请通过 hasattr() 探测并在不支持时回退,而不要假定仅 v0.4
版本才有的方法一定存在:
if hasattr(engine, "subgraph_edges"):
edges = engine.subgraph_edges("tainted")
else:
# v0.2 fallback: filter engine.to_json() edges whose endpoints
# are both in engine.subgraph("tainted")
edges = []
v0.2 安全基线: CLI analyze、diff、entrypoints、augment 和
--language auto;QueryEngine.from_directory()、callers_of()、
callees_of()、paths_between()、ancestors_of()、reachable_from()、
entrypoint_paths_to()、complexity_hotspots()、attack_surface()、
summary()、to_json()、preanalysis()、annotate()、annotations_of()、
nodes_with_annotation()、clear_annotations()、findings()、subgraph()、
subgraph_names()、diff_against()、augment_sarif() 和
augment_weaudit()。
在 0.2.2 中新增: CLI --version 标志和 version 子命令。
在 0.3.x 中新增: trailmark.parse 模块,包含模块级的
detect_languages() 和 supported_languages()。通过
from trailmark.query.api import detect_languages 导入的 detect_languages() 本身
兼容 v0.2(在 0.3+ 中作为已弃用别名保留);supported_languages() 没有
对应的 0.2.x 版本。
v0.4+ 功能: 原生 diagram 子命令;扩展的解析器覆盖范围;
用于未解析调用的代理节点;节点来源;通过 augment_binary() 进行二进制图增强;
connect_subgraphs();subgraph_edges();generic_parameters();以及
type_references()。
v0.5+ 功能: sql 解析器(面向 PostgreSQL 的架构、表、视图、
函数、存储过程和依赖关系);节点类型 schema、table、view、
procedure;.trailmark/links.toml 仓库链接配置(见下文的仓库链接),
包括用于已声明外部端点的 proxy.external:<symbol> 节点;仓库链接、未解析调用代理和
type_uses 边现在也会在单语言目录解析时生成(0.4
仅在多语言解析时生成);根据解析器元数据检测 Solidity 入口点(不包括接口;
节点属性包括 solidity_visibility、
solidity_mutability、solidity_override、solidity_container_kind 和
solidity_overridden_by);当节点具有属性时,attack_surface() 条目会包含
attributes 键;TypeScript 可解析通过 new ConcreteClass() 赋值的接收者;C# 文件级命名空间。
0.5.0 没有新增 QueryEngine 方法,因此无法通过
hasattr(engine, ...) 检测。请根据报告的版本门控 v0.5 功能,或进行结构探测:
from trailmark.models.nodes import NodeKind
has_v05 = "SCHEMA" in NodeKind.__members__ # sql kinds are 0.5+
快速开始
# Auto-detect and merge every supported language under the tree
uv run trailmark analyze --language auto --summary {targetDir}
# Explicit languages (single language or comma-separated list)
uv run trailmark analyze --language rust {targetDir}
uv run trailmark analyze --language python,rust {targetDir}
# Complexity hotspots
uv run trailmark analyze --language auto --complexity 10 {targetDir}
# Entrypoint inventory and structural diff (v0.2-safe)
uv run trailmark entrypoints --language auto {targetDir}
uv run trailmark diff --language auto --repo {repoDir} main HEAD --json
# Version report (0.2.2+)
uv run trailmark --version
# v0.4+: native diagram command
uv run trailmark diagram -t {targetDir} -T call-graph -f main --depth 2
编程 API
# trailmark.parse is a 0.3+ module; on 0.2.x import detect_languages from
# trailmark.query.api instead (supported_languages has no 0.2.x equivalent)
from trailmark.parse import detect_languages, supported_languages
from trailmark.query.api import QueryEngine
# Ask the installed Trailmark build what it supports
supported_languages()
detect_languages("{targetDir}")
# Prefer auto for unknown or polyglot trees; use explicit lists when needed
engine = QueryEngine.from_directory("{targetDir}", language="auto")
engine = QueryEngine.from_directory("{targetDir}", language="python,rust")
engine.callers_of("function_name")
engine.callees_of("function_name")
engine.paths_between("entry_func", "db_query")
engine.complexity_hotspots(threshold=10)
engine.attack_surface()
engine.summary()
engine.to_json()
# Transitive slices and entrypoint path queries (v0.2-safe)
engine.ancestors_of("sensitive_sink")
engine.reachable_from("entry_func")
engine.entrypoint_paths_to("sensitive_sink")
# v0.4+: connect named subgraphs
if hasattr(engine, "connect_subgraphs"):
engine.connect_subgraphs("tainted", "privilege_boundary")
# Run pre-analysis (blast radius, entrypoints, privilege
# boundaries, taint propagation)
result = engine.preanalysis()
# Query subgraphs created by pre-analysis
engine.subgraph_names()
engine.subgraph("tainted")
engine.subgraph("high_blast_radius")
engine.subgraph("privilege_boundary")
engine.subgraph("entrypoint_reachable")
if hasattr(engine, "subgraph_edges"):
engine.subgraph_edges("tainted")
# Add LLM-inferred annotations
from trailmark.models import AnnotationKind
engine.annotate("function_name", AnnotationKind.ASSUMPTION,
"input is URL-encoded", source="llm")
# Query annotations (including pre-analysis results)
engine.annotations_of("function_name")
engine.annotations_of("function_name",
kind=AnnotationKind.BLAST_RADIUS)
engine.annotations_of("function_name",
kind=AnnotationKind.TAINT_PROPAGATION)
engine.nodes_with_annotation(AnnotationKind.FINDING)
engine.clear_annotations("function_name", kind=AnnotationKind.ASSUMPTION)
# v0.4+: generic/type-reference and binary augmentation APIs
if hasattr(engine, "generic_parameters"):
engine.generic_parameters("GenericTypeOrFunction")
if hasattr(engine, "type_references"):
engine.type_references("function_name")
if hasattr(engine, "augment_binary"):
engine.augment_binary("binary_graph.json")
预分析阶段
在将 engine.preanalysis() 的结果交给 genotoxic
skill 之前,务必运行该方法。(diagramming-code 会构建自己的图,不使用此结果。)
预分析会通过四个阶段丰富图:
- 影响范围估算——统计每个函数的下游和上游节点,识别复杂度高的关键后代节点
- 入口点枚举——按信任级别映射入口点,并计算可达节点集
- 权限边界检测——查找信任级别发生变化的调用边(不可信 -> 可信)
- 污点传播——标记所有可从不可信入口点到达的节点
结果以注释和命名子图的形式存储在图中。
详细文档请参阅 references/preanalysis-passes.md。
语言选择
不要在下游工作流中硬编码过时的语言表。请查询已安装的 Trailmark 构建版本,了解其支持的语言:
from trailmark.parse import detect_languages, supported_languages
supported_languages()
detect_languages("{targetDir}")
CLI 用法:
# Auto-detect and merge
uv run trailmark analyze --language auto {targetDir}
# Explicit list for a known polyglot target
uv run trailmark analyze --language python,rust {targetDir}
截至 Trailmark 0.5.0,解析器名称包括:python、javascript、
typescript、php、ruby、c、cpp、c_sharp、java、go、rust、
solidity、cairo、circom、haskell、erlang、masm、swift、objc、
kotlin、dart、move、tact、func、sway、rego、proto、thrift、
graphql 和 sql(在 0.5.0 中新增;面向 PostgreSQL,适用于 .sql 文件)。
请将此列表视作文档,而非权威信息源;依赖某个解析器之前,请调用已安装构建版本中的
supported_languages()。
仓库链接(v0.5+)
解析器无法识别跨语言调用(FFI、RPC、IPC、合约调用),也无法识别指向外部系统的边。请在分析根目录下的 .trailmark/links.toml 中声明这些边,Trailmark 会在每次解析时生成相应的边——这是稳定的公共配置接口:
[[link]]
source = "backend:submit"
target = "contract:Verifier.verify"
kind = "calls" # any EdgeKind; defaults to calls
confidence = "certain" # certain | inferred | uncertain; defaults to inferred
description = "JSON-RPC eth_call"
[[link]]
source = "backend:notify"
target = "payments-webhook"
target_external = true # required because target is unresolved
端点引用可以是精确节点 ID,也可以是唯一名称或后缀。验证采用严格失败策略:引用存在歧义、内部端点未知、枚举值无效或 TOML 格式错误时,会引发 ValueError,而不会悄悄削弱图的完整性。source_external = true / target_external = true 允许端点无法解析,并通过创建 proxy.external:<symbol> 节点来处理。已配置的边带有 configured_by = .trailmark/links.toml 属性,因此可与解析器生成的边区分。
当审计跨越合理性分析表所警告的 FFI/RPC 边界时,请使用此功能:先声明边界边,然后路径查询和污点查询便可像跨越其他调用边一样跨越这些边。
图模型
节点类型: function、method、class、module、struct、
interface、trait、enum、namespace、contract、library、
template;v0.4+ 还会将无法解析的引用实例化为 proxy 节点;v0.5+ 为 SQL
图新增 schema、table、view 和 procedure。
节点来源: v0.4+ 节点可带有 source、proxy、
binary 或 synthetic 来源。v0.2 导出可能不包含来源信息。
边类型: calls、inherits、implements、contains、imports;
v0.4+ 新增 resolves_to、type_uses、specializes 和
corresponds_to。
边置信度: certain(直接调用,如 self.method())、inferred
(访问非 self 对象的属性)、uncertain(动态分派)
每个代码单元
- 带类型的参数、返回类型、异常类型
- 圈复杂度和分支元数据
- 文档字符串
- 注解:
assumption、precondition、postcondition、invariant、blast_radius、privilege_boundary、taint_propagation、finding、audit_note(最后两项由augment_sarif/augment_weaudit设置)
每条边
- 源节点/目标节点 ID、边类型、置信度
项目级
- 依赖项(导入的包)
- 带有信任级别和资产价值的入口点
- 命名子图(由预分析填充)
核心概念
声明契约与有效输入域: Trailmark 将函数声明接受的内容与实际可通过调用路径到达函数的内容区分开来。两者不匹配之处正是漏洞藏身之处:
- 范围扩大: 未受约束的数据到达一个假定已完成验证的函数
- 恰好安全: 没有验证,但目前只有安全的调用方
边置信度: 动态分派会产生 uncertain 边。提出安全结论时,请考虑置信度。
代理节点(v0.4+): 未解析的调用会作为 proxy.unresolved:<symbol> 等节点保留下来。不要将它们视为源代码函数;可用它们识别解析缺口、动态分派、外部 API 或二进制链接候选项。v0.5+ 还会为 .trailmark/links.toml 中声明为外部的端点生成 proxy.external:<symbol> 节点。
可达性不等于污点: entrypoint_paths_to() 和污点子图回答的是不同问题。路径查询报告调用图中的可达性;预分析污点会将从不可信入口点可达的节点标记为粗略信号。Trailmark 不执行跨过程污点分析——不要将其中任何一项作为攻击者可控数据到达汇点的证据。
二进制增强(v0.4+): engine.augment_binary() 会导入外部二进制分析图 JSON 文件。Trailmark 会尽可能将其连接到源代码节点;它本身不会反汇编二进制文件。
子图: 由预分析生成的命名节点 ID 集合。使用 engine.subgraph("name") 查询。调用 engine.preanalysis() 后即可使用。
查询模式
常见安全分析模式请参阅 references/query-patterns.md。
预分析阶段文档请参阅 references/preanalysis-passes.md。
当用户有一个具体的候选发现、SARIF 结果、weAudit 注释、可疑函数或报告摘录,并需要一份可直接交接的可达性与影响范围证据包时,请使用 trailmark-finding-triage。
当已知一个种子问题,且用户需要基于图推导的变体候选项,以用于 variant-analysis、Semgrep、CodeQL 或人工审查时,请使用 trailmark-variant-neighborhood。