アップデート一覧に戻る
New releaseJul 24, 2026

trailmark v0.5.0

ソースコードのグラフデータベース表現を構築し、クエリを実行する

共有

Trailmark

CI Mutation Testing

ソースコードをパースし、関数、クラス、呼び出し、およびセキュリティ分析のための意味注釈からなる、クエリ可能なグラフに変換します。

Trailmarkは、言語に依存しないASTパーシングにtree-sitterを、高性能グラフトラバーサルにrustworkxを使用しています。長期的なビジョンは、このグラフをミューテーションテストやカバレッジガイドファジングと組み合わせ、前提とユーザー入力から到達可能なテストカバレッジの間にあるギャップを特定することです。

仕組み

Trailmark は3つのフェーズで動作します: parseindexquery。```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` | 関数、クラス、インターフェース、トレイト |
| Ruby | `.rb` | メソッド、クラス、モジュール |
| C | `.c`, `.h` | 関数、構造体、列挙型 |
| C++ | `.cpp`, `.hpp`, `.cc`, `.hh`, `.cxx`, `.hxx` | 関数、クラス、構造体、名前空間 |
| C# | `.cs` | メソッド、クラス、インターフェース、構造体、列挙型、名前空間 |
| Java | `.java` | メソッド、クラス、インターフェース、列挙型 |
| Go | `.go` | 関数、メソッド、構造体、インターフェース |
| Rust | `.rs` | 関数、構造体、トレイト、列挙型、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:functionmodule:Class、または module:Class.method のスキームに従います。ディレクトリ解析では、一意の定義が存在する場合にベアのファイル間呼び出しを解決します。曖昧なファイル間呼び出しは、元のベストエフォートのターゲットのままにされ、uncertain とマークされます。エッジの信頼性は、certain(直接呼び出し、self.method())、inferred(self以外のオブジェクトに対する属性アクセス)、または uncertain(動的ディスパッチまたは曖昧な解決)としてタグ付けされます。

2. インデックス

GraphStoreCodeGraph を rustworkx の PyDiGraph に読み込み、高速なトラバーサルのための双方向の ID/インデックスマッピングを構築します。

3. クエリ

QueryEngine はインデックス化されたグラフに対する高レベルAPIを提供します:

メソッド説明
callers_of(name)指定されたターゲットの直接呼び出し元
callees_of(name)指定されたソースの直接呼び出し先
ancestors_of(name)推移的にターゲットに到達可能なすべての関数(アップワードスライス)
reachable_from(name)ソースから推移的に到達可能なすべての関数
paths_between(src, dst)2つのノード間のすべての単純な呼び出しパス
connect_subgraphs(source, target)2つの名前付きサブグラフを接続するパス
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 の findings をグラフにマージ
augment_weaudit(path)weAudit の findings をグラフにマージ
augment_binary(path)外部のバイナリ解析グラフ JSON ファイルをマージ
findings(kind=None)ファインディング形式のアノテーションを持つノードを返す
subgraph(name)名前付きサブグラフ内のノードを返す
subgraph_edges(name)名前付きサブグラフ内の誘導エッジを返す
subgraph_names()グラフ上のすべての名前付きサブグラフをリスト
summary()ノード数、エッジ数、依存関係
to_json()グラフの完全エクスポート

データモデル```mermaid

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
**Node kinds:** `function`(関数), `method`(メソッド), `class`(クラス), `module`(モジュール), `struct`(構造体), `interface`(インターフェース), `trait`(トレイト), `enum`(列挙型), `namespace`(名前空間), `contract`(コントラクト), `library`(ライブラリ), `template`(テンプレート), `proxy`(プロキシ)

**Node origins:** `source`(ソース), `proxy`(プロキシ), `binary`(バイナリ), `synthetic`(合成)

**Edge kinds:** `calls`(呼び出し), `inherits`(継承), `implements`(実装), `contains`(包含), `imports`(インポート), `resolves_to`(解決先), `type_uses`(型使用), `specializes`(特殊化), `corresponds_to`(対応)

**Edge confidence:** `certain`(確実), `inferred`(推論), `uncertain`(不確か)

未解決の呼び出しは、`proxy.unresolved:<raw-symbol>` のようなプロキシノードとして具現化され、トラバーサル結果は、ソース分析が解決を失った場所を、そのエッジを黙って落とす代わりに表示できます。バイナリ分析は外部JSONコールグラフのインポートをサポートします。Trailmarkが実行ファイルを逆アセンブルすることはありません。

### グラフ例

以下の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 依存関係として同梱されており、そのキャッシュを使用しません。

使用法```bash

Report the installed version

trailmark --version # or: trailmark -V trailmark version # subcommand form

