Lilith.
⌕
編集イラスト: webframeはWebページを0.5秒でプログラムのように動かす。サンドボックスになるのは外側を隔離したときだけ
Lilithのイラスト · 編集リミックス

Simon Willisonは、Webページをコマンドラインのプログラムのように動かすJeff Lindsayのツールwebframeの評価レポートを公開した。レポートはWillisonのプロンプトをもとにClaude Opus 5.5が書いたもので、結論は二つに分かれる。スクレイピングには十分使えるが、信頼できないコードを動かすなら外側からの強固な隔離が必要だ。

5.3MBのプログラムが、システムに入っているブラウザでページを動かす

webframeにURLかファイルを渡すと、隠れたwebviewでページを読み込む。console.logの出力は標準出力へ、例外は標準エラーへ送られる。-cオプションを付けると、標準入力の各行をページ上で評価する。Go製で、システムのブラウザを使うglazeライブラリの上に作られている。macOSではWKWebView、LinuxではWebKitGTK、WindowsではWebView2を使い、独自のエンジンは同梱しない。レポートによれば、CLI全体はGoとJavaScriptで約460行、ビルドすると5.3MBのバイナリになる。

Willisonがモデルに与えたプロンプトは一つだけだ。ツールを試し、プロトタイプを作り、サンドボックスとしても、shot-scraper javascriptコマンドの代わりとしても評価せよという内容である。リポジトリには、文章もコードも言語モデルが書いたと明記されている。テスト環境はUbuntu 24.04、WebKitGTK 2.52、Xvfbで、起動には約0.5秒かかる。作られたプロトタイプは三つ。webframe向けのパッチ、約350行のGoで書いたshot-scraperのクローンwfjs、そしてモデルが20個の悪意あるコード片で攻撃したPythonのrun_js関数だ。

スクレイピングではPlaywrightに並び、エージェントには軽いJavaScriptエンジンを渡す

スクレイピングの比較は互角だった。example.comのタイトル取得にかかった時間は、wfjsで1.08から1.17秒、Playwright上のshot-scraperで1.19から1.40秒。メモリはWebKitとXvfbを含めたwfjsが約640MB、ChromeとPythonが約456MBだった。shot-scraperのドキュメントにある例のうち、専用オプションに頼らないものはすべてそのまま動いた。

エージェントを作る側にとって興味深いのは後半だ。生成したり、ダウンロードしたりしたJavaScriptを動かしたいエージェントは、webframeでWASM、crypto.subtle、DOMを備えた本物のブラウザエンジンを使える。しかもfsやprocessといったオブジェクトはない。対してPlaywrightは独自のブラウザをダウンロードする。進め方そのものも注目に値する。Willisonは新しい依存ツールの評価をエージェントに任せ、パッチ、プロトタイプ、攻撃一式を受け取った。こうしたレポートは、チームがツールを信頼する前に通す安価な最初のふるいになる。

初期状態のページはネットワーク、localhost、無制限のメモリを使える

手を加えないwebframeは、それだけではサンドボックスにならない。ページはlocalhostを含むネットワークに自由にアクセスでき、ストレージは実行をまたいで残り、WebKitのレンダラー用サンドボックスは無効になっている。時間やメモリの上限もない。さらにページは内部のフックを書き換え、評価のたびに偽の結果を返せる。スクレイピングでも三つの問題がある。GitHubを含め、CSPでevalを禁じているページはすべての評価を拒否する。ビューポートは0×0のままで、読み込みに失敗すると永遠に止まることがある。

bubblewrapを使うパッチでも穴はふさがらない。WebKitのネットワークプロセスはその外側で動くため、乗っ取られたレンダラーは防げても、外へ出ようとするページは防げない。実際、ある攻撃ではlocation.hrefによるリダイレクトが、厳しいCSPがあってもlocalhostまでリクエストを届けた。止められたのは、unshare -rnで空のネットワークnamespaceを使ったときだけだった。WebKitは約75GBの仮想メモリを確保するため、RLIMIT_ASによる制限も効かない。なお、数値はすべてLinuxでの各3回の計測によるもので、macOSとWindowsは試されていない。

決め手は隔離されたscript worldと、cgroupによるメモリ制限だ

試したすべての攻撃に耐えた構成は五層からなる。空のネットワークnamespace、bubblewrap付きの使い捨てプロファイル、プロセスグループごと終了させるタイムアウト、呼び出しごとの新しいプロセス、そしてランダムなnonceを付けた結果だ。この構成では1回の呼び出しに約450ミリ秒かかる。本格的に使うには、cgroupによるメモリとCPUの制限、glazeがまだ提供していない隔離されたscript world、そして起動待ちをなくす常駐ワーカーも必要だとレポートは述べる。

最初の注目点は、Lindsayが使い捨てプロファイルとサンドボックスのパッチを本流に取り込むかどうかだ。二つ目は、glazeがスクリプト用の隔離された実行環境を公開するかどうかである。それがなければ、webframeは信頼できるページをスクレイピングする便利な道具にとどまり、信頼できないコードに対しては防御の一層にすぎない。

Lilithの判定

厳しいCSPがあらゆるfetchを禁じていたのに、リダイレクト一行だけでページはlocalhostの扉を叩いた。webframeは身軽で速いエンジンだが、他人のJavaScriptを走らせるなら、空のネットワーク、使い捨てのプロファイル、時間が来たらプロセスを断ち切るストップウォッチという独房を、動かす側が自分で組み立てるしかない。