将源代码解析为可查询的函数、类、调用和语义注解图,用于安全分析。
Trailmark 使用 tree-sitter 进行语言无关的 AST 解析,并使用 rustworkx 进行高性能图遍历。其长远愿景是将此图与变异测试和覆盖率引导的模糊测试相结合,以识别从用户输入可达的假设与测试覆盖率之间的差距。
Trailmark 分三个阶段运行:解析、索引和查询。```mermaid flowchart TD A["Source Files"] --> B["tree-sitter Parser"] B --> C["CodeGraph (nodes + edges)"] C --> D["rustworkx GraphStore"] D --> E["QueryEngine"] E --> F["JSON / Summary / Hotspots"]
classDef src fill:#007bff26,stroke:#007bff,color:#007bff
classDef parse fill:#28a74526,stroke:#28a745,color:#28a745
classDef data fill:#6f42c126,stroke:#6f42c1,color:#6f42c1
classDef query fill:#ffc10726,stroke:#e6a817,color:#e6a817
class A src
class B parse
class C,D data
class E,F query
### 1. 解析
一种特定语言的解析器遍历目录,将每个文件解析为 tree-sitter AST,并提取:
- **节点** — 函数、方法、类、结构体、接口、特性、枚举、模块、命名空间
- **边** — 调用、继承、实现、包含、导入
- **元数据** — 类型注解、圈复杂度、分支、文档字符串、异常类型
### 支持的语言
| 语言 | 扩展名 | 关键构造 |
| --- | --- | --- |
| Python | `.py` | 函数、类、方法 |
| JavaScript | `.js`, `.jsx`, `.mjs`, `.cjs` | 函数、类、箭头函数 |
| TypeScript | `.ts`, `.tsx` | 函数、类、接口、枚举 |
| PHP | `.php` | 函数、类、接口、特性(traits) |
| Ruby | `.rb` | 方法、类、模块 |
| C | `.c`, `.h` | 函数、结构体、枚举 |
| C++ | `.cpp`, `.hpp`, `.cc`, `.hh`, `.cxx`, `.hxx` | 函数、类、结构体、命名空间 |
| C# | `.cs` | 方法、类、接口、结构体、枚举、命名空间 |
| Java | `.java` | 方法、类、接口、枚举 |
| Go | `.go` | 函数、方法、结构体、接口 |
| Rust | `.rs` | 函数、结构体、特性(traits)、枚举、impl 块 |
| Solidity | `.sol` | 合约、接口、库、函数、修饰符、结构体、枚举 |
| Cairo | `.cairo` | 函数、特性、结构体、枚举、impl 块、StarkNet 合约 |
| Circom | `.circom` | 模板、函数、信号、组件 |
| Haskell | `.hs` | 函数、数据类型、类型类、实例 |
| Erlang | `.erl` | 函数、记录、行为、模块 |
| Miden Assembly | `.masm` | 过程、入口点、常量、调用 |
| Swift | `.swift` | 函数、类、结构体、枚举、协议、扩展 |
| Objective-C | `.m`, `.mm`, `.h` | C 函数、类、方法(基于选择器的命名) |
| Kotlin | `.kt`, `.kts` | 函数、类、接口、数据类、对象、方法 |
| Dart | `.dart` | 函数、类、抽象类、方法、构造函数 |
| Move | `.move` | 模块、函数、导入、直接调用 |
| Tact | `.tact` | 合约、结构体、接收器、函数 |
| Func | `.fc`, `.func` | 函数、包含、直接调用 |
| Sway | `.sw` | ABI 接口、结构体、impl 方法、函数 |
| Rego | `.rego` | 包、导入、策略规则、规则调用 |
| Proto | `.proto` | 服务、RPC、消息、字段、枚举 |
| Thrift | `.thrift` | 服务、函数、结构体、字段、枚举 |
| GraphQL | `.graphql`, `.gql` | 对象类型、根操作、字段、枚举 |
| SQL | `.sql` | 模式、表、视图、函数、过程 |```mermaid
flowchart TD
subgraph "Per-File Parsing"
F["Source file"] --> TS["tree-sitter AST"]
TS --> EX["Extract nodes"]
TS --> EC["Extract call edges"]
TS --> EB["Count branches"]
TS --> ET["Resolve types"]
end
EX --> CG["CodeGraph"]
EC --> CG
EB --> CG
ET --> CG
classDef src fill:#007bff26,stroke:#007bff,color:#007bff
classDef parse fill:#28a74526,stroke:#28a745,color:#28a745
classDef extract fill:#ffc10726,stroke:#e6a817,color:#e6a817
classDef data fill:#6f42c126,stroke:#6f42c1,color:#6f42c1
class F src
class TS parse
class EX,EC,EB,ET extract
class CG data
节点ID遵循module:function、module:Class或module:Class.method的格式,实现无歧义查找。目录解析能够在唯一定义存在时解析裸跨文件调用;存在歧义的跨文件调用则保留其原始最佳猜测目标,并标记为uncertain。边置信度标记为certain(直接调用、self.method())、inferred(对非self对象的属性访问)或uncertain(动态调度或歧义解析)。
GraphStore将CodeGraph加载到一个rustworkx PyDiGraph中,并构建双向ID/索引映射以实现快速遍历。
QueryEngine为上层的索引图提供了一个高级API:
| 方法 | 描述 |
|---|---|
callers_of(name) | 指定目标的所有直接调用方 |
callees_of(name) | 指定源的所有直接被调用方 |
ancestors_of(name) | 所有可传递到达目标(向上切片)的函数 |
reachable_from(name) | 所有从源可传递到达的函数 |
paths_between(src, dst) | 两个节点间的所有简单调用路径 |
connect_subgraphs(source, target) | 连接两个命名子图的路径 |
entrypoint_paths_to(name) | 从任何检测到的入口点到目标的路径 |
attack_surface() | 标记了信任级别、资产价值以及解析器属性(如果存在)的入口点 |
complexity_hotspots(n) | 圈复杂度 ≥ n 的函数 |
functions_that_raise(exc) | 解析器检测到的异常列表中包含exc的函数 |
generic_parameters(name) | 节点声明的泛型类型参数 |
type_references(name) | 参数、返回值、异常以及泛型边界的类型引用 |
annotate(name, kind, description, source) | 向节点添加语义注解 |
annotations_of(name, kind=None) | 获取一个节点的注解,可选的通过kind过滤 |
nodes_with_annotation(kind) | 所有标记了给定注解类型的节点 |
clear_annotations(name, kind=None) | 移除一个节点的注解 |
diff_against(other) | 当前引擎的图与另一个图的结构化差异 |
preanalysis() | 运行内置预处理分析并向图形添加注解/子图 |
augment_sarif(path) | 将SARIF发现合并到图中 |
augment_weaudit(path) | 将weAudit发现合并到图中 |
augment_binary(path) | 合并外部二进制分析图JSON文件 |
findings(kind=None) | 返回带有查找类型注解的节点 |
subgraph(name) | 返回指定命名子图中的节点 |
subgraph_edges(name) | 返回指定命名子图内部的导出边 |
subgraph_names() | 列出图中当前所有命名的子图 |
summary() | 节点计数、边计数、依赖关系 |
to_json() | 导出完整图形 |
classDiagram class CodeGraph { language: str root_path: str nodes: dict[str, CodeUnit] edges: list[CodeEdge] annotations: dict[str, list[Annotation]] entrypoints: dict[str, EntrypointTag] dependencies: list[str] add_annotation(node_id, annotation) clear_annotations(node_id, kind=None) merge(other) }
class CodeUnit {
id: str
name: str
kind: NodeKind
location: SourceLocation
parameters: tuple[Parameter]
return_type: TypeRef
exception_types: tuple[TypeRef]
cyclomatic_complexity: int
branches: tuple[BranchInfo]
docstring: str
}
class CodeEdge {
source_id: str
target_id: str
kind: EdgeKind
confidence: EdgeConfidence
}
class Annotation {
kind: AnnotationKind
description: str
source: str
}
class EntrypointTag {
kind: EntrypointKind
trust_level: TrustLevel
description: str
asset_value: AssetValue
}
CodeGraph "1" *-- "*" CodeUnit
CodeGraph "1" *-- "*" CodeEdge
CodeGraph "1" *-- "*" Annotation
CodeGraph "1" *-- "*" EntrypointTag
**节点种类:** `function`, `method`, `class`, `module`, `struct`, `interface`, `trait`, `enum`, `namespace`, `contract`, `library`, `template`, `proxy`
**节点起源:** `source`, `proxy`, `binary`, `synthetic`
**边种类:** `calls`, `inherits`, `implements`, `contains`, `imports`, `resolves_to`, `type_uses`, `specializes`, `corresponds_to`
**边置信度:** `certain`, `inferred`, `uncertain`
未解决的调用会被具体化为代理节点,例如 `proxy.unresolved:<raw-symbol>`,这样遍历结果可以显示源分析在何处丢失了解析,而不是默默地丢弃该边。二进制分析支持导入外部 JSON 调用图;Trailmark 本身不会反汇编可执行文件。
### 示例图
给定以下 Python 代码:
```python```python
class Auth:
def verify(self, token: str) -> bool:
return self._check_sig(token)
def _check_sig(self, token: str) -> bool:
...
def handle_request(req: Request) -> Response:
auth = Auth()
if auth.verify(req.token):
return process(req)
return deny()
Trailmark 生成一个类似如下的图形:```mermaid graph TD HR["handle_request"] -->|calls| AV["Auth.verify"] HR -->|calls| P["process"] HR -->|calls| D["deny"] AV -->|calls| CS["Auth._check_sig"] A["Auth"] -->|contains| AV A -->|contains| CS
classDef fn fill:#007bff26,stroke:#007bff,color:#007bff
classDef cls fill:#6f42c126,stroke:#6f42c1,color:#6f42c1
class HR,P,D fn
class A,AV,CS cls
## 安装
下面的示例追踪当前开发分支。对于最新发布的包,请从PyPI安装。要获得此处描述的精确功能集,请从源代码检出安装,并通过 `uv run` 运行命令。```bash
# Latest published release
uv pip install trailmark
# Current checkout / development branch
uv sync --all-groups
需要 Python ≥ 3.12。
Trailmark 使用 tree-sitter-language-pack 来管理大多数语法解析器。当前版本使用平台证书存储来下载语法文件。在 TLS 检测或离线环境中,请先在匹配的平台上通过 python -c "import tree_sitter_language_pack as p; p.download_all()" 预填充包缓存,然后将生成的 tree-sitter-language-pack 缓存目录复制到目标机器。HTTPS_PROXY 环境变量同样被支持。SQL 语法以 tree-sitter-sql wheel 依赖形式提供,不使用该缓存。