Full JSON graph (Python, the default)

trailmark analyze path/to/project

Analyze a different language

trailmark analyze --language rust path/to/project trailmark analyze --language javascript path/to/project

Polyglot: auto-detect and merge every supported language found in the

tree, or pass an explicit comma-separated list.

trailmark analyze --language auto path/to/project trailmark analyze --language python,rust,solidity path/to/project

Summary statistics

trailmark analyze --summary path/to/project

Complexity hotspots (threshold >= 10)

trailmark analyze --complexity 10 path/to/project

Augment the graph with external findings (SARIF from static analyzers,

weAudit findings from the VS Code extension). Each --sarif / --weaudit

flag is repeatable. Add --json to print the augmented graph.

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

List detected entrypoints (attack surface). Uses heuristic detection

(main() functions, pyproject.toml [project.scripts]) plus an optional

override file at .trailmark/entrypoints.toml (see below).

trailmark entrypoints path/to/project trailmark entrypoints --json path/to/project

Structural diff between two code graphs. Accepts directory paths or

git refs (branches, tags, commits). Surfaces added/removed nodes,

call-edge changes, and — most usefully — attack-surface changes.

trailmark diff before/ after/ trailmark diff --repo . main HEAD # compare git refs trailmark diff --json before/ after/ # machine-readable output

Generate a Mermaid diagram from the code graph. --type is required; the

choices are call-graph, class-hierarchy, module-deps, containment,

complexity, and data-flow. Use --focus to scope large graphs.

trailmark diagram --target path/to/project --type call-graph trailmark diagram -t path/to/project -T call-graph -f parse_file --depth 3 trailmark diagram -t path/to/project -T complexity --threshold 5 --direction LR

### エントリポイントの検出

Trailmark は自動的に `graph.entrypoints` を設定し、`attack_surface()`、汚染伝播、特権境界の横断が動作するためのデータを提供します。検出は4つのレイヤーで実行され、後続のレイヤーが前のレイヤーを上書きします。

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 + legacy annotations |
| Rust | actix-web, rocket, FFI exports (`#[no_mangle]`, `pub extern "C"`), async-main attributes |
| Solidity | `external` / `public` visibility |
| Cairo / StarkNet | `#[external]`, `#[view]`, `#[l1_handler]`, `#[constructor]` |
| Circom | `component main` declarations |
| Miden Assembly | `export.<name>` directives |
| Haskell | top-level `main ::` / `main =` |
| Erlang | functions listed in `-export([...])` |
| Swift | `@main` app attribute |
| Objective-C | `UIApplicationDelegate` lifecycle selectors (e.g. `application:openURL:options:`) |
| Kotlin | Spring MVC / WebFlux annotations (shared with Java), Android component lifecycle methods (`onCreate`, `onReceive`, `onBind`, ...) |
| Dart | `@pragma('vm:entry-point')` native-callable markers |
| Go | `http.HandleFunc` / `http.Handle` stdlib registrations, gin/chi/echo-style `<router>.GET/POST/...` handler registrations |
| Ruby | Rails controller actions (classes inheriting `ApplicationController` / `ActionController::*`), Sidekiq worker `perform` methods |
| C / C++ | `extern "C"` linkage, `__attribute__((visibility("default")))`, `__declspec(dllexport)` |

ヒューリスティックが見逃したものについては、プロジェクトルートの `.trailmark/entrypoints.toml` にエントリポイントを明示的に宣言してください。このファイルは単一ノードとルールベースのエントリの両方をサポートしています。```toml
# Single-node entry
[[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"

# Rule: every PHP script under public_html/ is a web-exposed entrypoint.
[[entrypoint]]
file_glob = "public_html/**/*.php"
kind = "user_input"
trust = "untrusted_external"
asset_value = "high"
description = "Web-exposed PHP script"

# Rule: any function that takes a PSR-7 request object.
[[entrypoint]]
param_type = "ServerRequestInterface"
kind = "api"
trust = "untrusted_external"
asset_value = "high"
description = "PSR-7 HTTP handler"

# Rule: functions named `handle_*`.
[[entrypoint]]
name_regex = "^handle_"
kind = "api"
trust = "untrusted_external"

# Rule: conditions compose with AND — web.py files AND name starts with handle_.
[[entrypoint]]
file_glob = "public/*.py"
name_regex = "^handle_"
kind = "api"
trust = "untrusted_external"

後続のルールは、同じノードに二つのルールがタグ付けされた場合に先のルールを上書きするため、広範なルールを先に配置し、特定の修正を後に配置してください。

完全なリファレンスはdocs/entrypoint-patterns.mdを参照してください。これには、まだ実装されていないフレームワーク(Express / Koa / Fastify、Laravel、Cobra、axum、warp、clapなど)と、コントリビューターが新しいディテクターを追加するために使用できるgrep対応パターンが含まれています。

Solidityの検出は、シグネチャの正規表現ではなくパーサーメタデータを使用します。インターフェース宣言は除外され、派生したオーバーライドが一致するベース実装を抑制します。具体的なpublicおよびexternal関数はエントリーポイントのままです(viewおよびpure関数を含む)。これらのsolidity_visibilityおよびsolidity_mutability属性はattack_surface()によって返されるため、呼び出し元は読み取り専用の露出を区別できます。attack_surface()は、基になるグラフノードにパーサー固有のエントリーポイント属性が付加されている場合、それらも含みます。

クロス言語および外部リンク

ポリグロットパースは言語グラフを統合しますが、多くのRPC、FFI、サブプロセス、ホスト/コントラクト関係はソース構文では見えません。これらを.trailmark/links.tomlで決定的に宣言します:```toml [[link]] source = "backend:submit" target = "contract:Verifier.verify" kind = "calls" # defaults to calls confidence = "certain" # defaults to inferred description = "JSON-RPC eth_call"

