検証日: 2026年8月13日 / Claude Code v2.1.220 / Node.js v22.21.1 / Python 3.12.9 / Windows 11
執筆: 国内のAI狂い(あいちゃん)
- 画面に一切映らない不可視文字攻撃「GlassWorm」の恐ろしい仕組み 🖥️⚡
- 手元の129,097ファイルを全スキャン!誤検出72件の面白い内訳とハマりどころ 💥
- 自作ツール「pkgguard」の設計と、処理速度を14分から2.9秒へ約290倍高速化した神改善 ⚡
不可視のUnicode文字を使って画面に映らない悪意あるコードを直接実行させる恐ろしい攻撃「GlassWorm」が広がってるってニュース、見た!?(><)💦
あいちゃんもMCPサーバーをたくさん入れてるし、無名ツールを掘りに行くから他人事じゃない!ってことで、手元を全部検査した上で、インストール前に自動検査するツール「pkgguard」を自作しちゃいました!
その開発過程で踏んだ失敗や学びがめちゃくちゃ面白かったから、全部シェアしちゃうね!🚀
GlassWorm ってなにがそんなにヤバいの?😱
日経クロステックでも話題になっていた不可視文字マルウェア混入攻撃「GlassWorm」。
この攻撃の何が一番厄介かって…「画面上で完全に見えない」 ことなんだよ!!
「エディタで見ても、GitHubのdiff画面で見ても、コードは普通にきれいなまま。でも実行すると悪意あるコードが裏で動く!」
つまり「気をつけてコードを読めば気づける」というレベルの代物じゃないの!
それなら自分で防御陣形を組むしかない!ってことで、まずは現状把握からスタートしたよ!✨
まず手元は汚染されているのか全スキャン!🔍
接続中のMCPサーバーと、npxキャッシュに残っている依存ツリーを全部スキャンしてみたよ!
結果:129,097ファイルを検査して、実際の脅威はゼロだったよ!🎉
| 対象 | ファイル数 | 検出 | 「危険」判定 | 実際の脅威 |
|---|---|---|---|---|
| グローバルnpm | 21,105 | 62 | 12 | 0 |
| npxキャッシュ他 | 107,992 | 268 | 60 | 0 |
| 合計 | 129,097 | 330 | 72 | 0 |
一安心…と思いきや!問題は「危険」判定が72件も出ちゃったことなの!(;’∀’)
全部手動で中身を精査したら、100%全件誤検出だったよ!その内訳がめちゃくちゃ面白かったの!笑
- zodの絵文字テスト(46件)
国旗絵文字(🏴 など)は、Unicode仕様上タグ文字という不可視文字の連続で構成されてるの。バリデーションのテストデータに全絵文字が並んでいて誤検知! - figletのフォント定義(12件)
0x202A のように、全コードポイントのアスキーアート字形を定義したファイル。制御文字も1文字として収録しているだけだったよ! - effect(Scalar APIドキュメント)
HTMLエンティティ変換テーブルと、「不可視文字を検出して無害化するための正規表現」!不可視文字対策コードをスキャナが引っかけるという間抜けな絵面になっちゃった!笑
素朴に「不可視文字があったら即警告!」というナイーブな実装をしちゃうと、実運用では誤爆祭りになってまったく使い物にならないことがここで確定したよ!
本当に恐ろしい穴は別の場所にあった!💣
スキャン結果自体は白だったんだけど、調査を進めていたらもっと根本的で恐ろしい構造的リスクを発見しちゃったの…!
google-analytics : uvx --from analytics-mcp
context7 : npx -y @upstash/context7-mcp
agentmemory : npx -y @agentmemory/mcp
バージョンが固定されていないの!!😱npx -y や uvx は起動のたびに最新版を取得して即実行する仕様なの。つまり「今日スキャンして安全だったからOK」と思っても、明日は保証されないの!
実際、npxのキャッシュを調べたら context7 が…
- 5月21日時点:
2.3.0 - 8月12日(前日):
4.0.2
なんと確認なしでメジャーバージョンが2つも勝手に上がっていたの!
すぐに設定ファイルでバージョンを固定したよ!
// ~/.claude.json
"context7": {
"command": "npx",
"args": ["-y", "@upstash/context7-mcp@4.0.2"] // ← バージョンをしっかり固定!
}
ついでに読み取り経路そのものを検証&バグ発見!🕵️♀️
あいちゃんは token-pilot というツール経由でファイルを読んでいるんだけど、「もしツール自体が汚染されていたら、偽のクリーンなコードを見せかけられるのでは?」という懸念が浮かんだの。
そこでnpmレジストリから公式版を新規ダウンロードして、ローカルの実体と1ファイルずつSHA-256で突き合わせを行いました!
tokenjuice@0.7.0 : 413/413 完全一致
token-pilot@0.43.1 : 269中268一致、1ファイルだけ相違 ← ??
1件だけ出た相違ファイル(dist/core/validation.js)を調べたら、ホームディレクトリ全体をスキャンさせない安全装置をWindows向けに直したパッチ痕跡だったよ。
しかし!最新版(0.50.2)でも未修正の安全装置のすり抜けバグがあったので、再現コードを添えてGitHub Issue #65を報告したよ!8ケース中4ケースで安全装置がすり抜けちゃうの!
// Windows で isDangerousRoot() がすり抜ける例
isDangerousRoot("C:\\Users") // true ← 正しい
isDangerousRoot("C:/Users") // false ← すり抜け!
isDangerousRoot("C:/Users/name") // false ← すり抜け!(ホームそのもの)
isDangerousRoot("C:\\Users\\") // false ← すり抜け!
isDangerousRoot("C:/") // false ← すり抜け!
自作セキュリティツール「pkgguard」誕生!🛡️
設計のキモは「インストールの順番」の分解!
npx -y foo や uvx foo の問題は、取得と実行が1ステップになっていて間に検査を挟む隙間がないことなんだよね。
だから「検査してからインストール」ではなく、インストールの工程自体をバラバラに分解したよ!

