irify-sast について
irify-sastは、コーディングエージェント向けの静的解析スキルです。IRifyのSSAコンパイラーとSyntaxFlowでファイルをまたぐデータフローを調べ、yaklang MCP サーバーを通じたクエリ主導のセキュリティ解析と差分コンパイルを案内します。
主な機能
- 01SSAによるファイル横断解析
- 02SyntaxFlowクエリの案内
- 03差分コンパイルの手順
- 04意図に沿ったセキュリティクエリ
- 05クエリ構文エラーの再試行案内
活用シーン
- 01コード内のユーザー入力の追跡
- 02指定された脆弱性経路の検出
- 03呼び出し元と値の行き先の特定
- 04コード変更後の解析更新
MCP 対応
yaklang MCP サーバーの設定が必要で、`yak mcp -t ssa` コマンドを使用します。このスキルはSSAのコンパイルおよびクエリ機能を利用します。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ yaklang/ irify-sast-skill --skill irify-sastインストール後、Claude Code で/irify-sastを入力して読み込まれたか確認します。
同梱ファイルnativecall-reference.mdsyntaxflow-examples.md
詳細
IRify SAST
IRify の SSA コンパイラーと SyntaxFlow クエリエンジンを活用した、詳細な静的解析スキルです。
前提条件
このスキルには yaklang MCP サーバーが必要です。エージェントの MCP 設定で構成してください。
# Codex: ~/.codex/config.toml
[mcp_servers.yaklang-ssa]
command = "yak"
args = ["mcp", "-t", "ssa"]
// Claude Code / Cursor / others
{ "command": "yak", "args": ["mcp", "-t", "ssa"] }
ワークフロー:エンジン優先(sf → read → rg)
重要:必ず エンジン優先のファネルモデルに従ってください。SSA エンジンは、すべてのファイルを横断するプロシージャ間のデータフローを同時に把握できますが、grep にはできません。クエリを実行する前に grep/rg を使って「候補ファイルのプール」を作成しないでください。まずエンジンをレーダーとして活用してください。
ステップ 1:コンパイル(プロジェクトごとに 1 回、自動でキャッシュ)
ssa_compile(target="/path/to/project", language="java", program_name="MyProject")
→ full compilation, returns program_name
自動キャッシュ:プログラムがすでにコンパイル済みで、ソースファイルに変更がない場合、エンジンは再コンパイルせずに即座に [Cache Hit] を返します。キャッシュを有効にするには、必ず program_name を指定してください。
ステップ 2:クエリ — SyntaxFlow をグローバルレーダーとして使う
コンパイル済みの IR に対して、SyntaxFlow ルールを直接組み立てて実行します。候補を見つけるために grep で事前スキャンしないでください。
ssa_query(program_name="MyProject", rule="<SyntaxFlow rule>")
エンジンはメモリ内の SSA グラフ全体をたどり、すべてのファイル境界を横断します。1 つのクエリで何十もの grep コマンドに相当する調査を実行でき、データフローにおける誤検知はありません。
ステップ 3:読み取り — Read を顕微鏡として使う
ssa_query が具体的なファイルパスと行番号を返したら、Read を使って周辺のコンテキスト(±20 行)を確認します。検出箇所が実際の業務コードなのか、未使用コードやテストコードなのかを検証してください。
ステップ 4:grep — Grep/Glob はコード以外のファイルにのみ使う
Grep/Glob は、SSA エンジンが処理しない内容にのみ使ってください。
- 設定ファイル(
.yml、.xml、.properties、logback.xml) - 静的リソース、テンプレート、ビルドスクリプト
- 正確な文字列がすでに分かっている場合の、名前やパスの簡易検索
ソースコード内のデータフローパターンの検索に Grep を絶対に使わないでください。そのために ssa_query があります。
増分コンパイル(コード変更時)
ssa_compile(target="/path/to/project", language="java", base_program_name="MyProject")
→ only changed files recompiled, ProgramOverLay merges base + diff layers
→ returns NEW program_name for subsequent queries
重要:増分コンパイルには base_program_name を使用してください。re_compile=true はすべてのデータを破棄する完全な再コンパイルです。完全に新しい状態から始める場合にのみ使用してください。
自己修復クエリ(構文エラー時に自動再試行)
ssa_query が SyntaxFlow の解析エラーを返した場合:
- ユーザーに謝罪したり、助けを求めたりしないでください
- エラーメッセージを読みます。正確な解析エラーの位置と、期待されるトークンが含まれています
- エラーに基づいて SyntaxFlow ルールを修正します
- 修正したルールで
ssa_queryを再実行します - 失敗を報告する前に、最大 3 回繰り返します
- すべての再試行が失敗した場合は、元のルール、試した修正内容、および最終エラーをユーザーに提示します
重要:ユーザーの意図に従う
ユーザーが脆弱性検出を明示的に求めていない限り、ソースからシンクへの脆弱性ルールを自動的に作成しないでください。
- ユーザーが「ユーザー入力を探して」と依頼した場合 → ソースのみのルールを作成し、すべての入力エンドポイントを列挙します
- ユーザーが「SQL インジェクションを探して」と依頼した場合 → ソースからシンクへの taint ルールを作成します
- ユーザーが「この値がどこへ行くか教えて」と依頼した場合 → 前方トレース(
-->)ルールを作成します - ユーザーが「この関数を呼び出すものは何か」と依頼した場合 → 呼び出し箇所のルールを作成します
ソースのみのクエリ例(Java)
ユーザー入力、HTTP エンドポイント、または制御可能なパラメーターについて尋ねられた場合:
// Find all Spring MVC controller handler methods
*Mapping.__ref__?{opcode: function} as $endpoints;
alert $endpoints;
// Find all user-controllable parameters in Spring controllers
*Mapping.__ref__?{opcode: function}<getFormalParams>?{opcode: param && !have: this} as $params;
alert $params;
// Find GetMapping vs PostMapping endpoints separately
GetMapping.__ref__?{opcode: function} as $getEndpoints;
PostMapping.__ref__?{opcode: function} as $postEndpoints;
alert $getEndpoints;
alert $postEndpoints;
ソースからシンクへのクエリ例(ユーザーが脆弱性検出を求めた場合のみ)
// RCE: trace user input to exec()
Runtime.getRuntime().exec(* #-> * as $source) as $sink;
alert $sink for {title: "RCE", level: "high"};
// SQL Injection (MyBatis): detect ${} unsafe interpolation in XML mappers / annotations
// <mybatisSink> is a dedicated NativeCall that finds all MyBatis ${} injection points
<mybatisSink> as $sink;
$sink#{
until: `* & $source`,
}-> as $result;
alert $result for {title: "SQLi-MyBatis", level: "high"};
セキュリティに関する積極的な知見の提示
クエリを実行して結果が見つかったら、フォローアップの質問や提案を積極的に提示してください。結果を提示するだけで終わらないでください。
脆弱性が見つかった場合:
- 修正を提案する:「この
exec()呼び出しには、サニタイズされていないユーザー入力が渡されています。許可リストを使用するか、引数を明示的に分離してProcessBuilderを使用することを検討してください。」 - 関連する質問をする:
- 「ほかにも
Runtime.exec()を呼び出しているエンドポイントがないか確認しましょうか?」 - 「入力元からシンクまでの間に入力の検証やサニタイズが行われているか、追跡しましょうか?」
- 「ほかのコントローラーにも同様のパターンがないか調べましょうか?」
- 「ほかにも
- 関連項目を相互参照する:ある種類の脆弱性が見つかった場合は、関連する種類も積極的にスキャンします:
- RCE が見つかった場合 → 「SSRF も確認したところ、潜在的な問題が 2 件見つかりました。詳細を確認しますか?」
結果が見つからなかった場合:
- 「結果なし」とだけ伝えず、理由を説明する:
- 「直接の
exec()呼び出しは見つかりませんでしたが、ProcessBuilderの使用が見つかりました。代わりにそちらを確認しましょうか?」 - 「クエリに一致するシンクは 0 件でした。コードでフレームワークの抽象化が使われている可能性があります。フレームワーク固有のパターンを検索しましょうか?」
- 「直接の
- 代替クエリを提案する
結果が曖昧な場合:
- 確認を求める:「
executeQuery()へのデータフローパスが 8 件見つかりましたが、そのうち 5 件はパラメーター化クエリを使用しており安全です。文字列連結を使用している 3 件だけに絞り込みますか?」
関連リファレンスファイル
SyntaxFlow ルールを作成する際は、構文の確認や実例の参照に、Read ツールを使って以下のファイルを読んでください:
| ファイル | 読むタイミング | パス(このファイルからの相対パス) |
|---|---|---|
| NativeCall リファレンス | <nativeCallName()> 関数が必要なルールを作成するとき — 構文と例を含む 40 種類以上の NativeCall 関数 | nativecall-reference.md |
| SyntaxFlow の例 | 新しいルールを作成するとき — 脆弱性の種類別に整理された、Java/Go/PHP/C を網羅する 20 以上の実運用ルール | syntaxflow-examples.md |
ワークフロー:
- 類似するルールのパターンを探すために
syntaxflow-examples.mdを読む - NativeCall が必要な場合は、
nativecall-reference.mdを読む - 作成して
ssa_query経由で実行する
SyntaxFlow クイックリファレンス
検索とマッチング
documentBuilder // variable name
.parse // method name (dot prefix)
documentBuilder.parse // chain
*config* // glob pattern
/(get[A-Z].*)/ // regex pattern
関数呼び出しとパラメーター
.exec() // match any call
.exec(* as $params) // capture all params
.parse(*<slice(index=1)> as $a1) // capture by index
データフロー演算子
| 演算子 | 方向 | 用途 |
|---|---|---|
#> | 1 レベル上へ | 直接の定義 |
#-> | 再帰的に上へ | 起点を追跡 —「これはどこから来るのか?」 |
-> | 1 レベル下へ | 直接の使用箇所 |
--> | 再帰的に下へ | 最終的な使用箇所を追跡 —「これはどこへ行くのか?」 |
.exec(* #-> * as $source) // trace param origin
$userInput --> as $sinks // trace where value goes
$sink #{depth: 5}-> as $source // depth-limited trace
$val #{
include: `*?{opcode: const}`
}-> as $constSources // filter during trace
$sink #{
until: `* & $source`, // stop when reaching source
}-> as $reachable
フィルター ?{...}
$vals?{opcode: call} // by opcode: call/const/param/phi/function/return
$vals?{have: 'password'} // by string content
$vals?{!opcode: const} // negation
$vals?{opcode: call && have: 'sql'} // combined
$factory?{!(.setFeature)} // method NOT called on value
変数、チェック、アラート
.exec() as $sink; // assign
check $sink then "found" else "not found"; // assert
alert $sink for { title: "RCE", level: "high" }; // mark finding
$a + $b as $merged; // union
$all - $safe as $vuln; // difference
NativeCall(40 種類以上の組み込み関数)
よく使われる関数 — 全リストは nativecall-reference.md を参照してください:
<include('rule-name')> // import lib rule
<typeName()> // get short type name
<fullTypeName()> // get full qualified type name
<getReturns> // function return values
<getFormalParams> // function parameters
<getFunc> // enclosing function
<getCall> // find call sites
<getCallee> // get called function
<getObject> // parent object
<getMembers> // object members
<name> // get name
<slice(index=N)> // extract by index
<mybatisSink> // MyBatis SQL injection sinks
<dataflow(include=`...`)> // filter data flow paths
ヒント
#->は「これはどこから来るのか?」、-->は「これはどこへ行くのか?」を表します- パラメーターには
*を使い、名前をハードコードしないでください - SSA は代入を解決します:
a = getRuntime(); a.exec(cmd)=getRuntime().exec(cmd) opcodeフィルターを使って、定数、パラメーター、呼び出しを区別します- 実用的な結果を得るには、
checkとalertを組み合わせます - コード変更後に高速な増分更新を行うには、
re_compileではなくbase_program_nameを使います - 新しいルールを書く前に、類似したパターンを探すために
syntaxflow-examples.mdを読んでください - NativeCall について不明な点がある場合は、使用方法や例を確認するために
nativecall-reference.mdを読んでください