[[link]] source = "backend:notify" target = "payments-webhook" external = true # required when either endpoint is unresolved

参照は正確なノードIDまたは一意の名前/サフィックスである場合があります。あいまいな参照、不明な内部エンドポイント、無効なenum値、および不正なTOMLは`ValueError`を発生させます。`external = true`を設定すると、未解決のエンドポイントが明示的に許可され、プロキシノードが作成されます。このファイルは安定した公開構成インターフェースです。

### 分析の制限事項

- `entrypoint_paths_to()`はコールグラフの到達可能性を報告し、攻撃者が制御するデータフローは報告しません。事前分析のtaint結果を大まかな別個のシグナルとして使用してください。Trailmarkはまだ手続き間taint分析を実行しません。
- TypeScriptは直接呼び出しと`new ConcreteClass()`で割り当てられた単純なレシーバを解決します。マニフェスト、計算プロパティ名、依存性注入コンテナ、その他の動的メカニズムを通したインターフェースディスパッチは、ベストエフォートのままです。
- SQLサポートはPostgreSQL指向で、スキーマ、テーブル、ビュー、関数、プロシージャ、およびルーチン/ビューの依存関係を抽出します。これは完全なSQL方言バリデータやクエリセマンティックアナライザではありません。

### プログラムAPI```python
from trailmark.parse import parse_directory, parse_file
from trailmark.query.api import QueryEngine

# Parse-only API: get the raw CodeGraph without building GraphStore/QueryEngine.
graph = parse_file("path/to/file.py")
graph = parse_directory("path/to/project", language="auto")

# Single-language (default) or auto-detect + merge across all languages
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")

# Direct neighbors
engine.callers_of("handle_request")
engine.callees_of("handle_request")

# Transitive slicing — who could reach this sink, or what could it reach?
engine.ancestors_of("Auth._check_sig")
engine.reachable_from("handle_request")

# Attack-surface paths from any detected entrypoint
engine.entrypoint_paths_to("Auth._check_sig")

# All call paths between two nodes
engine.paths_between("handle_request", "Auth._check_sig")

# Functions with cyclomatic complexity >= 10
engine.complexity_hotspots(10)

# What functions can raise a given exception? (uses parser-detected
# exception_types; no runtime tracing required)
engine.functions_that_raise("PermissionError")

# Add and query semantic annotations
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 against an earlier snapshot of the same codebase
before = QueryEngine.from_directory("before/")
diff = engine.diff_against(before)
# diff contains: summary_delta, nodes {added/removed/modified},
# edges {added/removed}, entrypoints {added/removed/modified}

# Run the built-in audit-oriented preanalysis passes
engine.preanalysis()
engine.findings()
engine.subgraph_names()

# Programmatic augmentation hooks for external tooling
engine.augment_sarif("results.sarif")
engine.augment_weaudit("findings.json")

NodeKind.SCHEMA、TABLE、VIEW、およびPROCEDURE は v0.5.0 で追加されています;enumの値を網羅的にマッチさせるコンシューマは、それらのケースを追加する必要があります。

開発```bash

Install package and dev dependencies

uv sync --all-groups

Lint and format

uv run ruff check --fix uv run ruff format

Type check

uv tool install ty && ty check

Tests

uv run pytest -q tests/

Mutation testing (on macOS, set this env var to avoid rustworkx fork segfaults)

OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES uv run mutmut run

## ライセンス

Apache-2.0

カテゴリ