具体的には npm pack でtarball(圧縮ファイル)だけダウンロード(この時点ではスクリプトは走らない)➔ 展開してスキャン ➔ 合格したら npm i -g --ignore-scripts で安全にインストール!Python側も pip download(wheelのみ)を使う設計にしたよ!
# 検査だけ(インストールはしない)
python "%USERPROFILE%\.claude\pkgguard\pkgguard.py" check npm <パッケージ名>
# 検査に通ったらインストールまで実行
python "%USERPROFILE%\.claude\pkgguard\pkgguard.py" install npm <パッケージ名>
# ディスク上のディレクトリを直接検査(git clone した場合など)
python "%USERPROFILE%\.claude\pkgguard\pkgguard.py" scan <ディレクトリ>
判定ロジック:「連続長」で見るのが正解!
誤検出72件の反省から、判定基準を根本変更したよ!
悪意あるコードを不可視文字の中に隠す場合、絶対に長い連続文字になるの!
攻撃サンプルを検証したところ39文字の連続になったのに対して、正常なドキュメントのゼロ幅スペースは1〜2個程度。
だから「個数」ではなく「連続長」で判定するのが大正解だったよ!✨
eval は単独ではブロックしない!
eval や child_process は正規のライブラリにも大量に入ってるから、単体で止めると全滅しちゃうの。
「不可視文字と同じファイルに同居したときだけ」最優先でブロックする設計にしたよ!
Claude Code の PreToolUse フックで自動ブロック!
手動実行は絶対忘れちゃうから、Claude Code の PreToolUse フックに組み込んで、未検査の npm i -g / npx / uvx / pip install / cargo install を自動検出してインターセプト&ブロックするようにしたよ!✨

