手元のリポジトリを開いて、最初に数えたのは依存関係でした
Project Glasswing の初期アップデートを読み終えたあと、私が最初にしたのは自分のリポジトリを開くことでした。個人開発で運用しているサイトは、いずれも Next.js と Cloudflare Workers の上に乗っています。攻撃者がいきなり私のコードを狙うとは思っていません。けれども、そのコードが依存しているライブラリは、世界中の誰かが同じように依存しているものです。
src/ 配下だけで 68 ファイル、概算 14 万トークン。自分が書いた範囲は一日で読み返せます。読み返せないのは、その下にある依存関係の層です。
この記事は、その落ち着かなさを起点に、Claude Mythos Preview と Project Glasswing について公開情報で確認できることだけを整理したものです。数字はすべて Anthropic の公式発表と、そこからリンクされている一次資料に当たって取っています。私自身の推測は推測として分けて書きました。
なお本文の内容は 2026 年 8 月時点で公開されている情報に基づいて見直しています。この分野は数週間で状況が変わるため、実装判断の前には必ず一次情報をご確認ください。
Claude Mythos Preview が何をするモデルなのか
Anthropic が Claude Mythos Preview を公表したのは 2026 年 4 月です。汎用のフロンティアモデルでありながら、ソフトウェアの脆弱性を発見し悪用する能力において「最も熟練した人間を除くすべてを上回る」水準に達した、というのが同社の説明です。
重要なのは、これが専用のセキュリティツールではないという点です。ファジングでも、CVE データベースとのパターン照合でもありません。コードの意図を理解した上で「この前提が崩れたら何が起きるか」を仮説として立て、実際に動く実証コードまで組み上げます。そこまでを人間の介在なしに進めた例が公開されています。
報道で確認できる具体例としては、OpenBSD の TCP SACK 実装に 27 年間残っていたサービス拒否の欠陥、FFmpeg の H.264 コーデックに 16 年間見過ごされていた欠陥、そして FreeBSD の NFS サーバーにおける 17 年もののリモートコード実行(CVE-2026-4747)があります。最後のものは、最初のプロンプト以降は人間が一切関与せずに、認証なしの root 権限取得まで到達したと報じられています。
「新しいコードにバグがある」のではありません。何十年も多くの目に晒され、ファザーにもかけられてきたコードから出てきています。ここが従来のツールと質的に違う部分です。
Anthropic はこのモデルを一般公開していません。理由も明確に述べられています。サイバー能力の悪用を防ぐ十分に強固な安全策が、同社にも、同社の知る限り他のどの開発元にも、まだ存在しないためです。
公開されている数字を並べてみる
推測を混ぜずに、確認できる数字だけを並べます。出典はいずれも Anthropic が 2026 年 5 月 22 日に公開した Project Glasswing の初期アップデートです。
私がいちばん注目したのは、この次の数字です。オープンソース側で検出された高・重大 6,202 件のうち 1,752 件が、6 社の独立したセキュリティ研究会社(一部は Anthropic 自身)によって精査されました。その結果が次のとおりです。
真陽性率 90.6% という数字は、自動検出ツールの実務感覚からするとかなり異質です。静的解析ツールを本気で運用したことがある方なら、ノイズの海からひとつの本物を拾い上げる作業に何時間を溶かしたか、思い当たるところがあると思います。Cloudflare も、偽陽性率について「人間のテスターより良い」と評価しています。
この率のまま走査を続けた場合、オープンソース領域だけで高・重大の脆弱性が 3,900 件近く表に出る計算になる、と Anthropic は書いています。パートナー企業側の 1 万件超とは別枠での話です。
具体例もひとつ公開されています。数十億台の機器で使われている暗号ライブラリ wolfSSL に対して、Mythos Preview は証明書を偽造できる実証コードを構成しました。銀行やメールプロバイダを騙る偽サイトが、利用者から見て正規のものと区別できなくなる種類の欠陥です(CVE-2026-5194、修正済み)。
Project Glasswing は 50 社から約 200 社へ広がりました
Project Glasswing は 2026 年 4 月、約 50 社の初期パートナーで始まりました。発足時の顔ぶれは、Amazon Web Services、Anthropic、Apple、Broadcom、Cisco、CrowdStrike、Google、JPMorganChase、Linux Foundation、Microsoft、NVIDIA、Palo Alto Networks です。
そして 6 月 2 日、Anthropic は約 150 組織を新たに加えると発表しました。合計で約 200 組織、15 か国以上に広がっています。新しい層として明示されているのは、電力・水道・医療・通信・ハードウェアです。初期コホートにあまり含まれていなかった領域を意図的に埋めにいった形です。
Anthropic はこの拡大の基準を、ひとつの文で説明しています。パートナーの多くについて、そのコードベースへの攻撃が成功した場合、1 億人以上に影響が及ぶと見積もっている、と。加入には同社のセキュリティ要件を満たすことが条件になっています。
同じ発表の中で、私が実務者として最も重く受け取ったのは次の見通しです。今後 6 〜 12 か月のうちに、他の多くの AI 企業が Mythos 級のモデルを持つと予想され、そこには悪用を防ぐ安全策が伴わない可能性がある——。
Glasswing は「Anthropic が強力なモデルを持ってしまった」ことへの対処ではなく、「誰もが持つ状態が来る」ことへの前倒しの準備として設計されています。この読み方をすると、参加していない側の開発者が何をすべきかも見えてきます。
ボトルネックは「見つける」から「直す」へ移りました
Anthropic の初期アップデートには、この一年のセキュリティ業界を要約する一文があります。ソフトウェアセキュリティの進歩は、これまで「新しい脆弱性をどれだけ速く見つけられるか」で制限されていました。いまは「見つかった大量の脆弱性を、どれだけ速く検証し、開示し、修正できるか」で制限されています——。
修正側の実データも公開されています。
530 件開示して、パッチが当たったのが 75 件。高・重大のバグ 1 件あたり、平均で 2 週間かかっているという記述もあります。Anthropic 自身が、この低さの理由を 3 つに分けて説明しています。90 日の開示ウィンドウの途中であること、公開アドバイザリを伴わない修正を数え漏らしていること、そして 3 つ目——比較的ゆっくりした開示ペースであってもなお、すでに過負荷のセキュリティ・エコシステムに負荷を足してしまっていること。
ここは読んでいて胸が痛くなる部分でした。オープンソースのメンテナーの一部は、低品質な AI 生成バグ報告の洪水にすでに晒されています。その上で Anthropic に対して「パッチを設計する時間が要るので、開示のペースを落としてほしい」と要請したメンテナーがいる、と明記されています。
発見が 10 倍速くなっても、直す人の数は 10 倍になりません。この非対称性が、いま起きていることの本体です。
一方で、修正側が動いていないわけではありません。Palo Alto Networks の直近リリースには通常の 5 倍のパッチが含まれ、Microsoft は今後しばらく毎月のパッチ数が増え続けると予告し、Oracle は検出と対応が従来の数倍速くなったと報告しています。数の増加そのものが、いまは健全さの指標になっています。
招待を持たない私たちが、公開モデルでできること
Mythos Preview には申請すれば誰でも触れる、という状態ではありません。では個人開発者や小さなチームは待つしかないのか。ここは Anthropic の発表を読む限り、明確に「そうではない」と書かれています。
一般公開されているモデルでも、大量のソフトウェア脆弱性はすでに見つけられます。最も高度なものを見つけたり、Mythos Preview ほど効果的に悪用できたりはしませんが、ほとんどの現場が抱えている「基本的な穴」は十分に射程内です。実際、Claude Security の公開ベータでは、リリースから 3 週間で 2,100 件超の脆弱性が修正されています。
使える手段として公開されているものを整理します。
このうち「ハーネス」の設計思想は、公開モデルでも自前で再現できます。コードベースを地図化し、走査するサブエージェントを立ち上げ、その結果をトリアージして報告書にまとめます。この 4 段構えです。
そして、ここで最初に詰まるのが「地図化」の部分でした。私が手を動かして分かったのはそこです。
実際に動かしてみた——走査の前段でどこが詰まるか
エージェントに走査させる前に、必ず必要になる処理があります。リポジトリのファイルを集め、1 リクエストに収まる単位へ詰め直すことです。地味ですが、ここの設計でコストと取りこぼしが決まります。
API を呼ばずに、この前段だけを切り出したものが次のスクリプトです。Python 3.10.12 で動作を確認しています。
"""リポジトリを走査し、モデルに渡す「解析単位」へ詰め直す前段処理。
API は呼ばない。ここで確定させたいのは、何ファイルを何回のリクエストに分けるかだけ。"""
from __future__ import annotations
import os
from dataclasses import dataclass, field
from pathlib import Path
SKIP_DIRS = { ".git" , "node_modules" , "__pycache__" , ".next" , "dist" , "build" , ".venv" , "vendor" }
CODE_EXT = ( ".py" , ".ts" , ".tsx" , ".js" , ".jsx" , ".go" , ".rs" , ".c" , ".h" , ".java" )
MAX_FILE_BYTES = 200_000 # 生成物・ミニファイ済みファイルを弾く
BUDGET_TOKENS = 150_000 # 1リクエストあたりの入力予算
@dataclass
class SourceFile :
path: Path
text: str
tokens: int
@dataclass
class Batch :
files: list[SourceFile] = field( default_factory = list )
tokens: int = 0
def add (self, f: SourceFile) -> None :
self .files.append(f)
self .tokens += f.tokens
def estimate_tokens (text: str ) -> int :
"""予算取り専用の概算。厳密な計測ではなく、必ず多めに出る側へ倒す。"""
return max ( 1 , len (text) // 3 )
def collect (repo: Path) -> list[SourceFile]:
out: list[SourceFile] = []
for root, dirs, files in os.walk(repo):
dirs[:] = [d for d in dirs if d not in SKIP_DIRS ]
for name in files:
if not name.endswith( CODE_EXT ):
continue
p = Path(root) / name
try :
if p.stat().st_size > MAX_FILE_BYTES :
continue
text = p.read_text( encoding = "utf-8" , errors = "replace" )
except OSError :
continue
out.append(SourceFile(p, text, estimate_tokens(text)))
return out
def pack (files: list[SourceFile], budget: int = BUDGET_TOKENS ) -> list[Batch]:
batches: list[Batch] = []
oversized: list[SourceFile] = []
for f in files:
if f.tokens > budget:
# 単独で予算を超えるファイルは、詰め方の問題ではない。別処理へ回す
oversized.append(f)
continue
for b in batches:
if b.tokens + f.tokens <= budget:
b.add(f)
break
else :
nb = Batch()
nb.add(f)
batches.append(nb)
if oversized:
print ( f "[warn] 予算超過のため関数単位の分割が必要: { len (oversized) } ファイル" )
return batches
estimate_tokens を len(text) // 3 にしているのには理由があります。実測したところ、tiktoken の cl100k_base で Python ソース 300 ファイル(594 万文字)をエンコードすると 160 万トークン、つまり 1 トークンあたり 3.71 文字 でした。// 3 の概算はこれに対して 1.24 倍、常に多めに出ます。予算取りでは過小評価が事故になるので、この向きの誤差は歓迎すべきものです。
もうひとつ、実装より前の話を挟みます。CODE_EXT の一覧は、借りてきたまま使わずに自分のリポジトリで数えてから決めていただければと思います。Lab のサイトのひとつを数えたところ、.tsx と .ts で 70 ファイルを拾える一方、ビルド用に置いていた .mjs の 4 ファイルがこの一覧から漏れていました。走査から漏れたファイルは、走査結果のどこにも「漏れました」とは出てきません。出るのは、何も見つからなかったという静かな行だけです。
詰め方を工夫しても、ほとんど効きませんでした
最初、私はファイルをトークン数の降順に並べてから詰める First-Fit Decreasing を実装していました。ビンパッキングの定石ですし、断片化が減るはずだと思っていたためです。
実測した結果がこれです。Python 3.10 の標準ライブラリ(650 ファイル・約 337 万トークン)を対象に、走査順そのままの First-Fit と、降順ソート後の First-Fit Decreasing を比較しました。
最も差が出た予算 6,000 でも、339 バッチが 336 バッチになるだけでした。0.9% です。ソート処理を足す価値はありません。自分の実装からは外しました。
書いたコードを実測して消す判断は気持ちのいいものではありませんが、ここで消しておかないと、この先ずっと「意味のある最適化」として引き継がれてしまいます。
本当のボトルネックは、単独で予算を超えるファイルでした
同じ計測から、もっと重要なことが出てきました。予算を小さくしていくと、1 ファイルで予算を超えるファイルの占有率が跳ね上がります。
対象コーパスの 1 ファイルあたりトークン数は、中央値 2,866・平均 5,186・最大 47,920 でした。中央値と平均がここまで離れているということは、少数の巨大ファイルにコードが集中しているということです。
そして脆弱性は、たいてい大きいファイルの中にあります。長く育ったパーサ、状態を持つプロトコル実装、後方互換のために分岐が積み上がったハンドラ。バッチの詰め方を磨いても、この 70% には届きません。ここは関数単位・クラス単位の分割と、呼び出し元の文脈をどう添えるかという別の問題です。
70% に手を届かせる——ファイルではなく定義で切りました
「別の問題です」と書いたまま放置していたのが気になって、後日ここだけを実装しました。やることは単純で、予算を超えたファイルをトップレベルの境界で切り、import 群だけを各断片の先頭に付け直します。
import ast
def _start (node) -> int :
# デコレータがある定義は、デコレータの行から始める
return min ([node.lineno] + [d.lineno for d in getattr (node, "decorator_list" , [])]) - 1
def split_python (sf: SourceFile, budget: int ) -> list[SourceFile]:
# トップレベルの境界だけで切る。どの行も、必ずどれかの断片に入る
try :
tree = ast.parse(sf.text)
except SyntaxError :
return [sf]
if len (tree.body) < 2 :
return [sf]
lines = sf.text.splitlines( keepends = True )
starts = [_start(n) for n in tree.body]
starts[ 0 ] = 0 # 先頭のコメント・docstring は 1 つ目に含める
ends = starts[ 1 :] + [ len (lines)] # 次の定義の直前まで。あいだのコメントも落とさない
header, units = [], []
for node, s, e in zip (tree.body, starts, ends):
seg = "" .join(lines[s:e])
(header if isinstance (node, (ast.Import, ast.ImportFrom)) else units).append(seg)
if len (units) < 2 :
return [sf]
head = "" .join(header)
head_tokens = estimate_tokens(head)
chunks, cur, cur_tokens = [], [], 0
for seg in units:
t = estimate_tokens(seg)
if cur and head_tokens + cur_tokens + t > budget:
chunks.append( "" .join(cur))
cur, cur_tokens = [], 0
cur.append(seg)
cur_tokens += t
if cur:
chunks.append( "" .join(cur))
return [
SourceFile(Path( f " { sf.path } #part { i + 1 } " ), head + c, estimate_tokens(head + c))
for i, c in enumerate (chunks)
]
同じコーパス(Python 3.10.12 の標準ライブラリ 650 ファイル)で、分割の前後を測りました。
予算 6,000 で、届かない量が 70.6% から 18.0% になりました。前の節でソート順を工夫して得られたのが 0.9% だったことを思うと、手を入れる場所がここだったのは明らかです。
バッチ数が 169 から 499 へ増えている点は、誤解のないように書いておきます。これは仕事が 3 倍になったのではありません。それまでの 169 バッチで「見ていた」対象には、コードの 3 割ほどしか入っていませんでした。増えた分は、これまで請求もされない代わりに、読まれてもいなかった領域です。
切るときは、残すものではなく捨てているものを数えます
この実装は一度失敗しています。最初は ast.FunctionDef と ast.ClassDef だけを拾う書き方にしていました。定義を集めるのだから定義を拾えばよい、と考えたのです。
断片を突き合わせて、ようやく気づきました。デコレータは node.lineno より上の行にありますから、この書き方では消えます。モジュール直下の定数表も、定義ではないので拾われません。170 ファイルを検査したところ、121 ファイルで本文の一部が欠けていました。走査の前段で本文が欠けるということは、見逃しがそのまま「異常なし」という形で返ってくるということです。
直し方は、境界を「定義の範囲」ではなく「次の定義の直前まで」に変えるだけでした。上のコードの ends がそれにあたります。同じ検査をやり直して、行が失われたファイルは 170 分の 0 になりました。
切り方を足したら、捨てていないことを数える検査も一緒に足します。 分割は黙って失敗する種類の処理ですので、この一手間だけは省かないようにしています。
なお 18.0% は残ります。残った 65 断片の中身は、base_events.py の BaseEventLoop、subprocess.py の Popen、entities.py の実体参照表のように、単一の定義そのものが予算を超えているもの でした。ここから先はクラス内のメソッド単位へ降りるか、その部分だけ予算を上げて別に投げるかの二択になります。私は後者にしています。数が少ないので、丁寧に扱うほうが結果的に安く済みました。
Anthropic のハーネスに「脅威モデルビルダー」が含まれている理由が、実装してみて腑に落ちました。全部を均等に見るのではなく、攻撃対象になりうる箇所を先に決めて、そこへ予算を寄せます。順序が逆だったのは私のほうでした。
自分の運用で見直した 3 つのこと
大きな話に引きずられて何もしないのが一番まずいので、その週のうちに手を付けたことを 3 つだけ書きます。派手さはありません。
1 つ目は、依存更新の承認待ちを廃止したことです。 それまで自動更新の適用には自分の確認を挟んでいました。確認とは名ばかりで、実際には差分をろくに読まずに承認していた回のほうが多かったと思います。パッチが 5 倍の量で降ってくる世界では、この工程は安全装置ではなく単なる遅延です。テストが通ることを条件に、パッチ・マイナーは自動で入れる設定へ変えました。
2 つ目は、ログの保存期間を延ばしたことです。 侵害の検知は、パッチが間に合わなかったときの最後の砦です。英国 NCSC の 10 Steps や NIST のサイバーセキュリティフレームワークが挙げている基本の統制——初期設定の堅牢化、多要素認証の強制、検知と対応のための包括的なログ——は、どれも「特定のパッチが間に合うこと」に依存しないという共通点があります。個人の運用でも、ここだけは効きます。
3 つ目は、公開しているコード例そのものを走査対象に含めたことです。 これは自分の仕事の性質上の話です。技術記事に載せたコードは、読んだ方の本番環境へそのまま入ることがあります。記事のコードは資料ではなく出荷物だと考え直しました。実際、過去記事のコード例をあらためて動かしたところ、そのままでは動かないものが見つかりました。恥ずかしい話ですが、直せてよかったと思っています。私はこの 3 つを、四半期ごとに見直す項目として書き留めました。一度やって終わりにすると、次に思い出すのは事故のあとになります。
書かなかったこと、確認できなかったこと
この記事を書き直すにあたって、意図的に落とした情報があります。透明性のために残しておきます。
脆弱性の CWE 分類別の内訳や、OS・ブラウザ別の発見件数は書いていません。Anthropic は、これを書ける段階にないと明言しています。同社の協調的脆弱性開示ポリシーは業界慣行に沿って 90 日(パッチ公開済みなら約 45 日)を置く方針で、その途中で詳細を出せば利用者を危険に晒すためです。つまり現時点で流通している「内訳」の数字には、一次情報の裏付けがありません。
Mythos Preview の料金体系、モデル ID、Bedrock や Vertex AI 経由の呼び出し方も書いていません。一般提供されていないモデルについて、公式に確認できる形の情報がないためです。
そして、モデルの内部的な振る舞いに関する類の話も扱っていません。一次情報で確認できない挙動をセンセーショナルに扱うことは、この分野では特に害が大きいと考えています。
書けることが減ると記事は薄くなります。それでも、確認できない数字を並べるより、確認できる数字と自分で測った値だけで組み立てたほうが、読んでくださる方の判断の役に立つはずです。
次に手を動かすなら
長い記事になりましたので、明日からの 1 手だけ書いて終わります。
自分のリポジトリで、1 ファイルあたりのトークン数の中央値と最大値を出してみてください。 本記事のスクリプトの collect と estimate_tokens だけで足ります。中央値と最大値が大きく離れていたら、そのリポジトリの脆弱性は「最大値側のファイル」に偏っています。走査を始めるなら、そこからです。
上のスクリプトの末尾に、これだけ足せば動きます。
if __name__ == "__main__" :
import statistics
import sys
files = collect(Path(sys.argv[ 1 ]))
if not files:
print ( "対象ファイルが見つかりませんでした。CODE_EXT を確認してください。" )
raise SystemExit ( 1 )
tok = sorted (f.tokens for f in files)
biggest = max (files, key =lambda f: f.tokens)
print ( f "files : { len (files) } " )
print ( f "median : { statistics.median(tok) :,.0f } tokens" )
print ( f "mean : { statistics.mean(tok) :,.0f } tokens" )
print ( f "max : { biggest.tokens :, } tokens ( { biggest.path } )" )
print ( f "batches : { len (pack(files)) } (budget { BUDGET_TOKENS :, } )" )
中央値の数倍以上の最大値が出たなら、そのファイル名が最初の走査対象です。私の環境では、この 1 行が優先順位の議論を終わらせてくれました。
Anthropic が繰り返し書いているとおり、Mythos 級の能力は 6 〜 12 か月のうちに広く行き渡ります。そのとき効くのは、特定のパッチが間に合ったかどうかではなく、それまでに整えておいた工程のほうです。
私自身もまだ手探りで、上に書いた 3 つも十分とは思っていません。同じことを考えている方と、少しずつ知見を持ち寄れたら嬉しく思います。最後までお読みいただきありがとうございました。