trailofbits/trailmark
GitHub: trailofbits/trailmark
将多语言源代码解析为可查询的调用图,支持攻击面分析、复杂度热点定位和版本差异对比,服务于安全审计与代码理解。
Stars: 44 | Forks: 2
# Trailmark
[](https://github.com/trailofbits/trailmark/actions/workflows/ci.yml)
[](https://github.com/trailofbits/trailmark/actions/workflows/mutation.yml)
将源代码解析为包含函数、类、调用和语义注解的可查询图,用于安全分析。
Trailmark 使用 [tree-sitter](https://tree-sitter.github.io/) 进行与语言无关的 AST 解析,并使用 [rustworkx](https://www.rustworkx.org/) 进行高性能图遍历。长期愿景是将此图与变异测试和覆盖率引导的模糊测试相结合,以识别从用户输入可达的假设与测试覆盖率之间的差距。
## 工作原理
Trailmark 分三个阶段运行:**解析**、**索引** 和 **查询**。
```
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` | functions、classes、methods |
| JavaScript | `.js`, `.jsx` | functions、classes、arrow functions |
| TypeScript | `.ts`, `.tsx` | functions、classes、interfaces、enums |
| PHP | `.php` | functions、classes、interfaces、traits |
| Ruby | `.rb` | methods、classes、modules |
| C | `.c`, `.h` | functions、structs、enums |
| C++ | `.cpp`, `.hpp`, `.cc`, `.hh`, `.cxx`, `.hxx` | functions、classes、structs、namespaces |
| C# | `.cs` | methods、classes、interfaces、structs、enums、namespaces |
| Java | `.java` | methods、classes、interfaces、enums |
| Go | `.go` | functions、methods、structs、interfaces |
| Rust | `.rs` | functions、structs、traits、enums、impl blocks |
| Solidity | `.sol` | contracts、interfaces、libraries、functions、modifiers、structs、enums |
| Cairo | `.cairo` | functions、traits、structs、enums、impl blocks、StarkNet contracts |
| Circom | `.circom` | templates、functions、signals、components |
| Haskell | `.hs` | functions、data types、type classes、instances |
| Erlang | `.erl` | functions、records、behaviours、modules |
| Miden Assembly | `.masm` | procedures、entrypoints、constants、invocations |
```
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` 方案,以便进行明确的查找。边的置信度标记为 `certain`(直接调用,`self.method()`)、`inferred`(对非 self 对象的属性访问)或 `uncertain`(动态分派)。
### 2. 索引
`GraphStore` 将 `CodeGraph` 加载到 rustworkx 的 `PyDiGraph` 中,并构建双向 ID/索引映射以实现快速遍历。
### 3. 查询
`QueryEngine` 在索引图之上提供了高级 API:
| 方法 | 描述 |
| --- | --- |
| `callers_of(name)` | 命名目标的直接调用者 |
| `callees_of(name)` | 命名源的直接被调用者 |
| `ancestors_of(name)` | 可以传递到达目标的每个函数(向上切片) |
| `reachable_from(name)` | 从源传递可达的每个函数 |
| `paths_between(src, dst)` | 两个节点之间的所有简单调用路径 |
| `entrypoint_paths_to(name)` | 从任何检测到的入口点到目标的路径 |
| `attack_surface()` | 标记有信任级别和资产价值的入口点 |
| `complexity_hotspots(n)` | 圈复杂度 ≥ n 的函数 |
| `functions_that_raise(exc)` | 解析器检测到的异常列表包含 `exc` 的函数 |
| `annotate(name, kind, desc, source)` | 为节点添加语义注解 |
| `annotations_of(name, kind=None)` | 获取节点的注解,可选按类型过滤 |
| `nodes_with_annotation(kind)` | 每个标记有给定注解类型的节点 |
| `clear_annotations(name, kind=None)` | 删除节点的注解 |
| `diff_against(other)` | 此引擎图与另一个图的结构差异 |
| `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`
**边类型:** `calls`、`inherits`、`implements`、`contains`、`imports`
**边置信度:** `certain`、`inferred`、`uncertain`
### 图示例
给定以下 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 会生成如下图表:
```
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
```
## 安装
```
uv pip install trailmark
```
需要 Python ≥ 3.12。
## 使用
```
# 完整 JSON 图 (Python, 默认)
trailmark analyze path/to/project
# 分析不同语言
trailmark analyze --language rust path/to/project
trailmark analyze --language javascript path/to/project
# Polyglot: 自动检测并合并树中发现的每种受支持语言,
# 或传入显式的逗号分隔列表。
trailmark analyze --language auto path/to/project
trailmark analyze --language python,rust,solidity path/to/project
# 摘要统计信息
trailmark analyze --summary path/to/project
# 复杂度热点 (阈值 >= 10)
trailmark analyze --complexity 10 path/to/project
# 使用外部发现扩充图 (来自静态分析器的 SARIF,
# 来自 VS Code 扩展的 weAudit 发现)。每个 --sarif / --weaudit
# 标志可重复使用。添加 --json 以打印扩充后的图。
trailmark augment --sarif results.sarif path/to/project
trailmark augment --weaudit findings.json path/to/project
trailmark augment --sarif a.sarif --sarif b.sarif --json path/to/project
# 列出检测到的入口点 (攻击面)。使用启发式检测
# (main() 函数, pyproject.toml [project.scripts]) 以及位于
# .trailmark/entrypoints.toml 的可选覆盖文件 (见下文)。
trailmark entrypoints path/to/project
trailmark entrypoints --json path/to/project
# 两个代码图之间的结构差异。接受目录路径或
# git 引用 (分支、标签、提交)。展示增加/移除的节点、
# 调用边变更,以及 — 最有用的 — 攻击面变更。
trailmark diff before/ after/
trailmark diff --repo . main HEAD # compare git refs
trailmark diff --json before/ after/ # machine-readable output
```
### 入口点检测
Trailmark 会自动填充 `graph.entrypoints`,以便 `attack_surface()`、污点传播和特权边界跨越有数据可用。检测分四层运行,每一层会覆盖上一层的定义:
1. **通用 `main` 启发式规则。** 任何语言中名为 `main` 的函数。标记为 `user_input` / `trusted_internal` / `low`。
2. **框架感知扫描。** 每种语言的装饰器、属性和可见性模式——见下表。
3. **`pyproject.toml [project.scripts]`。** 显式 CLI 目标获得升级的信任/资产分类。
4. **仓库本地覆盖文件。** `.trailmark/entrypoints.toml` 中手工管理的入口点始终具有最高优先级。
框架覆盖范围:
| 语言 | 检测的框架 |
| --- | --- |
| Python | Flask、FastAPI、aiohttp、Click、Typer、Celery |
| JavaScript / TypeScript | NestJS、Next.js (App Router + Pages API)、AWS Lambda |
| Java | Spring MVC / WebFlux、JAX-RS、Kafka listeners、servlets |
| C# | ASP.NET Core、Azure Functions |
| PHP | Symfony `#[Route]` attributes + 旧版 annotations |
| Rust | actix-web、rocket、FFI 导出 (`#[no_mangle]`、`pub extern "C"`)、async-main 属性 |
| Solidity | `external` / `public` 可见性 |
| Cairo / StarkNet | `#[external]`、`#[view]`、`#[l1_handler]`、`#[constructor]` |
| Circom | `component main` 声明 |
| Miden Assembly | `export.` 指令 |
| Haskell | 顶层 `main ::` / `main =` |
| Erlang | 列在 `-export([...])` 中的函数 |
对于启发式规则遗漏的内容,可以在项目根目录的 `.trailmark/entrypoints.toml` 中显式声明入口点:
```
[[entrypoint]]
node = "my_module:handle_request" # node id, or "module.path:function"
kind = "api" # user_input | api | database | file_system | third_party
trust = "untrusted_external" # untrusted_external | semi_trusted_external | trusted_internal
asset_value = "high" # high | medium | low
description = "HTTP POST /auth"
```
有关完整参考,请参见 [docs/entrypoint-patterns.md](docs/entrypoint-patterns.md),包括尚未实现的框架(Express / Koa / Fastify、Laravel、Rails、Cobra、axum、warp、clap 等),以及贡献者可用于添加新检测器的可直接 grep 的模式。
### 编程 API
```
from trailmark.query.api import QueryEngine
# 单一语言 (默认) 或跨所有语言自动检测 + 合并
engine = QueryEngine.from_directory("path/to/project")
engine = QueryEngine.from_directory("path/to/project", language="auto")
engine = QueryEngine.from_directory("path/to/project", language="python,rust")
# 直接邻居
engine.callers_of("handle_request")
engine.callees_of("handle_request")
# 传递切片 — 谁能到达此汇聚点,或者它能到达哪里?
engine.ancestors_of("Auth._check_sig")
engine.reachable_from("handle_request")
# 来自任何检测到的入口点的攻击面路径
engine.entrypoint_paths_to("Auth._check_sig")
# 两个节点之间的所有调用路径
engine.paths_between("handle_request", "Auth._check_sig")
# 循环复杂度 >= 10 的函数
engine.complexity_hotspots(10)
# 哪些函数可以抛出给定的异常? (使用解析器检测到的
# exception_types; 无需运行时追踪)
engine.functions_that_raise("PermissionError")
# 添加和查询语义注解
from trailmark.models.annotations import AnnotationKind
engine.annotate(
"handle_request",
AnnotationKind.ASSUMPTION,
"Caller has already authenticated the session token",
source="llm",
)
engine.annotations_of("handle_request")
engine.nodes_with_annotation(AnnotationKind.FINDING)
# 对比同一代码库的早期快照进行 Diff
before = QueryEngine.from_directory("before/")
diff = engine.diff_against(before)
# diff 包含: summary_delta, nodes {added/removed/modified},
# edges {added/removed}, entrypoints {added/removed/modified}
```
## 开发
```
# 安装包和开发依赖项
uv sync --all-groups
# Lint 和格式化
uv run ruff check --fix
uv run ruff format
# 类型检查
uv tool install ty && ty check
# 测试
uv run pytest -q
# Mutation testing (在 macOS 上,设置此环境变量以避免 rustworkx fork 段错误)
OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES uv run mutmut run
```
## 许可证
Apache-2.0
标签:AST解析, Python, Rust, rustworkx, tree-sitter, 云安全监控, 代码图谱, 代码安全, 代码安全扫描, 代码审查, 变异测试, 威胁情报, 客户端加密, 开发者工具, 数据管道, 无后门, 查询引擎, 源代码分析, 漏洞枚举, 网络安全, 网络流量审计, 覆盖率分析, 软件工程, 逆向工具, 隐私保护, 静态分析