AIエージェントにAPKやバイナリの解析をやらせようとすると、最初に詰まるのは解析そのものではなく「jadxなのかapktoolなのかFridaなのかIDAなのか」という道具選びだ。reverse-skill(zhaoxuya520/reverse-skill)は、この振り分けを41本のルーティングルールに固定してしまうスキルルーターで、2026年8月16日時点でGitHub Star 25,491・フォーク3,450・MITライセンス。5月13日の公開から3か月でこの規模に達している。
本記事は、READMEの紹介ではなく リポジトリを実際にクローンしてルーターを走らせ、権限ゲートを1条件ずつ崩し、ツール自動導入のハッシュを実物と照合した記録 である。結論から言うと、いちばん効くのは日本語話者にとっての落とし穴だった。
この記事のポイント(30秒でわかる)
・PRIMARY判定にLLMは入っていない。routing.json の41ルート・49キーワード規則を正規表現で照合し、命中数で採点するだけの決定論的な処理
・日本語ヒントは空振りしうる。\bapk\b のような単語境界が日本語の連続文字列では成立せず、「このAPKを解析して」はR0へ転落した。半角スペース1つで直る
・166ケースの回帰テストは全て通るが、日本語は1件も無い(中国語97・英語69・日本語0)。CIが緑でも日本語入力は守られていない
・ACT前の権限ゲートは実在し、節をまたいだ偽装は弾いた。ただし --force で全条件が警告に降格する
・GitHubリリースのSHA-256照合は本物。jadx v1.5.6 を実際に落として照合し一致を確認した。ただし事前ハッシュがあるのは24能力中2件
道具の自動導入を伴うスキルパックは、それ自体が開発環境への新しい侵入経路になりうる。前提として押さえておきたい攻撃面の全体像はサプライチェーンセキュリティ2026|攻撃手法・防御ツール・実践チェックリストにまとめてある。
reverse-skillとは——逆解析タスクを41ルールで振り分けるスキルパック
reverse-skillが解こうとしている問題は、READMEの “Why this exists” に4行で書かれている。AIエージェントは与えられた対象に対してどのツールを使うべきか知らない、APK・ELF・JS・PCAP・CTFはそれぞれ別の手順書を必要とする、ツールとMCPサーバーはマシンごとに散らばっている、経験が再利用されないので同じ失敗を繰り返す——の4つだ。
対処として置かれているのが「ルーティング」「スキルモジュール」「ケース管理」「ツールチェーン自動導入」の4層である。実際のリポジトリを数えると、規模はこうなる。
| 要素 | 実測値 | 確認方法 |
|---|---|---|
| ルーティングルール | 41(R0〜R40) | skills/config/routing.json を集計 |
| キーワード規則 | 49 | 同上(1ルートに複数規則あり) |
| スキルモジュール | 42 | skills/ 配下の SKILL.md は全43個で、うち1つは総合入口の skills/SKILL.md。残る42個が INDEX.md の登録数と一致 |
| CTFサブスキル | 42 | CTF-Sandbox-Orchestrator/ 直下のディレクトリ数(competition-* が41+オーケストレータ本体1) |
| 自動導入対象ツール | 24能力 | skills/scripts/bootstrap-manifest.json |
| 回帰テストケース | 166 | skills/tests/routing-benchmark.json |
| リポジトリ総ファイル数 | 586 | git clone --depth 1 後に集計 |
スキルモジュールの内訳は、apk-reverse / mobile-reverse / ida-reverse / radare2 / ghidra-reverse / js-reverse / dotnet-reverse / macos-reverse / go-rust-reverse といった解析側と、malware-analysis / digital-forensics / threat-hunting / supply-chain-security / llm-security といった防御・分析側が混在している。攻撃寄りのモジュール(attack-chain / pwn-chain / edr-bypass-re など)も含まれるが、いずれも後述する権限ゲートを通した「認可済みの対象」を前提にした設計になっている。本記事では手法カタログには立ち入らず、振り分けの仕組みと、実行を止める側の実装だけを扱う。
ライセンスはリポジトリ本体がMIT、CTF-Sandbox-Orchestrator/ 配下のみGPLv3という二層構成だ。READMEは Pentest Swarm AI(AGPL-3.0)について「CLIまたはMCC経由で呼び出すだけでソースは含まない」と明記しており、ライセンス境界の扱いは丁寧な部類に入る。
検証環境
以下すべて、次の環境で実際に実行した結果である。
・macOS 14.5(Darwin 23.5.0 / Apple Silicon)
・Python 3(bash版ルーターが依存)、Node.js、bash 3.2
・reverse-skill を git clone --depth 1 した 2026-08-16 時点の main(VERSION は 1.0.1)
・PowerShellランタイム(pwsh)は未導入。したがって .ps1 のみで提供されるスクリプトは実行していない(後述)
reverse-skillのルーティングの正体——AI判定ではなく正規表現の採点だった
リポジトリの説明文には “AI-powered routing”、中国語側には「AI 自动路由」とある。ここが最初の検証ポイントになった。付属の skills/scripts/master-route.sh を読むと、実体はこうだ。
bashスクリプトはヒアドキュメントでPythonを起動し、routing.json を読み込む。各ルートの keywords 配列には must(必須パターン)、mustAll(追加の必須条件)、exclude(除外パターン)が入っていて、これを re.search(..., re.IGNORECASE) で照合する。命中したルールごとに1点を加算し、priority 配列の順に見て最高得点のルートをPRIMARYとする。同点なら priority で先に定義された側が勝つ。1つも命中しなければ meta.fallbackId(R0)へ落ちる。
つまり モデル呼び出しもAPIキーも一切登場しない。routing.json 自身の meta.scoring フィールドにも、この採点方式が中国語で明記されている。confidence の決まり方も単純で、命中したルートが1つだけなら high、2つ以上なら medium、0なら low だ。
正規表現で照合"} B -->|"must が命中"| C{"mustAll を
すべて満たす?"} B -->|"命中なし"| F["候補ゼロ"] C -->|"No"| F C -->|"Yes"| D{"exclude に
該当する?"} D -->|"Yes"| F D -->|"No"| E["候補に加点(+1)"] E --> G{"候補は何本?"} F --> H["R0 へフォールバック
confidence: low"] G -->|"1本"| I["PRIMARY 確定
confidence: high"] G -->|"2本以上"| J["priority 順で最高得点
confidence: medium"] H --> K["route-scope.md を書き出す"] I --> K J --> K
これは欠点ではない。むしろ設計上の美点として読むべきところで、決定論的だからこそ166ケースの回帰テストが書けるし、CIで固定できる。実際にmacOSでベンチマークを走らせると全件通った。
# reverse-skill をクローンして回帰テストを実行(Python 3 が必要)
git clone --depth 1 https://github.com/zhaoxuya520/reverse-skill.git
cd reverse-skill && bash skills/scripts/test-routing.sh
# => TOTAL=166 PASS=166 FAIL=0 / 実行時間 約8秒(macOS 14.5 実測)
問題は「AI-powered routing」という説明文から読者が想像するもの——自然言語を理解して意図を汲む振り分け——と、実装(正規表現の完全一致的な照合)のあいだにあるギャップだ。そしてそのギャップは、日本語で使ったときに具体的な形で表面化する。
READMEの数値と実物の差(軽微)
READMEとCHANGELOG v1.0.1(2026-08-08)は回帰ベンチマークを「163ケース」と記載しているが、routing-benchmark.json の実際の件数は166で、ランナーも TOTAL=166 と出力する。v1.0.1リリース後に3件追加され、ドキュメント側が追いついていないだけと見られる。実装の欠陥ではないが、件数を引用するときは実ファイルを数えたほうがよい。
日本語ヒントでreverse-skillのルーターが空振りする条件と回避策
冒頭のGIFがこの節の全てだが、条件を切り分けて確認した。まず素の状態。
# 日本語のヒントをそのまま渡す
bash skills/scripts/master-route.sh --hint "このAPKを解析して"
# PRIMARY -> skills/reverse-engineering/SKILL.md
# Label: General reverse-engineering | confidence: low
# NOTE: No strong keyword hit; open routing.md full matrix
APKと明記しているのにAPK用スキル(R1)へ届かず、汎用フォールバック(R0)に落ちている。原因は routing.json のR1側の定義にある。
"must": "\\bapk\\b|smali|jadx|apktool|\\bandroid\\b|android.?reverse|安卓|反编译.?apk|..."
先頭が \bapk\b、つまり単語境界で囲まれている。Pythonの正規表現では、str型パターンの \w は既定でUnicodeの単語文字にマッチし、漢字・ひらがな・カタカナはいずれも単語文字として扱われる。したがって「このAPKを解析して」を小文字化した このapkを解析して では、a の直前が の(単語文字)、k の直後が を(単語文字)となり、両端とも境界が成立しない。文字列としてはAPKを含んでいるのに、正規表現としては1文字も命中しない。
英数字トークンの前後に半角スペースを1つ入れるだけで、結果は反転する。
# APK の前後に半角スペースを入れただけ
bash skills/scripts/master-route.sh --hint "この APK を解析して"
# PRIMARY -> skills/apk-reverse/SKILL.md
# Label: APK reverse | confidence: high
同じ条件を並べて確認した実測結果が次の表だ。入力文の意味は変えず、区切りだけを変えている。
| ヒント(入力) | PRIMARY | confidence | 判定 |
|---|---|---|---|
apk |
R1 apk-reverse | high | ✅ 通る |
このAPKを解析して |
R0 reverse-engineering | low | ❌ 空振り |
この APK を解析して |
R1 apk-reverse | high | ✅ 通る |
APKを解析して |
R0 reverse-engineering | low | ❌ 空振り |
androidアプリの解析 |
R0 reverse-engineering | low | ❌ 空振り |
android アプリの解析 |
R1 apk-reverse | high | ✅ 通る |
分析apk(中国語) |
R0 reverse-engineering | low | ❌ 空振り |
安卓app分析(中国語) |
R1 apk-reverse | high | ✅ 通る |
抓包分析(中国語) |
R3 js-reverse | high | ✅ 通る |
4行目に注目してほしい。APKを解析して は文頭にAPKがあるので前側の境界は成立するが、k の直後が を なので後ろ側で落ちる。境界は両端必要なので、片方が塞がれば同じことだ。
中国語話者が実質的に困らない理由も表から読める。安卓(Android)や 抓包(パケットキャプチャ)といった中国語キーワードが規則側に多数登録されており、これらは単語境界を使っていないため連続文字列の中でも命中する。実際 routing.json の41ルート中39ルートに中国語キーワードが含まれている。一方で、ひらがな・カタカナは routing.json 全体で1文字も出現しない(0文字)。日本語話者だけが、ASCIIトークンに頼らざるを得ないうえ、日本語は分かち書きしないためそのASCIIトークンが境界で落ちる、という二重の不利を負う。
影響範囲——どのルートが日本語で落ちるか
単語境界を使っているのは41ルート中18ルートで、そこで \b に囲まれているASCIIトークンは21種類ある。
apk / android / ipa / ida / r2 / oauth / pwn / report / k8s / ad / sigma / ot / ics / crx / xpi / golang / rustc / mysql / sdr / urh / ble
これらを日本語の文中で連続表記した場合(「IDAで開いて」「K8sの権限」「BLEの通信を」など)、そのトークンは命中しない。ただし各ルートは複数のパターンをOR結合しているので、同じ規則内の別トークン(中国語キーワードや \b の無い英単語)が命中すれば救われる。したがって「18ルートが必ず壊れる」ではなく、該当トークンだけを頼りにした日本語ヒントが落ちるというのが正確な影響範囲だ。
回避策
実務上の対処は3つある。上から順に手軽い。
・回避策1(即効・推奨):ヒント文字列の英数字トークンの前後に半角スペースを入れる。「この APK を解析して」「IDA で開く」。1文字の追加で済み、リポジトリを変更しない
・回避策2:ヒントを英語で書く。analyze this apk and find the encryption は confidence: high でR1に届くことを確認済み
・回避策3(恒久):skills/config/routing.json に日本語キーワードを追加する。単一のソースファイルなので変更箇所は1つで済む。追加後は必ず bash skills/scripts/test-routing.sh を実行して166ケースが緑のままか確認する
自分の環境でどのヒントが落ちるかは、次のコマンドで一括確認できる。
# 自分がよく使う日本語ヒントを列挙して、空振り(low)するものを洗い出す
for h in "このAPKを解析して" "IDAで開いて関数を追う" "K8sの権限設定を確認" "BLEの通信を解析"; do
printf '%-24s => ' "$h"
bash skills/scripts/master-route.sh --hint "$h" 2>/dev/null | grep '^Label:'
done
# confidence: low が出たヒントは、英数字の前後に半角スペースを入れて再実行する
166ケースの回帰テストがこの空振りを検出しない理由
「これだけ明確な挙動なら、CIが落ちるのでは」と思うところだが、落ちない。理由はベンチマークの中身にある。skills/tests/routing-benchmark.json の166ケースを言語別に分類すると次のようになった。
| 分類 | 件数 | 判定方法 |
|---|---|---|
| 中国語(漢字を含みかなを含まない) | 97 | 正規表現でかな・漢字の有無を判定 |
| 英語(ASCIIのみ) | 69 | 同上 |
| 日本語(かなを含む) | 0 | 同上 |
| カバーされるルート | 41 / 41 | expect フィールドの一意集合 |
CHANGELOG も v1.0.1 の記述で “163 bilingual cases”(バイリンガル)と明言している。つまり設計として英語と中国語の2言語であり、日本語は最初から対象外だ。ルートのカバレッジは41/41で完璧なのに、言語カバレッジには穴がある。
さらに踏み込むと、中国語ケースですら「連続表記」をほとんど検証していない。166ケースのうちASCII文字が漢字と直接隣接している(間に空白が無い)ものはわずか3件で、内訳は 前端签名 加密参数 js逆向(R3)、渗透测试 端口扫描 sql注入(R11)、无线渗透 wifi攻击(R29)。しかもこの3件はいずれも中国語キーワード側(前端.?签名 渗透测试 无线)で命中するため、単語境界の問題が露出しない。ベンチマークの著者は自然と 安卓 APK 加固 反编译 のようにトークンを空白で区切って書いており、その書き癖が問題を覆い隠している。
ここで押さえるべき一般論
「CIが緑」が意味するのは「テストが検証している範囲で壊れていない」ことだけで、検証していない入力については何も言っていない。多言語で使われるルールベースの振り分けを導入するときは、回帰テストの入力言語の分布を必ず確認する。件数(166)とカバレッジ率(41/41)は立派に見えても、軸が1本足りなければその軸は無防備なままになる。
プラットフォームによって「本番の経路」が違う
もうひとつ重要なのは、この決定論ルーターが常に使われるわけではない点だ。README_AI.md の手順6は、OSによって指示を分けている。
・Windows:powershell -File skills/scripts/master-route.ps1 -Hint "<task>" を実行する。スクリプトが本番の経路
・Linux / macOS / Kali:skills/MASTER-ROUTING.md を開く(pwsh があれば同じスクリプトでも可)。つまりAIがマークダウンの梯子を読んで自分で判断する
したがって単語境界の問題を正面から踏むのはWindows利用者と、master-route.sh / .ps1 を明示的に実行した利用者だ。macOS・Linuxで既定の手順に従う場合はLLMが日本語を解釈するので、日本語ヒントでも妥当なスキルに辿り着く可能性が高い。ただしその場合は166ケースの回帰保証が一切かからない——テストが検証しているのはスクリプト側だからだ。決定論の安心を取るか、言語の柔軟さを取るかが、OSによって自動的に決まってしまう構造になっている。
なお MASTER-ROUTING.md(5,687バイト)は全文が中国語で、こちらもかなの出現は0文字だった。LLMが読む前提なので実害は小さいが、日本語での運用を考えるなら翻訳版を用意する価値はある。
reverse-skillのACT前権限ゲートは何を保証し、何を保証しないか
逆解析・ペネトレーションテスト系のスキルパックで最も重要なのは、機能ではなく実行を止める側の実装だ。reverse-skillは RULES.md と skills/ops/scope-contract.md で「auth が granted になるまで対象へACTしない」と定めており、それを機械的に検査する case-guard.sh が付属している。実際に動かして、条件を1つずつ崩してみた。
まずケースを初期化する。
# ケースを作る(scope.md / timeline / workitems が生成される)
bash skills/scripts/case-init.sh --hint "apk reverse" --case-name demo
# auth.status=pending network_profile=offline ready_for_act=false
# ACT前ゲートを実行(未就緒なら exit 2)
bash skills/scripts/case-guard.sh --case-root work/demo; echo "EXIT=$?"
初期状態は auth.status=pending / ready_for_act=false で、ゲートは exit 2 を返した。既定値が「拒否」側に倒れているのは正しい設計だ。ここから条件を1つずつ満たしていく。
| # | scope.md の状態 | 結果 | 残った指摘 |
|---|---|---|---|
| 0 | 生成直後(既定値) | exit 2 | auth.status is not granted / ready_for_act is not true |
| A | auth.status: granted のみ |
exit 2 | ready_for_act is not true |
| B | A+ready_for_act: true |
exit 0 | (なし) |
| C | ## ops_refs 配下に status: granted を追記+ready_for_act: true |
exit 2 | auth.status is not granted |
| D | 全条件達成+テンプレの定型文を削除 | exit 2 | network_profile.mode is offline without offline sample cue |
| F | 既定値のまま --force |
exit 0 | 2件の指摘が「警告」に降格 |
良かった点:節をまたいだ偽装は弾かれた(ケースC)
ケースCは意図的な異常系だ。scope.md の末尾——つまり ## auth ではない場所——に - status: granted という行を追記して、ゲートを騙せるか試した。結果は exit 2 のままで、auth.status is not granted が正しく残った。
スクリプトを読むと、section_field 関数がawkで ## <節名> の見出しを追跡し、目的の節がアクティブな間だけフィールドを拾う実装になっている。ソース中のコメントにも「notes・evidence・ops_refs にあるステータス風の行が認可ゲートを満たしてはならない」と明記されていた。scope.mdに外部由来のテキスト(対象から採取したログや第三者のメモ)を貼り付けても、それが認可を偽装できないということで、これは実務上かなり効く性質だ。対照群として、同じ値を正しい ## auth 節に書いたケースA・Bはきちんと通ることも確認している。
弱かった点:offline時の「サンプル手掛かり」検査は自己成就する(ケースD)
network_profile.mode が offline のとき、ゲートは「オフラインなのに検体の手掛かりが無い」場合に指摘を出す設計になっている。ところがこの検査は scope.md 全体に対する grep で、sample / offline.?path / \.apk などにマッチすれば通ってしまう。
そして case-init が生成するテンプレートには、署名欄のチェックリストとして
- [x] in_scope.assets non-empty OR offline sample path set
という定型文が常に含まれている。この行に sample が入っているため、実際には検体を1つも指定していなくても検査は通る。ケースDでこの行の文言だけを書き換えたところ、初めて network_profile.mode is offline without offline sample cue が発火した。つまり生成直後の scope.md に対して、この検査は原理的に失敗しない。
ただし影響は限定的だ。offline は最もリスクの低いモード(ネットワーク越しに対象へ触らない)であり、この検査が守ろうとしているのは「オフラインと宣言したのに実は何も用意していない」という運用のずさんさであって、無認可アクセスそのものではない。重大な欠陥ではなく、検査を数えるときに1つ割り引いておくべき項目という位置づけになる。
--force の位置づけ
ケースFの通り、--force を付けると全ての指摘が警告に降格して exit 0 を返す。これは隠れたバイパスではなく、ヘルプにもエラーメッセージにも明示された運用者向けの上書きだ。欠陥として数えるべきではない。
ただし、このゲートが実際に保証している内容を正確に言語化しておくことには意味がある。保証されるのは「scope.md の所定の欄が所定の値で埋まっている、または人間が明示的に --force を打った」ことであって、書面の許諾が実在することではない。エージェントに自動で --auth-granted を渡させたり、--force を常用させたりすれば、ゲートは形だけ残って中身が消える。AIエージェントに自律的な攻撃的操作を任せる際のガバナンス要件を体系的に押さえたいなら、OWASP APTS|AIエージェント時代の自律型ペネトレーションテスト基準を読むが対応する国際的な整理を扱っている。
ツールチェーン自動導入のサプライチェーン対策を検証する
reverse-skillのもう一つの中核機能が「按需自举工具链」——必要になった時点でツールを取ってきて入れる仕組みだ。開発マシンに任意のバイナリを落として実行する以上、ここは記事化の価値がいちばん高い箇所になる。
skills/scripts/bootstrap-manifest.json には24の「能力」が定義され、うち22が canAutoInstall: true になっている。導入方式(bootstrapKind)は10種類——GitHubリリースのZIP、JARラッパー、pip、npm、winget、go install、git clone、Dockerイメージ、ローカルHTTP MCP、手動——に分かれる。
何をもって「ピン留め済み」としているか
verify-routing-coherence.ps1 の「supply-chain pin gate」は、自動導入対象の能力が pinnedVersion / pinnedCommit / pinPolicy のいずれかを持つことを要求し、GitHubリリース系はさらに assetSha256 / preferApiDigest を受け入れる。マニフェストを集計すると内訳はこうなった。
| ピン留めの種類 | 件数 | 意味 |
|---|---|---|
assetSha256(事前登録ハッシュ) |
2 | jadx v1.5.6、apktool v3.0.2。内容が固定される |
preferApiDigest(API申告ダイジェスト) |
4 | radare2×2、ghidra-mcp、bkcrack。ダウンロード時にGitHub APIが返すダイジェストと照合 |
pinnedVersion(バージョン指定) |
7 | frida-tools 14.10.4、pwntools 4.15.0 など |
pinnedCommit(コミット指定) |
4 | SecLists、ProxyCat、ida-pro-mcp など |
pinPolicy: winget-latest |
4 | adb、nmap、binwalk、yara。常に最新を入れる方針の記録 |
つまりゲートが検証しているのは「取得元について何らかの意思決定が記録されていること」であって、「取得物の内容が固定されていること」ではない。この2つは別の線だ。実際、winget-latest は「毎回最新を入れる」という宣言であり、内容は固定されない。それでもゲートは通る。悪い設計とは言い切れない——winget経由のnmapやyaraにハッシュを固定すると、更新のたびにマニフェスト修正が必要になり、運用が破綻する。方針を明示的に記録させるというのは現実的な落としどころだ。ただし利用者側は「ピン留め済み=改ざん検知される」と読み替えないほうがよい。
ハッシュ照合は本物か——jadxを実際に落として確かめた
事前ハッシュが2件しか無いとはいえ、その2件が機能しているかは別問題だ。マニフェストに記載された jadx v1.5.6 のSHA-256と、GitHubリリースの実物を照合した。
# マニフェスト記載のハッシュと、GitHubリリースの実物を突き合わせる
curl -sL -o jadx.zip https://github.com/skylot/jadx/releases/download/v1.5.6/jadx-1.5.6.zip
shasum -a 256 jadx.zip
# actual : 545ea2be9c242511bc145755cf4bda2485ade42966e096f8b4d3da2a230e8974
# manifest: 545ea2be9c242511bc145755cf4bda2485ade42966e096f8b4d3da2a230e8974 ← 完全一致
72,646,741バイトのZIPに対して完全一致した。ハッシュは飾りではなく実在の値だ。
bootstrap-reverse.sh の verify_sha256() 関数の実装も確認した。優先順位は「マニフェストの事前ハッシュ → GitHub APIが返したダイジェスト → どちらも無ければ警告して観測値を記録」で、不一致の場合はファイルを削除して失敗を返す(rm -f "$file" の後に return 1)。ハッシュ計算コマンドの選択も sha256sum が無ければ shasum -a 256 にフォールバックする実装になっており、sha256sum を持たないmacOSでも整合性検査が飛ばされないようになっていた。ここは丁寧に書かれている。
ピンの鮮度
「ピン留めしたまま放置されて何年も古いバージョンを入れ続ける」は、この種の仕組みでよくある劣化パターンだ。主要な固定値を上流と突き合わせた。
| 能力 | マニフェストのピン | 上流の最新(2026-08-16時点) | 状態 |
|---|---|---|---|
frida(実体は frida-tools) |
14.10.4 | 14.10.4(2026-06-25公開) | ✅ 最新 |
| pwntools | 4.15.0 | 4.15.0(2025-10-12公開) | ✅ 最新 |
| jadx | v1.5.6 + SHA-256 | — | ✅ ハッシュ一致を確認 |
frida の欄には注意点がある。マニフェストの能力名は frida だが pipPackage は frida-tools==14.10.4 で、これは frida 本体(PyPIでは17.x系)とは別パッケージだ。能力名だけを見て「fridaが3メジャー遅れている」と誤読しやすいので、実際に入るパッケージ名を確認してから判断したい。実測ではいずれのピンも上流の最新と一致しており、少なくとも現時点でピンは放置されていない。
依存の固定と自動更新のバランスをどう取るかという一般論は、Renovate・Dependabotのサプライチェーン攻撃対策|自動更新を止めずに守る設定で扱っている考え方がそのまま応用できる。
導入前に自分で確認するコマンド
reverse-skillを自分の環境に入れる前に、何がどこから入ってくるのかを自分の目で確認しておくべきだ。マニフェストは単一のJSONなので、数分で棚卸しできる。
# 1. 自動導入される能力と、その取得元・ピン留め状況を一覧化する
python3 - <<'PY'
import json
m = json.load(open('skills/scripts/bootstrap-manifest.json', encoding='utf-8-sig'))
for c in m['capabilities']:
if not c.get('canAutoInstall'):
continue
pin = c.get('assetSha256') and 'sha256' or c.get('pinnedCommit') and 'commit' \
or c.get('pinnedVersion') or c.get('pinPolicy') or c.get('preferApiDigest') and 'api-digest' or 'NONE'
src = c.get('repo') or c.get('pipPackage') or c.get('npmPackage') or c.get('goPackage') or c.get('bootstrapKind')
print(f"{c['name']:18} {str(pin):16} {src}")
PY
# 2. git clone で入る外部リポジトリ(コミット固定の有無)を確認
grep -o '"[^"]*github.com[^"]*"' skills/scripts/bootstrap-manifest.json | sort -u
macOS / Linuxでのreverse-skill実行可否と導入前チェックリスト
READMEは「クライアント中立」を強く打ち出しており、実際にルーティング中核・回帰テスト・マニフェスト・ケースワークフローは特定のAIクライアントに依存していなかった。一方でOSに対する中立性は、そこまで高くない。skills/scripts/ 配下のスクリプトを拡張子で突き合わせた結果がこれだ。
| 提供形態 | 件数 | 該当スクリプト |
|---|---|---|
| PowerShell + bash 両方 | 6 | master-route / case-init / case-guard / bootstrap-reverse / refresh-tool-index / test-routing |
| PowerShellのみ | 10 | verify-routing-coherence / smoke / scan-leaks / extract-summaries / append-evidence / test-p0-friction / test-bootstrap-supply-chain / test-workflow-title-safety / update-star-history / verify-doc-facts |
| bashのみ | 1 | test-bootstrap-manifest |
日常運用の中核(ルーティング → ケース作成 → 権限ゲート → ツール導入 → 回帰テスト)は6本すべてがbashで揃っているので、macOS・Linuxでも問題なく回る。本記事の検証もすべてbash側で実施した。
一方、検証・監査の層はPowerShell専用だ。とくに前節で扱ったサプライチェーンのピン留めゲート(verify-routing-coherence.ps1)自体がPowerShellでしか実行できない。筆者の環境には pwsh が無かったため、このゲートは実行しておらず、スクリプトの判定ロジックとマニフェストの中身を読み合わせて内訳を再構成した。監査を自動で回したいなら、macOS/Linuxでも brew install --cask powershell などで pwsh を用意しておくのが実際的だ。
work/ ディレクトリの既定位置にも差がある。master-route.sh はコード内のコメントで「ルート成果物は呼び出し元のプロジェクトに属する」と明記し、既定でカレントディレクトリ配下に書き出す。一方 case-init.sh は、別ディレクトリから呼んでもクローンしたパッケージ側(<reverse-skill>/work/<case>)にケースを作った。証跡が意図しない場所に溜まるので、ケース作成時は --project-root を明示したほうがよい。
類似のAIエージェント向けセキュリティパックとの比較
同じ「AIエージェントに攻撃的セキュリティ作業をさせる」領域には複数のアプローチがある。役割が異なるので、競合というより補完関係にある。
| reverse-skill | pentest-ai-agents | OWASP APTS | |
|---|---|---|---|
| 形態 | スキル手順書+ルーター+導入スクリプト | Claude Code用サブエージェント集 | 標準・ガバナンス要件 |
| 中核 | 41ルールの決定論的振り分け | 35の専門システムプロンプト | 173要件・3ティア |
| 主眼 | 逆解析(APK/バイナリ/JS/CTF) | ペネトレーションテスト全工程 | 自律実行の安全性・説明責任 |
| 実行制御 | case-guard のscopeゲート(--force可) |
Tier 1助言 / Tier 2実行の分離 | Kill switch・スコープ境界の要件定義 |
| ツール導入 | 24能力を自動導入(ハッシュ検証あり) | 前提ツールは利用者が用意 | 対象外 |
| ライセンス | MIT(CTF部分はGPLv3) | リポジトリ参照 | OWASP |
reverse-skillの独自性はツールチェーン自動導入まで面倒を見る点にあり、それは同時に最大のリスク面でもある。導入判断はこの一点に集約されると言っていい。
導入前チェックリスト
reverse-skillを業務環境に入れる前に確認すること
・用途の合法性を先に固める。逆解析・ペネトレーションテストは、対象と権限が明確な場合にのみ実施する。リポジトリのREADMEも「全ての操作は法的境界の内側で」と明記している
・bootstrap-manifest.json を先に読む。24能力・22自動導入の取得元とピン留め状況を、前節のコマンドで棚卸ししてから入れる
・work/ の出力先を明示する。証跡と検体が意図しないディレクトリに溜まらないよう --project-root を指定する
・--force の運用を決める。エージェントに --force や --auth-granted を自動で打たせないこと。ゲートは打ち手を止めるためにある
・日本語ヒントは半角スペースを入れる。または routing.json に日本語キーワードを追加し、test-routing.sh が緑のままか確認する
・pwsh を用意する。監査系スクリプトはPowerShell専用なので、macOS/Linuxでも入れておくと verify-routing-coherence を自分で回せる
タイムライン(リポジトリと検証の時系列)
| 日付 | 出来事 |
|---|---|
| 2026-05-13 | リポジトリ作成(GitHub API の created_at) |
| 2026-08-08 | v1.0.1 リリース。routing.json の単一ソース化、163ケースの回帰ベンチマーク、サプライチェーンのピン留めゲートを追加(CHANGELOG) |
| 2026-08-15 | 最終push(pushed_at)。ベンチマークは166ケースに増加 |
| 2026-08-16 | 本記事の検証実施。Star 25,491 / Fork 3,450 |
まとめ——決定論の強みと、日本語という死角
reverse-skillは、AIエージェントの逆解析タスクを「毎回その場で考える」から「決まった手順書へ落とす」へ変える試みとして、想像よりずっと真面目に作られていた。ルーティングは単一のJSONに集約され、166ケースの回帰テストとWindows/UbuntuのCIで固定されている。ACT前の権限ゲートは既定で拒否側に倒れ、節をまたいだ偽装も弾いた。ツール導入のSHA-256照合は実物と一致し、不一致ならファイルを削除して失敗する。
その上で、日本語で使う人間にとっての結論はこうだ。この決定論的なルーターは、日本語の入力を想定していない。単語境界に依存したASCIIトークンは日本語の連続表記の中で命中せず、そして166ケースの回帰テストには日本語が1件も含まれていない。CIが緑であることは、この件について何も保証していない。
対処は半角スペース1つで済む。むしろ本記事で持ち帰ってほしいのは、その先の一般論のほうだ。ルールベースの振り分けを多言語環境に持ち込むときは、ルールの本数やテストの件数ではなく、テスト入力の言語分布を見る。そして「ピン留め済み」と書かれた仕組みを評価するときは、それが記録しているのが意思決定なのか、内容の固定なのかを分けて数える。この2つの目を持っていれば、次に流行るスキルパックも同じ手順で自分で測れる。
参照ソース
・zhaoxuya520/reverse-skill — GitHubリポジトリ(README.md / README_AI.md / RULES.md / CHANGELOG.md、2026-08-16取得)
・reverse-skill CHANGELOG v1.0.1(2026-08-08。routing.json単一ソース化・回帰ベンチマーク・サプライチェーンピン留めゲートの追加履歴)
・skylot/jadx リリース v1.5.6(SHA-256照合に使用した実物のアセット)
・Python 公式ドキュメント — re の正規表現シンタックス(\b は \w と \W の境界と定義され、str型パターンでは既定でUnicodeの単語文字が使われる=漢字・かなも単語文字になる根拠)
・frida-tools — PyPI / pwntools — PyPI(ピン留めバージョンの鮮度確認)