Lilith.
⌕
Redaktionelle Illustration: webframe startet eine Webseite in einer halben Sekunde wie ein Programm. Zur Sandbox wird es erst durch die Isolation drumherum
Illustration von Lilith · redaktioneller Remix

Simon Willison hat eine Bewertung von webframe veröffentlicht, einem Tool von Jeff Lindsay, das eine Webseite wie ein Kommandozeilenprogramm ausführt. Geschrieben hat die Bewertung Claude Opus 5.5 nach einem Prompt von Willison, und das Ergebnis fällt zweigeteilt aus: Fürs Scraping reicht das Tool, fremder Code braucht aber eine feste Isolation von außen.

Ein 5,3 MB großes Programm führt die Seite im Browser aus, den das System schon hat

Man zeigt webframe auf eine URL oder Datei, und es lädt die Seite in einer versteckten Webview. Ausgaben von console.log landen auf stdout, Exceptions auf stderr. Mit dem Schalter -c wertet es jede Zeile von stdin im Kontext der Seite aus. Geschrieben ist es in Go auf Basis der Bibliothek glaze, die den Systembrowser nutzt: WKWebView unter macOS, WebKitGTK unter Linux und WebView2 unter Windows. Eine eigene Engine bringt es nicht mit. Laut Bericht umfasst die gesamte CLI etwa 460 Zeilen Go und JavaScript und wird zu einem 5,3 MB großen Binary gebaut.

Willison gab dem Modell einen einzigen Prompt: das Tool ausprobieren, Prototypen bauen und es als Sandbox sowie als Ersatz für shot-scraper javascript bewerten. Das Repository weist ausdrücklich darauf hin, dass Text und Code von einem Sprachmodell stammen. Getestet wurde unter Ubuntu 24.04 mit WebKitGTK 2.52 und Xvfb. Der Start dauert etwa 0,5 s. Entstanden sind drei Prototypen: ein Patch für webframe, ein shot-scraper-Klon namens wfjs mit rund 350 Zeilen Go und eine Python-Funktion run_js, die das Modell mit 20 bösartigen Codeschnipseln angriff.

Beim Scraping hält es mit Playwright mit, Agenten bekommt es eine leichte JavaScript-Engine

Beim Scraping ging der Vergleich unentschieden aus. Den Titel von example.com zu holen dauerte mit wfjs 1,08 bis 1,17 s, mit shot-scraper auf Playwright 1,19 bis 1,40 s. wfjs braucht samt WebKit und Xvfb etwa 640 MB Arbeitsspeicher, Chrome mit Python etwa 456 MB. Alle Beispiele aus der shot-scraper-Dokumentation, die nicht auf dessen Spezialschalter angewiesen sind, liefen unverändert.

Für Entwickler von Agenten ist die zweite Hälfte spannender. Ein Agent, der generiertes oder heruntergeladenes JavaScript ausführen muss, bekommt mit webframe eine echte Browser-Engine mit WASM, crypto.subtle und DOM, aber ohne die Objekte fs und process. Playwright lädt dagegen einen eigenen Browser herunter. Auch die Methode ist bemerkenswert. Willison überließ die Prüfung einer neuen Abhängigkeit einem Agenten und bekam einen Patch, Prototypen und eine Angriffssammlung zurück. So ein Bericht ist ein günstiger erster Filter, den ein Tool passieren muss, bevor ein Team ihm vertraut.

Standardmäßig bekommt die Seite Netzwerk, localhost und unbegrenzten Speicher

Im Auslieferungszustand ist webframe für sich genommen keine Sandbox. Die Seite hat vollen Netzwerkzugriff einschließlich localhost, der Speicher überdauert zwischen den Läufen und die Renderer-Sandbox von WebKit ist ausgeschaltet. Zeit- und Speicherlimits fehlen. Zudem kann eine Seite einen internen Hook überschreiben und jede Auswertung mit einem gefälschten Ergebnis beantworten. Beim Scraping stören drei weitere Dinge: Seiten, deren CSP eval blockiert, darunter GitHub, lehnen jede Auswertung ab, der Viewport misst 0×0 und ein fehlgeschlagener Ladevorgang kann ewig hängen.

Selbst der bubblewrap-Patch schließt die Lücke nicht. Der Netzwerkprozess von WebKit läuft außerhalb davon, er schützt also vor einem kompromittierten Renderer, nicht vor einer Seite, die ins Netz will. Einer der Angriffe zeigte das: Eine Weiterleitung über location.href brachte trotz strenger CSP eine Anfrage bis zu localhost. Erst ein leerer Netzwerk-Namespace per unshare -rn stoppte sie. Ein Limit über RLIMIT_AS funktioniert nicht, weil WebKit etwa 75 GB virtuellen Speicher reserviert. Alle Zahlen stammen zudem von Linux und aus je drei Messungen, macOS und Windows hat der Bericht nicht getestet.

Eine isolierte Script World und ein cgroup-Speicherlimit werden entscheiden

Die Kombination, die allen versuchten Angriffen standhielt, hat fünf Schichten: einen leeren Netzwerk-Namespace, ein flüchtiges Profil mit bubblewrap, ein Timeout, das die ganze Prozessgruppe beendet, einen frischen Prozess pro Aufruf und ein mit zufälligem Nonce markiertes Ergebnis. Ein Aufruf dauert so etwa 450 ms. Für ernsthaften Einsatz verlangt der Bericht außerdem ein Speicher- und CPU-Limit per cgroup, eine isolierte Script World, die glaze noch nicht anbietet, und einen langlebigen Worker, der die Startwartezeit beseitigt.

Das erste Signal ist, ob Lindsay den Patch mit flüchtigem Profil und Sandbox in den Hauptzweig übernimmt. Das zweite, ob glaze eine isolierte Welt für Skripte freigibt. Ohne sie bleibt webframe ein praktisches Werkzeug zum Scrapen vertrauenswürdiger Seiten und für fremden Code nur eine Schicht von mehreren.

Liliths Urteil

Eine einzige Zeile mit einer Weiterleitung genügte, und die Seite klopfte bei localhost an, obwohl eine strenge CSP ihr jeden Fetch verbot. webframe ist ein flinker, schneller Motor, doch wer darin fremdes JavaScript ausführt, muss die Zelle drumherum selbst bauen: ein leeres Netz, ein Wegwerfprofil und eine Stoppuhr, die den Prozess rechtzeitig kappt.