意図的に通したいときは、コマンドの末尾にマーカーをつければOK!
npx -y some-tool # PKGGUARD_OK=1
ハマり地雷:スキャンに14分かかった件 😱⌛
判定ロジックを書き直した後、3,220ファイルのスキャンを実行したら…14分経っても終わらないの!!💦(本来なら50秒で終わるはずなのに!)
原因は、文字を1つずつPythonレベルのループで舐める愚直な書き方をしていたからなの…!
# ✗ これで3,220ファイルに14分かかった愚直コード
i = 0
while i < n:
if ord(text[i]) < 0x80 or not classify(ord(text[i])):
i += 1
continue
...
数MBあるミニファイ済みJavaScriptがあると、インデックスアクセスのオーバーヘッドが爆発しちゃうんだよね。
そこでC言語層で高速処理される正規表現一括抽出(re.compile)に修正したよ!
# ✓ 同じ3,220ファイルが2.9秒で完了する高速コード!
SUSPICIOUS = re.compile("[\u00ad\u200b-\u200f\u202a-\u202e\u2060-\u2064"
"\u2066-\u2069\ufe00-\ufe0f\U000e0000-\U000e01ef]+")
for m in SUSPICIOUS.finditer(text):
...
結果:14分 ➔ 2.9秒!!なんと約290倍の超爆速化!🚀✨
最終的な性能と精度まとめ!
| 項目 | 結果 |
|---|---|
| 誤検出 | 8パッケージ・20,806ファイルで0件(修正前は誤爆祭り) |
| 検出力 | 攻撃サンプル(不可視文字39連+eval)を確実に検出! |
| 実運用の所要時間 | 3〜7秒(大半は通信ダウンロード時間) |
実測でも tokenjuice@0.7.0 (412ファイル) が 7.0秒、@upstash/context7-mcp@4.0.2 (11ファイル) が 3.2秒!検査自体は1秒前後で完了するよ!
実戦投入&設計の正しさが証明された瞬間!✨
実際にGitHubで見つけた新しいMCPツール scientific-illustrator(PowerPointやdraw.ioの中にAIが図を描くツール)の導入で pkgguard を実戦投入したよ!
npmパッケージではないので、実行せずに git clone ➔ scan の手順で実行!
pkgguard scan → 40ファイル / 0.9秒 / 不可視文字ゼロ → 合格!
手動でコードを確認したら eval 系を含むファイルが12件あったんだけど、全件が描画用の正当な処理だったの!
- 外部通信先:github.com のみ(テレメトリも不明なサーバーも無し)
child_process:PowerPoint・draw.io・pythonの起動用(ツールの性質上当然)new Function:1箇所だけ(構文チェック用途で外部入力は通していない)
もし「evalがあったら一律ブロック」にしていたら、素晴らしいツールを誤って弾くところだったよ!「不可視文字と同居したときだけ止める」という設計判断の正しさが実証された瞬間でした!🎉
なお、このツールのREADMEには「この文章をそのままAIエージェントに貼り付けろ」というインストラクション形式が書かれていたんだけど、構造として外部文章がAIに直接命令する形になるので、そのまま実行せず中身を分解して手動設定したよ!
限界(過信は禁物だよ!)⚠️
ただし、絶対に過信しちゃダメだよ!
- pkgguardで防げるのは「隠されたコード(不可視文字)」だけ! 平文で堂々と書かれた悪意あるコードは素通りしちゃいます。
- フックが効くのは Claudeが打つコマンドだけ。自分がターミナルで直接叩く分は素通りします。
- MCPサーバーはアプリ自身が直接起動するので、起動時のnpx等にはフックが介在できません。
- 検査対象はパッケージ本体だけ!
npm packが持ってくるのは本体のみで、依存パッケージ(node_modules)は含まれないの。実際token-pilotは本体269ファイルに対して、インストール後は依存込みで 3,831ファイル——なんと14倍 もあったよ!サプライチェーン攻撃は奥の依存に仕込まれることが多いから、installの最後に依存ツリーごと再スキャンする処理を後から追加したよ!(--ignore-scriptsのおかげで、その時点ではまだ一行も実行されていないのがミソだよ!)
だからこそ、「MCPサーバーのバージョン固定」が第一の主防御で、フックは「新しいツールを入れる瞬間を守る別枠」という整理になるよ!両方の対策を組み合わせるのが最強スタイルだよ!✨
まとめ 📝
- 手元129,097ファイルを調べた結果、汚染はゼロ!ただしバージョン未固定という構造的な穴の方が問題だった!
- 不可視文字の検出は「個数」ではなく「連続長」で見る!コードを隠すには長い連続が必要だよ。
eval単独でブロックすると正規ライブラリが全滅する!不可視文字と同居したときだけ止めるのが正解!- 文字を1つずつPythonループで舐めると約290倍遅くなるから正規表現一括処理にすべし!
今回の検証環境
| OS | Windows 11 Home (Build 26200) |
| Claude Code | 2.1.220(デスクトップアプリ 2.1.227) |
| Node.js / npm | v22.21.1 / npm 11.6.2 |
| Python | 3.12.9 |
| 検証日 | 2026年8月13日 |
※ 検査したパッケージ:token-pilot@0.43.1(最新は0.50.2)、tokenjuice@0.7.0、@upstash/context7-mcp@4.0.2、@agentmemory/mcp@0.9.28、analytics-mcp==0.7.0、scientific-illustrator@v1.5.4
ツールの更新が非常に速い分野なので、この記事は2026年8月13日時点の情報として参考にしてみてね!(*´ω`*)✨






