trailmark

trailmark

热门

构建并查询多语言源代码与二进制代码图,用于安全分析。包含预分析阶段,用于爆炸半径、污点传播、权限边界、入口点枚举、代理/未解析调用追踪、类型/引用查询、结构遍历、图差异、审计增强、通过 `.trailmark/links.toml` 声明的跨语言/FFI/外部链接,以及 SQL 模式图。适用于分析调用路径、映射攻击面、发现复杂度热点、枚举入口点、追踪污点传播、衡量爆炸半径、导入 SARIF/weAudit/二进制发现、跨语言或 RPC 边界链接源代码图,或构建用于审计优先级的代码图。在使用特定版本的 Trailmark API 前,请先进行特性门控;当目标语言未知或为多语言时,优先使用 `trailmark.parse.detect_languages()` 或 `--language auto`。

6336Star
545Fork
更新于 2026/7/30
SKILL.md
readonly只读
name
trailmark
description

构建并查询多语言源代码与二进制代码图,用于安全分析。包含预分析阶段,用于爆炸半径、污点传播、权限边界、入口点枚举、代理/未解析调用追踪、类型/引用查询、结构遍历、图差异、审计增强、通过 `.trailmark/links.toml` 声明的跨语言/FFI/外部链接,以及 SQL 模式图。适用于分析调用路径、映射攻击面、发现复杂度热点、枚举入口点、追踪污点传播、衡量爆炸半径、导入 SARIF/weAudit/二进制发现、跨语言或 RPC 边界链接源代码图,或构建用于审计优先级的代码图。在使用特定版本的 Trailmark API 前,请先进行特性门控;当目标语言未知或为多语言时,优先使用 `trailmark.parse.detect_languages()` 或 `--language auto`。

Trailmark

将源代码解析为函数、类、调用和语义元数据的有向图,用于安全分析。

使用场景

  • 映射从用户输入到敏感函数的调用路径
  • 发现复杂度热点以确定审计优先级
  • 识别攻击面和入口点
  • 理解不熟悉代码库中的调用关系
  • 跨多语言项目的安全审查或审计准备
  • 为代码单元添加 LLM 推断的注释(假设、前置条件)
  • 导入外部二进制分析图,连接源代码和二进制视图
  • 查询传递切片、入口点路径、子图边或类型引用
  • 为某个可疑函数或候选发现生成图证据
  • 在变异测试(genotoxic 技能)或绘图前进行预分析

不适用场景

  • 单文件脚本,调用图无额外价值(直接阅读文件)
  • 非代码派生的架构图(使用 diagramming-code 技能或手绘)
  • 变异测试分类(使用 genotoxic 技能,其内部调用 trailmark)
  • 运行时行为分析(trailmark 是静态分析,非动态)

需拒绝的合理化理由

合理化理由 错误原因 正确操作
"我手动阅读源文件就行了" 手动阅读会遗漏调用路径、爆炸半径和污点数据 安装 trailmark 并使用 API
"快速查询不需要预分析" 爆炸半径、污点和权限数据仅在 preanalysis() 后可用 在交给其他技能前始终运行 engine.preanalysis()
"图太大,我抽样分析" 抽样会遗漏跨模块攻击路径 构建完整图;使用子图查询聚焦
"不确定的边不重要" 动态分发正是类型混淆漏洞隐藏之处 在安全声明中考虑 uncertain
"单语言分析就够了" 多语言仓库存在 FFI 边界,漏洞常聚集于此 为每个组件使用正确的 --language 标志
"复杂度热点是唯一值得检查的" 低复杂度函数在受污染路径上是高价值目标 结合复杂度、污点和爆炸半径数据
"文档提到一个版本门控方法,所以我可以随处调用" 许多环境仍安装 Trailmark 0.2.x 在使用 v0.4+/v0.5+ 特性前检查安装版本或探测特性可用性

安装

必须: 如果 uv run trailmark 失败(命令未找到、导入错误、ModuleNotFoundError),请先安装 trailmark:

uv pip install trailmark

不要退而求其次使用“手动验证”、“手动分析”或手动阅读源文件来替代运行 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 回退:过滤 engine.to_json() 中两端点均在 engine.subgraph("tainted") 中的边
    edges = []

v0.2 安全基线: CLI analyzediffentrypointsaugment--language autoQueryEngine.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()detect_languages() 本身通过 from trailmark.query.api import 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 的模式、表、视图、函数、过程、依赖关系);节点类型 schematableviewprocedure.trailmark/links.toml 仓库链接配置(见下文仓库链接),包括用于声明外部端点的 proxy.external:<symbol> 节点;仓库链接、未解析调用代理和 type_uses 边现在在单语言目录解析中实现(0.4 仅在多语言解析中发出);从解析器元数据检测到的 Solidity 入口点(排除接口;solidity_visibilitysolidity_mutabilitysolidity_overridesolidity_container_kindsolidity_overridden_by 节点属性);attack_surface() 条目在节点有属性时携带 attributes 键;TypeScript 解析使用 new ConcreteClass() 赋值的接收者;C# 文件作用域命名空间。

v0.5.0 没有新增 QueryEngine 方法,因此 hasattr(engine, ...) 无法检测它。通过报告版本或结构探测来门控 v0.5 特性:

from trailmark.models.nodes import NodeKind

has_v05 = "SCHEMA" in NodeKind.__members__  # sql 类型是 0.5+

快速开始

