trailmark について
Trailmarkは、コードを関数、型、呼び出し、メタデータのグラフに解析する静的コード分析スキル兼コマンドラインツールです。複数言語のセキュリティレビュー向けに、グラフクエリや事前分析、リポジトリ間を結ぶ任意の設定を提供します。
主な機能
- 01コード構造と呼び出しのグラフを構築
- 02経路、部分グラフ、エントリーポイントを検索
- 03汚染伝播と権限境界を事前分析
- 04複数言語のパーサーに対応
- 05設定によりリポジトリ境界を接続
- 06監査注釈でグラフを拡張
活用シーン
- 01ユーザー入力から機密関数への経路を追跡
- 02複雑度に基づき監査対象を優先
- 03攻撃対象領域とエントリーポイントを整理
- 04FFIやRPC境界の両側にあるコードを接続
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ trailofbits/ skills --skill trailmarkインストール後、Claude Code で/trailmarkを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
agents/openai.yamlassets/trail-of-bits-mark.svgreferences/preanalysis-passes.mdquery-patterns.md詳細
Trailmark
セキュリティ分析のために、ソースコードを関数、クラス、呼び出し、およびセマンティックメタデータの有向グラフに解析します。
使用する場面
- ユーザー入力から機密性の高い関数までの呼び出し経路をマッピングする
- 監査の優先順位付けのために、複雑さのホットスポットを見つける
- 攻撃対象領域とエントリーポイントを特定する
- 不慣れなコードベースの呼び出し関係を把握する
- 複数の言語を使うプロジェクトのセキュリティレビューや監査の準備をする
- LLM が推論した注釈(前提条件、事前条件)をコード単位に追加する
- 外部のバイナリ解析グラフをインポートし、ソースとバイナリのビューをつなぐ
- 推移的スライス、エントリーポイントへの経路、サブグラフのエッジ、または型参照をクエリする
- 疑わしい関数または候補となる指摘について、グラフの証拠を作成する
- 変更テスト(genotoxic スキル)や図表作成の前に事前分析を行う
使用しない場面
- 呼び出しグラフを作成する価値がない単一ファイルのスクリプト(ファイルを直接読んでください)
- コードから導出されていないアーキテクチャ図(
diagramming-codeスキルを使うか、手作業で描いてください) - 変更テストのトリアージ(内部で trailmark を呼び出す genotoxic スキルを使ってください)
- 実行時の動作分析(trailmark は静的解析用であり、動的解析用ではありません)
避けるべき誤った判断
| 誤った判断 | それが誤りである理由 | 必須の対応 |
|---|---|---|
| 「ソースファイルを手作業で読めばよい」 | 手作業で読むと、呼び出し経路、影響範囲、汚染データを見落とします | trailmark をインストールして API を使ってください |
| 「簡単なクエリなら事前分析は不要だ」 | 影響範囲、汚染、権限のデータは preanalysis() の実行後にのみ利用できます | 他のスキルに引き継ぐ前に、必ず engine.preanalysis() を実行してください |
| 「グラフが大きすぎるので、サンプリングしよう」 | サンプリングではモジュールをまたぐ攻撃経路を見落とします | グラフ全体を作成し、サブグラフのクエリで対象を絞ってください |
| 「不確かなエッジは重要ではない」 | 型の混同によるバグは、動的ディスパッチに潜んでいます | セキュリティ上の主張では uncertain エッジを考慮してください |
| 「単一言語の分析で十分だ」 | 複数の言語を使うリポジトリでは、バグが集中する FFI の境界があります | コンポーネントごとに適切な --language フラグを使ってください |
| 「確認する価値があるのは複雑さのホットスポットだけだ」 | 汚染された経路上にある複雑さの低い関数も、重要な標的です | 複雑さのデータを汚染と影響範囲のデータと組み合わせてください |
| 「ドキュメントにバージョンで利用が制限されるメソッドが載っているので、どこでも呼び出せる」 | 多くの環境では、今も Trailmark 0.2.x がインストールされています | v0.4+/v0.5+ の機能を使う前に、インストール済みのバージョンを確認するか、機能の利用可否を調べてください |
インストール
必須: trailmark が見つからない場合は、他の作業を始める前に CLI をインストールしてください。
uv tool install trailmark
ツールをインストールしても CLI のみが提供され、import trailmark が解決できるようになるわけではありません。
このスキルの Python スニペットは uv run --with trailmark python - で実行してください。インポートエラーやスニペット内の ModuleNotFoundError を解決するのはインストールではなく、この実行方法です。
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 で判別してください。プログラムから利用する場合は、v0.4専用のメソッドが存在すると決めつけず、hasattr() で確認して代替手段を使用してください。
if hasattr(engine, "subgraph_edges"):
edges = engine.subgraph_edges("tainted")
else:
# v0.2 fallback: filter engine.to_json() edges whose endpoints
# are both in engine.subgraph("tainted")
edges = []
v0.2互換のベースライン: CLIの analyze、diff、entrypoints、augment、--language auto;QueryEngine.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で追加: モジュールレベルの detect_languages() と supported_languages() を備えた trailmark.parse モジュール。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向けのスキーマ、テーブル、ビュー、関数、プロシージャ、依存関係);ノード種別 schema、table、view、procedure;リポジトリリンク設定 .trailmark/links.toml(詳しくは下記の「リポジトリリンク」を参照)。宣言された外部エンドポイント用の proxy.external:<symbol> ノードも含まれます;リポジトリリンク、解決できない呼び出しのプロキシ、type_uses エッジが単一言語のディレクトリ解析でも生成されるようになりました(0.4では多言語解析の場合のみ生成されていました);パーサーメタデータから検出されるSolidityのエントリーポイント(インターフェースは除外;ノード属性 solidity_visibility、solidity_mutability、solidity_override、solidity_container_kind、solidity_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 kinds are 0.5+
クイックスタート
# Auto-detect and merge every supported language under the tree
uv run trailmark analyze --language auto --summary {targetDir}
# Explicit languages (single language or comma-separated list)
uv run trailmark analyze --language rust {targetDir}
uv run trailmark analyze --language python,rust {targetDir}
# Complexity hotspots
uv run trailmark analyze --language auto --complexity 10 {targetDir}
# Entrypoint inventory and structural diff (v0.2-safe)
uv run trailmark entrypoints --language auto {targetDir}
uv run trailmark diff --language auto --repo {repoDir} main HEAD --json
# Version report (0.2.2+)
uv run trailmark --version
# v0.4+: native diagram command
uv run trailmark diagram -t {targetDir} -T call-graph -f main --depth 2
プログラムからのAPI利用
# trailmark.parse is a 0.3+ module; on 0.2.x import detect_languages from
# trailmark.query.api instead (supported_languages has no 0.2.x equivalent)
from trailmark.parse import detect_languages, supported_languages
from trailmark.query.api import QueryEngine
# Ask the installed Trailmark build what it supports
supported_languages()
detect_languages("{targetDir}")
# Prefer auto for unknown or polyglot trees; use explicit lists when needed
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()
# Transitive slices and entrypoint path queries (v0.2-safe)
engine.ancestors_of("sensitive_sink")
engine.reachable_from("entry_func")
engine.entrypoint_paths_to("sensitive_sink")
# v0.4+: connect named subgraphs
if hasattr(engine, "connect_subgraphs"):
engine.connect_subgraphs("tainted", "privilege_boundary")
# Run pre-analysis (blast radius, entrypoints, privilege
# boundaries, taint propagation)
result = engine.preanalysis()
# Query subgraphs created by pre-analysis
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")
# Add LLM-inferred annotations
from trailmark.models import AnnotationKind
engine.annotate("function_name", AnnotationKind.ASSUMPTION,
"input is URL-encoded", source="llm")
# Query annotations (including pre-analysis results)
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+: generic/type-reference and binary augmentation APIs
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スキルに渡す前に、必ず engine.preanalysis() を実行してください。(diagramming-code は独自のグラフを構築するため、これを使用しません。)
事前解析では、次の4つのパスによってグラフを拡張します。
- 影響範囲の推定 — 関数ごとに下流ノードと上流ノードを数え、複雑度が高く重要な子孫ノードを特定します
- エントリーポイントの列挙 — 信頼レベルごとにエントリーポイントをマッピングし、到達可能なノード集合を計算します
- 権限境界の検出 — 信頼レベルが変わる呼び出しエッジ(信頼されていない -> 信頼されている)を検出します
- 汚染伝播 — 信頼されていないエントリーポイントから到達可能なすべてのノードにマークを付けます
結果は、グラフ上のアノテーションおよび名前付きサブグラフとして保存されます。
詳しいドキュメントについては、 references/preanalysis-passes.md を参照してください。
言語の選択
下流のワークフローに古くなった言語一覧をハードコードしないでください。インストール済みの Trailmark ビルドに、サポート対象を確認してください。
from trailmark.parse import detect_languages, supported_languages
supported_languages()
detect_languages("{targetDir}")
CLI のパターン:
# Auto-detect and merge
uv run trailmark analyze --language auto {targetDir}
# Explicit list for a known polyglot target
uv run trailmark analyze --language python,rust {targetDir}
Trailmark 0.5.0 時点で、パーサー名には python、javascript、
typescript、php、ruby、c、cpp、c_sharp、java、go、rust、
solidity、cairo、circom、haskell、erlang、masm、swift、objc、
kotlin、dart、move、tact、func、sway、rego、proto、thrift、
graphql、sql(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" # any EdgeKind; defaults to calls
confidence = "certain" # certain | inferred | uncertain; defaults to inferred
description = "JSON-RPC eth_call"
[[link]]
source = "backend:notify"
target = "payments-webhook"
target_external = true # required because target is unresolved
エンドポイント参照には、正確なノード ID、または一意の名前/サフィックスを使用できます。検証は安全側に倒して失敗します。参照の曖昧さ、未知の内部エンドポイント、無効な列挙値、不正な TOML がある場合、グラフを暗黙に弱めるのではなく、ValueError が発生します。source_external = true/target_external = true を指定すると、未解決のエンドポイントを許可し、proxy.external:<symbol> ノードを作成します。設定したエッジには configured_by = .trailmark/links.toml 属性が付与されるため、パーサー由来のエッジと区別できます。
合理化テーブルで警告されている FFI/RPC 境界をまたぐ監査では、この機能を使用してください。まず境界エッジを宣言すれば、パス検索と汚染クエリで、他の呼び出しエッジと同様に境界を越えられます。
グラフモデル
ノードの種類: function、method、class、module、struct、
interface、trait、enum、namespace、contract、library、
template。v0.4 以降では、未解決の参照も proxy ノードとして生成されます。v0.5 以降では、SQL
グラフに schema、table、view、procedure が追加されます。
ノードの起点: v0.4 以降のノードには、起点として source、proxy、
binary、synthetic を設定できます。v0.2 のエクスポートでは、起点が省略される場合があります。
エッジの種類: calls、inherits、implements、contains、imports。
v0.4 以降では、resolves_to、type_uses、specializes、
corresponds_to が追加されます。
エッジの信頼度: certain(直接呼び出し、self.method())、inferred
(self 以外のオブジェクトへの属性アクセス)、uncertain(動的ディスパッチ)
コード単位ごと
- 型付きパラメーター、戻り値の型、例外の型
- 循環的複雑度と分岐メタデータ
- Docstring
- 注釈:
assumption、precondition、postcondition、invariant、blast_radius、privilege_boundary、taint_propagation、finding、audit_note(最後の 2 つはaugment_sarif/augment_weauditで設定)
エッジごと
- ソース/ターゲットのノード ID、エッジの種類、信頼度
プロジェクトレベル
- 依存関係(インポートされたパッケージ)
- 信頼レベルとアセット値を持つエントリーポイント
- 名前付きサブグラフ(事前解析で設定)
主要な概念
宣言された契約と実効的な入力ドメイン: 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 の注釈、不審な関数、またはレポートの抜粋を 1 つ示しており、引き継ぎにすぐ使える到達可能性と影響範囲の証拠一式を必要としている場合は、trailmark-finding-triage を使用してください。
問題のシードが 1 つ判明しており、ユーザーが variant-analysis、Semgrep、CodeQL、または手動レビュー向けに、グラフから導出した類似箇所の候補を必要としている場合は、trailmark-variant-neighborhood を使用してください。