trailmark-structural について
trailmark-structuralは、Trailmarkでコードをセキュリティ面から構造分析するスキルです。4つの事前分析をすべて実行し、Trailmarkのバージョンごとの機能差にも対応します。
主な機能
- 01Trailmarkコードグラフの構築
- 024つの事前分析の実行
- 03ホットスポットと汚染データの抽出
- 04影響範囲と特権境界の確認
- 05サブグラフとプロキシノードの出力
活用シーン
- 01セキュリティ監査向けコード箇所の優先付け
- 02対象範囲の構造データ分析
- 03未解決呼び出しとサブグラフ関係の確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ trailofbits/ skills --skill trailmark-structuralインストール後、Claude Code で/trailmark-structuralを入力して読み込まれたか確認します。
同梱ファイルagents/openai.yamlassets/trail-of-bits-mark.svg
詳細
Trailmark の構造解析
Trailmark グラフを構築し、engine.preanalysis()を実行して、4 つすべての事前解析パスを計算します。コアワークフローは v0.2 に対応しています。v0.4 固有の詳細は、メソッドが利用可能か確認した後にのみ含めます。また、新しいビルドでは同じ出力が拡張されます(0.5.0+ では攻撃対象領域のエントリーにattributesキーが追加され、.trailmark/links.tomlからproxy.external:*ノードが追加されます)。ワークフローに変更はありません。
使用する場合
- Vivisect Phase 1 で完全な構造データ(ホットスポット、テイント、影響範囲、権限境界)が必要な場合
- 特定のターゲット範囲について、詳細な事前解析パスが必要な場合
- 監査の優先順位付けに使う複雑度データとテイントデータを生成する場合
- Trailmark 0.4.0+ がインストールされている環境で、プロキシ/未解決の呼び出し数、サブグラフのエッジ、型参照の概要を調べる場合
使用しない場合
- 簡単な概要だけが必要な場合(代わりに
trailmark-summaryを使用してください) - アドホックなコードグラフクエリを実行する場合(メインの
trailmarkスキルを直接使用してください) - ターゲットが小さな単一ファイルで、構造解析に価値がない場合
退けるべき合理化
| 合理化 | それが誤りである理由 | 必須の対応 |
|---|---|---|
| 「概要分析で十分です」 | 概要ではテイント、影響範囲、権限境界のデータが省かれます | 詳細なデータが必要な場合は、完全な構造解析を実行します |
| 「1 つのパスで十分です」 | 各パスは相互に参照するため、影響範囲を調べずにテイントだけを見ると重要なノードを見落とします | 4 つすべてのパスを実行します |
| 「ツールがインストールされていないので、手作業で分析します」 | 手作業の分析では、ツールが検出できるものを見落とします | 「trailmark is not installed」と報告して終了します |
| 「パスの出力が空なら、パスの実行に失敗しています」 | コードベースによっては、一部のパスでデータが生成されないことがあります(例:権限境界がない場合) | 出力全体をそのまま返します |
| 「v0.4 のフィールドは常にあります」 | ユーザーが Trailmark 0.2.x をまだインストールしている場合があります | v0.4 固有のメソッドを呼び出す前にhasattr()で確認します |
使用方法
ターゲットディレクトリはargsパラメーターで渡します。
実行
ステップ 1:trailmark が利用可能か確認します。
trailmark analyze --help 2>/dev/null || \
uv run trailmark analyze --help 2>/dev/null
どちらのコマンドも動作しない場合は、「trailmark is not installed」と報告して終了します。pip install、uv pip install、git clone、またはインストールコマンドを実行してはいけません。trailmark はユーザー自身がインストールする必要があります。
必要に応じて、バージョンを記録します。
trailmark --version 2>/dev/null || uv run trailmark --version 2>/dev/null || true
このコマンドが見つからなくても失敗とせず、以下の API 機能チェックを使用します。
ステップ 2:Trailmark の解析 API で言語を検出します。
python3 - "{args}" <<'PY'
import json
import sys
try:
from trailmark.parse import detect_languages # canonical location since 0.3.x
except ModuleNotFoundError:
# v0.2.x predates trailmark.parse; the same function lives in query.api
from trailmark.query.api import detect_languages
print(json.dumps(detect_languages(sys.argv[1])))
PY
インポートに失敗した場合は、同じスニペットをuv run --with trailmark python - "{args}"で再実行します。結果が[]の場合は、「Trailmark found no supported languages under target」と報告して終了します。
ステップ 3:QueryEngineを使用して完全な構造解析を実行します。
このスニペットをpython3で実行します。インポートに失敗した場合は、同じスニペットをuv run --with trailmark python - "{args}"で再実行します。
python3 - "{args}" <<'PY'
import json
import sys
try:
from trailmark.parse import detect_languages # canonical location since 0.3.x
except ModuleNotFoundError:
# v0.2.x predates trailmark.parse; the same function lives in query.api
from trailmark.query.api import detect_languages
from trailmark.query.api import QueryEngine
target = sys.argv[1]
languages = detect_languages(target)
engine = QueryEngine.from_directory(target, language="auto")
preanalysis = engine.preanalysis()
def summarize_subgraph(name: str, limit: int = 25) -> dict[str, object]:
nodes = engine.subgraph(name)
summary = {
"count": len(nodes),
"sample_ids": [node["id"] for node in nodes[:limit]],
}
if hasattr(engine, "subgraph_edges"):
summary["edge_count"] = len(engine.subgraph_edges(name))
return summary
graph = json.loads(engine.to_json())
nodes = graph.get("nodes", {})
proxy_nodes = [
node_id for node_id, node in nodes.items()
if node.get("kind") == "proxy" or node.get("origin") == "proxy"
]
payload = {
"languages": languages,
"summary": engine.summary(),
"preanalysis": preanalysis,
"attack_surface": engine.attack_surface()[:25],
"hotspots": engine.complexity_hotspots(10)[:25],
"proxy_nodes": proxy_nodes[:25],
"subgraphs": {
name: summarize_subgraph(name)
for name in engine.subgraph_names()
},
}
if hasattr(engine, "type_references"):
payload["type_reference_samples"] = {
node_id: engine.type_references(node_id)[:10]
for node_id in list(nodes)[:25]
}
print(json.dumps(payload, indent=2))
PY
ステップ 4:出力を確認します。
出力には以下が含まれている必要があります。
languagessummarypreanalysishotspots(空の場合もあります)proxy_nodes(v0.2.x、または未解決の呼び出しがない場合は空です。0.5.0+ では、.trailmark/links.tomlで宣言されたproxy.external:*エントリーが含まれる場合があります)- 件数とサンプル ID を含む
subgraphs
Trailmark 0.5.0+ では、attack_surfaceのエントリーにattributesオブジェクト(例:solidity_visibility、solidity_overridden_by)が含まれる場合があります。変更せずにそのまま渡してください。後続のコンシューマーは、エントリーポイントの優先順位付けにこれを使用します。
コードベースによっては、一部のサブグラフのノード数がゼロになることがあります(これは正常です)。JSON ペイロード全体をそのまま返してください。