# 自动检测并合并树下的所有支持语言
uv run trailmark analyze --language auto --summary {targetDir}

# 显式指定语言(单语言或逗号分隔列表)
uv run trailmark analyze --language rust {targetDir}
uv run trailmark analyze --language python,rust {targetDir}

# 复杂度热点
uv run trailmark analyze --language auto --complexity 10 {targetDir}

# 入口点清单和结构差异(v0.2 安全)
uv run trailmark entrypoints --language auto {targetDir}
uv run trailmark diff --repo {repoDir} main HEAD --json

# 版本报告(0.2.2+)
uv run trailmark --version

# v0.4+:原生 diagram 命令
uv run trailmark diagram -t {targetDir} -T call-graph -f main --depth 2

编程 API

# trailmark.parse 是 0.3+ 模块;在 0.2.x 上改为从 trailmark.query.api 导入 detect_languages
# (supported_languages 在 0.2.x 中没有等价物)
from trailmark.parse import detect_languages, supported_languages
from trailmark.query.api import QueryEngine

# 询问已安装的 Trailmark 构建支持哪些语言
supported_languages()
detect_languages("{targetDir}")

# 对于未知或多语言树,优先使用 auto;必要时使用显式列表
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()

# 传递切片和入口点路径查询(v0.2 安全)
engine.ancestors_of("sensitive_sink")
engine.reachable_from("entry_func")
engine.entrypoint_paths_to("sensitive_sink")

# v0.4+:连接命名子图
if hasattr(engine, "connect_subgraphs"):
    engine.connect_subgraphs("tainted", "privilege_boundary")

# 运行预分析(爆炸半径、入口点、权限边界、污点传播)
result = engine.preanalysis()

# 查询预分析创建的子图
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")

# 添加 LLM 推断的注释
from trailmark.models import AnnotationKind

engine.annotate("function_name", AnnotationKind.ASSUMPTION,
                "input is URL-encoded", source="llm")

# 查询注释(包括预分析结果)
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+:泛型/类型引用和二进制增强 API
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")

预分析阶段

在将结果交给 genotoxic 或 diagramming-code 技能之前,始终运行 engine.preanalysis() 预分析通过四个阶段丰富图:

  1. 爆炸半径估计 — 计算每个函数的下游和上游节点数,识别关键的高复杂度后代
  2. 入口点枚举 — 按信任级别映射入口点,计算可达节点集
  3. 权限边界检测 — 查找信任级别变化的调用边(不可信 -> 可信)
  4. 污点传播 — 标记所有从不可信入口点可达的节点

结果作为注释和命名子图存储在图上。

详细文档请参见 references/preanalysis-passes.md

语言选择

不要在下游工作流中硬编码过时的语言表。询问已安装的 Trailmark 构建支持哪些语言:

from trailmark.parse import detect_languages, supported_languages

supported_languages()
detect_languages("{targetDir}")

CLI 模式:

# 自动检测并合并
uv run trailmark analyze --language auto {targetDir}

# 已知多语言目标的显式列表
uv run trailmark analyze --language python,rust {targetDir}

截至 Trailmark 0.5.0,解析器名称包括:pythonjavascripttypescriptphprubyccppc_sharpjavagorustsoliditycairocircomhaskellerlangmasmswiftobjckotlindartmovetactfuncswayregoprotothriftgraphqlsql(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"                 # 任何 EdgeKind;默认为 calls
confidence = "certain"         # certain | inferred | uncertain;默认为 inferred
description = "JSON-RPC eth_call"

[[link]]
source = "backend:notify"
target = "payments-webhook"
target_external = true         # 因为 target 未解析,所以必须设置

端点引用可以是精确的节点 ID 或唯一的名称/后缀。验证失败时关闭:模糊引用、未知内部端点、无效枚举值和格式错误的 TOML 会引发 ValueError,而不是静默削弱图。source_external = true / target_external = true 通过创建 proxy.external:<symbol> 节点来允许未解析的端点。配置的边带有 configured_by = .trailmark/links.toml 属性,以便与解析器派生的边区分。

当审计跨越合理化表警告的 FFI/RPC 边界时使用此功能:先声明边界边,然后路径和污点查询像其他调用边一样跨越它们。

图模型

节点类型: functionmethodclassmodulestructinterfacetraitenumnamespacecontractlibrarytemplatev0.4+ 还将未解析的引用实现为 proxy 节点;v0.5+ 为 SQL 图添加了 schematableviewprocedure

节点来源: v0.4+ 节点可能带有来源 sourceproxybinarysynthetic。v0.2 导出可能省略来源。

边类型: callsinheritsimplementscontainsimportsv0.4+ 添加了 resolves_totype_usesspecializescorresponds_to

边置信度: certain(直接调用,self.method())、inferred(对非 self 对象的属性访问)、uncertain(动态分发)

每个代码单元

  • 带类型的参数、返回类型、异常类型
  • 圈复杂度和分支元数据
  • 文档字符串
  • 注释:assumptionpreconditionpostconditioninvariantblast_radiusprivilege_boundarytaint_propagationfindingaudit_note(后两个由 augment_sarif / augment_weaudit 设置)

每条边

  • 源/目标节点 ID、边类型、置信度级别

项目级别

  • 依赖项(导入的包)
  • 带有信任级别和资产价值的入口点
  • 命名子图(由预分析填充)

关键概念

声明的契约 vs. 有效输入域: 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