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

trailmark v0.5.0

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

共有

Trailmark

CI Mutation Testing

ソースコードを、関数、クラス、呼び出し、セマンティックアノテーションのクエリ可能なグラフに解析し、セキュリティ分析に活用します。

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

仕組み

Trailmarkは、parse、index、queryの3つのフェーズで動作します。

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 にパースして以下を抽出します:

  • ノード — 関数、メソッド、クラス、構造体、インターフェース、トレイト、列挙型、モジュール、名前空間
  • エッジ — 呼び出し、継承、実装、包含、インポート
  • メタデータ — 型注釈、循環的複雑度、分岐、docstring、例外型

対応言語

言語拡張子主要な構文要素
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 ブロック、verus! ブロック
Solidity.solコントラクト、インターフェース、ライブラリ、関数、修飾子、構造体、列挙型
Cairo.cairo関数、トレイト、構造体、列挙型、impl ブロック、StarkNet コントラクト
Circom.circomテンプレート、関数、シグナル、コンポーネント
Haskell.hs関数、データ型、型クラス、インスタンス
Erlang.erl関数、レコード、ビヘイビア、モジュール
Miden Assembly.masmプロシージャ、エントリポイント、定数、呼び出し
Swift.swift関数、クラス、構造体、列挙型、プロトコル、拡張
Objective-C.m, .mm, .hC 関数、クラス、メソッド(セレクタベースの命名)
Kotlin.kt, .kts関数、クラス、インターフェース、データクラス、オブジェクト、メソッド
Dart.dart関数、クラス、抽象クラス、メソッド、コンストラクタ
Move.moveモジュール、関数、インポート、直接呼び出し
Tact.tactコントラクト、構造体、レシーバ、関数
Func.fc, .func関数、インクルード、直接呼び出し
Sway.swABI インターフェース、構造体、impl メソッド、関数
Rego.regoパッケージ、インポート、ポリシールール、ルール呼び出し
Proto.protoサービス、RPC、メッセージ、フィールド、列挙型
Thrift.thriftサービス、関数、構造体、フィールド、列挙型
GraphQL.graphql, .gqlオブジェクト型、ルート操作、フィールド、列挙型
SQL.sqlスキーマ、テーブル、ビュー、関数、プロシージャ

Rustパーサーは、verus! { ... } ブロックから本体を持つ通常の関数、spec 関数、proof 関数を抽出します。Verus の文法は現在、波括弧で区切られたブロックのみをサポートしており、verus!(...) と verus![...] は警告付きでスキップされます。インラインモジュールは修飾 ID(例えば file.inner:function)を保持し、メソッドはその impl またはトレイトの所有者を保持します。各ブロックは独立して解析され、構文エラーはファイルとブロックの位置を含む警告を生成します。vstd::prelude::verus! のような修飾された呼び出しもサポートされています。ローカルヘルパー関数は、その ID に外側の関数を保持します(例えば file:outer.inner)。これらのヘルパーへの呼び出しや、use super::* のような相対インポートは、ファイル内に存在する宣言に解決されます。異なるブロック内で繰り返されるローカル名には、ノードを区別するためにバイトオフセットの接尾辞が付与され、呼び出しはブロックスコープを尊重します。ローカルの構造体、列挙型、トレイト、インラインモジュールも外側の関数を保持します。例えば file:outer.Guard.drop や file:outer.local.helper のようになります。これらの本体は別個の呼び出しと複雑度を持ちます。既知のローカル型やモジュールへの明示的な呼び出しは、ファイル内で解決されます。裸の呼び出しは関数スコープとモジュールスコープを検索し、関連項目は Type::method のようなパスを使用します。ドット付き呼び出しは、レシーバが既知の構造体または列挙型を指す場合にのみ解決されます。self.method() を含むその他の値レシーバは未解決のままです。暗黙のデストラクタ呼び出しとレシーバの型推論はモデル化されません。これらの規則は通常の Rust にも Verus ブロックにも適用されます。オフセット接尾辞を持つ宣言の前のコードを編集するとその ID が変わるため、trailmark diff はその宣言を削除および追加として報告する可能性があります。

Trailmark は任意のマクロを展開しません。不透明なマクロや関数本体の中にあるリテラルの Verus 呼び出しは、警告付きでスキップされます。呼び出されていない macro_rules! 定義は展開されません。本体を持たない関数シグネチャや、requires/ensures 節内の呼び出しは抽出されません。

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(動的ディスパッチまたは曖昧な解決)としてタグ付けされます。

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)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)ノードのアノテーションを取得(オプションで種類によるフィルタリング)
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
    }

カテゴリ