2026-10-26 · ← Aktualności
webframe uruchamia stronę jak program w pół sekundy. Sandboksem staje się dopiero dzięki izolacji wokół
Simon Willison zlecił Claude Opus 5.5 sprawdzenie webframe, małego narzędzia Jeffa Lindsaya, które uruchamia stronę internetową jak program w terminalu. Do scrapingu jest mniej więcej tak szybkie jak shot-scraper. Jako sandbox dla cudzego JavaScriptu sprawdziło się dopiero opakowane w pustą przestrzeń nazw sieci, bubblewrap i timeout.
Nie udało się wczytać obrazu.
Simon Willison opublikował analizę narzędzia webframe autorstwa Jeffa Lindsaya. webframe uruchamia stronę internetową jak program w terminalu. Analizę na zlecenie Willisona przygotował model Claude Opus 5.5, a wniosek jest podwójny: do scrapingu narzędzie wystarcza, ale cudzy kod wymaga solidnej izolacji z zewnątrz.
Program ważący 5,3 MB uruchamia stronę w przeglądarce, którą system już ma
Wskazujesz webframe adres URL albo plik, a narzędzie ładuje stronę w ukrytym webview. Wyjście console.log trafia na standardowe wyjście, wyjątki na wyjście błędów. Z przełącznikiem -c wykonuje w kontekście strony każdą linię ze standardowego wejścia. Jest napisane w Go na bibliotece glaze, która korzysta z przeglądarki systemowej: WKWebView na macOS, WebKitGTK na Linuksie i WebView2 na Windows. Własnego silnika więc nie dołącza. Według analizy całe CLI to około 460 linii Go i JavaScriptu, a binarka waży 5,3 MB.
Willison dał modelowi jeden prompt: wypróbować narzędzie, zbudować prototypy i ocenić je zarówno jako sandbox, jak i zamiennik polecenia shot-scraper javascript. Repozytorium wprost informuje, że tekst i kod napisał model językowy. Testy przeprowadzono na Ubuntu 24.04 z WebKitGTK 2.52 i Xvfb. Start zajmuje około 0,5 s. Powstały trzy prototypy: łatka do webframe, klon shot-scrapera o nazwie wfjs liczący około 350 linii Go oraz pythonowa funkcja run_js, którą model zaatakował 20 złośliwymi fragmentami kodu.
W scrapingu dotrzymuje kroku Playwrightowi, agentom daje lekki silnik JavaScriptu
W scrapingu porównanie wypadło na remis. Pobranie tytułu strony example.com trwało z wfjs od 1,08 do 1,17 s, a z shot-scraperem na Playwrighcie od 1,19 do 1,40 s. wfjs razem z WebKitem i Xvfb zużywa około 640 MB pamięci, Chrome z Pythonem około 456 MB. Wszystkie przykłady z dokumentacji shot-scrapera, które nie opierają się na jego specjalnych przełącznikach, zadziałały bez zmian.
Dla twórców agentów ciekawsza jest druga połowa. Agent, który musi uruchomić wygenerowany albo pobrany JavaScript, dostaje w webframe prawdziwy silnik przeglądarki z WASM, crypto.subtle i DOM, ale bez obiektów fs i process. Playwright tymczasem pobiera własną przeglądarkę. Uwagę zwraca też sama metoda. Willison oddał ocenę nowej zależności agentowi i dostał z powrotem łatkę, prototypy i zestaw ataków. Taka analiza to tanie pierwsze sito, przez które narzędzie musi przejść, zanim zespół zacznie mu ufać.
Domyślnie strona ma sieć, localhost i nieograniczoną pamięć
Niezmodyfikowane webframe samo z siebie sandboksem nie jest. Strona ma pełny dostęp do sieci, łącznie z localhostem, dane w pamięci przeglądarki przetrwają między uruchomieniami, a sandbox renderera WebKita jest wyłączony. Brakuje limitów czasu i pamięci. Strona może też nadpisać wewnętrzny hak i na każde wykonanie kodu odpowiedzieć sfałszowanym wynikiem. Przy scrapingu przeszkadzają jeszcze trzy rzeczy: strony, których CSP blokuje eval, w tym GitHub, odrzucają każde wykonanie, viewport ma wymiar 0×0, a nieudane ładowanie potrafi wisieć bez końca.
Nawet łatka z bubblewrapem nie zamyka luki. Proces sieciowy WebKita działa poza nim, więc chroni tylko przed przejętym rendererem, a nie przed stroną, która chce wyjść na zewnątrz. Pokazał to jeden z ataków: przekierowanie przez location.href dostarczyło żądanie na localhost mimo ścisłej CSP. Zatrzymała je dopiero pusta przestrzeń nazw sieci przez unshare -rn. Limit przez RLIMIT_AS nie działa, bo WebKit rezerwuje około 75 GB pamięci wirtualnej. Wszystkie liczby pochodzą przy tym z Linuksa i trzech pomiarów, macOS i Windows nie były testowane.
Rozstrzygnie izolowany script world i limit pamięci przez cgroup
Zestaw, który wytrzymał wszystkie wypróbowane ataki, ma pięć warstw: pustą przestrzeń nazw sieci, jednorazowy profil z bubblewrapem, timeout zabijający całą grupę procesów, nowy proces dla każdego wywołania i wynik oznaczony losowym nonce. Jedno wywołanie trwa wtedy około 450 ms. Do poważnych zastosowań analiza wymaga jeszcze limitu pamięci i CPU przez cgroup, izolowanego script world, którego glaze na razie nie udostępnia, oraz długo działającego workera, który usunie czekanie na start.
Pierwszym sygnałem będzie to, czy Lindsay włączy łatkę z jednorazowym profilem i sandboksem do głównej gałęzi. Drugim, czy glaze udostępni izolowany świat dla skryptów. Bez niego webframe pozostanie zgrabnym narzędziem do scrapingu zaufanych stron, a dla cudzego kodu tylko jedną z warstw.
Werdykt Lilith
Wystarczyła jedna linijka z przekierowaniem, żeby strona zapukała do localhosta, choć ścisła CSP zabraniała jej każdego fetcha. webframe to zwinny i szybki silnik, ale celę dla cudzego JavaScriptu musi wokół niego zbudować ten, kto go uruchamia: pustą sieć, jednorazowy profil i stoper, który w porę utnie proces.