Lilith.
⌕
Redakční ilustrace: Webframe spustí web jako program za půl sekundy. Sandbox z něj udělá až izolace okolo
Ilustrace Lilith · redakční remix

Simon Willison zveřejnil rozbor nástroje webframe od Jeffa Lindsayho. Webframe spustí webovou stránku jako program v terminálu. Rozbor na Willisonovo zadání napsal model Claude Opus 5.5. Závěr má dvě půlky: na scraping nástroj stačí, cizí kód ale musí zavřít do pevné izolace zvenčí.

Program o velikosti 5,3 MB spouští stránku v prohlížeči, který už v systému je

Webframe dostane URL nebo soubor a stránku načte ve skrytém okně. Výpisy z console.log jdou na standardní výstup, chyby na chybový. S přepínačem -c spustí ve stránce každý řádek ze vstupu. Je psaný v Go nad knihovnou glaze. Ta půjčí prohlížeč ze systému: na macOS WKWebView, na Linuxu WebKitGTK a na Windows WebView2. Vlastní engine si webframe nenosí. Celé CLI má asi 460 řádků kódu a binárka váží 5,3 MB.

Willison dal modelu jeden prompt. Měl nástroj vyzkoušet, postavit prototypy a posoudit ho jako sandbox i jako náhradu za shot-scraper javascript. Repozitář jasně píše, že text i kód napsal jazykový model. Testy běžely na Ubuntu 24.04 s WebKitGTK 2.52 a Xvfb. Start trvá asi 0,5 s. Vznikly tři prototypy. Prvním je záplata pro webframe, druhým klon shot-scraperu wfjs o asi 350 řádcích. Třetím je funkce run_js v Pythonu, na kterou model pustil 20 zlých kousků kódu.

Při scrapingu drží krok s Playwrightem, agentům dá lehký engine pro JavaScript

Ve scrapingu vyšlo srovnání skoro nastejno. Titulek stránky example.com stáhl wfjs za 1,08 až 1,17 s. Shot-scraper nad Playwrightem potřeboval 1,19 až 1,40 s. Paměti si wfjs i s WebKitem a Xvfb vezme asi 640 MB, Chrome s Pythonem asi 456 MB. Všechny příklady z návodu shot-scraperu, které nestojí na jeho zvláštních přepínačích, prošly beze změny.

Pro autory agentů je zajímavější druhá půlka. Agent někdy musí spustit JavaScript, který sám napsal nebo stáhl. Ve webframe dostane skutečný engine z prohlížeče s WASM, crypto.subtle a DOM. Objekty fs a process tam nejsou. Playwright si naproti tomu stahuje vlastní prohlížeč. Za pozornost stojí i postup. Willison svěřil posouzení nové knihovny agentovi a dostal zpět záplatu, prototypy i sadu útoků. Takový rozbor je levné první síto. Nástroj jím musí projít dřív, než mu tým začne věřit.

Ve výchozím stavu má stránka síť, localhost i paměť bez mezí

Webframe bez úprav sám o sobě sandbox nedělá. Stránka vidí celou síť i localhost. Data v úložišti přežijí do dalšího běhu a sandbox pro vykreslování WebKitu je vypnutý. Chybí limit času i paměti. Stránka navíc umí přepsat vnitřní háček a na každý dotaz vrátit falešný výsledek. Scraping kazí ještě tři věci. Stránky, jejichž CSP zakazuje eval, odmítnou každý dotaz, a patří mezi ně i GitHub. Okno má rozměr 0×0 a nepovedené načtení může viset donekonečna.

Díru nezalepí ani záplata s bubblewrapem. Síťový proces WebKitu běží mimo něj. Chrání tedy jen před napadeným vykreslováním, stránka, která chce ven, projde. Ukázal to jeden z útoků. Přesměrování přes location.href doručilo dotaz na localhost i přes přísnou CSP. Zastavil ho až prázdný síťový namespace přes unshare -rn. Limit RLIMIT_AS nepomůže, protože WebKit si zabere asi 75 GB virtuální paměti. Všechna čísla jsou navíc z Linuxu a ze tří měření. Systémy macOS a Windows rozbor nezkoušel.

Rozhodne izolovaný script world a limit paměti přes cgroup

Všem útokům odolala sestava o pěti vrstvách. Tvoří ji prázdný síťový namespace, jednorázový profil s bubblewrapem a timeout, který zabije celou skupinu procesů. K tomu nový proces pro každé volání a výsledek označený náhodným nonce. Jedno volání pak trvá asi 450 ms. Pro vážné nasazení rozbor chce ještě limit paměti a CPU přes cgroup. Dál izolovaný script world, který glaze zatím nenabízí, a stále běžící worker, který ušetří čekání na start.

Prvním signálem bude, jestli Lindsay vezme záplatu s jednorázovým profilem a sandboxem do hlavní větve. Druhým, jestli glaze zpřístupní oddělený svět pro skripty. Bez něj zůstane webframe šikovný nástroj na scraping stránek, kterým věříte. Pro cizí kód bude jen jednou z vrstev.

Lilithin verdikt

Stačil jeden řádek s přesměrováním a stránka zaklepala na localhost, přestože jí přísná CSP zakazovala jakýkoli fetch. Webframe je šikovný a rychlý motor, celu pro cizí JavaScript ale kolem něj musí postavit ten, kdo ho pouští: prázdnou síť, jednorázový profil a stopky, které proces včas utnou.