ist jetzt verfügbar! Lesen Sie über die neuen Funktionen und Fehlerbehebungen vom November.

Migration von VS Code zu Process Sandboxing

Ein Gewinn für Sicherheit und die VS Code-Architektur

28. November 2022 von Benjamin Pasero, @BenjaminPasero

Die Aktivierung der Sandbox in Electron Renderer-Prozessen ist eine kritische Voraussetzung für sichere und zuverlässige Electron-Anwendungen wie Visual Studio Code. Die Sandbox reduziert den Schaden, den schädlicher Code verursachen kann, indem sie den Zugriff auf die meisten Systemressourcen einschränkt. In diesem Blogbeitrag geben wir einen detaillierten Überblick darüber, wie wir die Prozess-Sandboxing in VS Code erfolgreich aktivieren konnten. Diese Reise, die wir Anfang 2020 begonnen haben, planen wir Anfang 2023 abzuschließen. Um die Herausforderung des Prozess-Sandboxing zu verdeutlichen, beschreibt dieser Blogbeitrag auch Details des VS Code-Prozessmodells und wie es sich im Laufe dieser Reise entwickelt hat.

Dies war eine Teamleistung, da grundlegende architektonische Änderungen sowie Codeanpassungen in fast allen VS Code-Komponenten erforderlich waren. Die Prozessarchitektur von VS Code wurde überarbeitet und dabei erheblich gestärkt. Wir heben die wichtigsten Meilensteine auf dem Weg hervor, von denen wir hoffen, dass sie wertvolle Einblicke für andere bieten. Seit einigen Monaten läuft der Prozess-Sandbox-Modus erfolgreich in VS Code Insiders und liefert uns Feedback über die Auswirkungen dieser Änderung. Zögern Sie nicht, uns zu kontaktieren, wenn Sie ein Problem finden, eine Anregung zur Verbesserung der Erfahrung haben oder allgemeine Fragen stellen möchten.

Wenn Sie mit VS Code, Electron oder Sandboxing nicht vertraut sind, sollten Sie möglicherweise zuerst den Abschnitt Terminologie am Ende des Blogbeitrags lesen. Dort finden Sie Erklärungen der verwendeten Begriffe und Links zu Hintergrundmaterial.

Prozess-Sandboxing im Überblick

Lange Zeit hat Electron die direkte Nutzung von Node.js APIs in HTML und JavaScript erlaubt. Der folgende Code-Schnipsel bietet ein einfaches Beispiel für eine Webseite, die nicht nur "Hello World" für den Benutzer ausgibt, sondern auch in eine Datei auf der lokalen Festplatte schreibt.

HTML and Node.js code on a web page in Electron

Der Electron-Prozess, der für die Darstellung der Webseite für den Benutzer verantwortlich ist, wird als Renderer-Prozess bezeichnet. Die Aktivierung des Sandbox-Modus für den Renderer-Prozess schränkt seine Fähigkeiten zur Verbesserung der Sicherheit ein und nähert sich mehr dem Webmodell an: Während HTML und JavaScript weiterhin erlaubt sind, ist die Nutzung von Node.js nicht mehr möglich. Komponenten im Renderer-Prozess, die Zugriff auf Systemressourcen benötigen, müssen dies an einen anderen Prozess delegieren, der nicht sandboxed ist.

Der folgende Code ist nicht mehr von Node.js abhängig, sondern verwendet eine globale Variable namens vscode, die die Funktionalität zur Aktualisierung von Einstellungen bereitstellt. Die Implementierung der Methode beinhaltet das Senden einer Nachricht an einen anderen Prozess, der Zugriff auf Node.js hat. Somit wird sie auch nicht mehr synchron, sondern asynchron ausgeführt.

Removing Node.js by providing an asynchronous alternative in Electron

Wie wir zur globalen Variable vscode im Renderer-Prozess gekommen sind und wie sie implementiert ist, wird in den Timeline-Abschnitten weiter unten detailliert beschrieben.

Das Blockieren von Node.js aus Renderer-Prozessen ist eine empfohlene Sicherheitsempfehlung von Electron. Wir hatten in der Vergangenheit Sicherheitsprobleme, bei denen Angreifer beliebigen Node.js-Code aus dem Renderer-Prozess ausführen konnten. Ein sandboxed Renderer-Prozess reduziert das Risiko dieser Angriffe erheblich.

Wie sind wir dorthin gekommen?

Eine so große Änderung wie das Entfernen aller unserer Node.js-Abhängigkeiten aus dem Renderer-Prozess birgt das Risiko von Regressionen und Fehlern. Code, der zuvor in einem Prozess lief, muss aufgeteilt und über mehrere Prozesse verteilt werden. Node-Module, die nativ sind und somit nicht web-gepackt werden können, müssen ebenfalls ausgelagert werden. Bestimmte globale Objekte wie Node.js Buffer müssen durch Browser-kompatible Varianten wie Uint8Array ersetzt werden.

Das folgende Diagramm zeigt unsere Prozessarchitektur vor Beginn der Sandbox-Bemühungen. Wie Sie sehen können, sind die meisten Prozesse Node.js-Kindprozesse (in Grün), die vom Renderer-Prozess abgeleitet sind. Die meiste (Interprozesskommunikation) IPC wird über Node.js-Sockets implementiert, und der Renderer-Prozess ist ein wichtiger Kunde von Node.js-APIs – zum Beispiel zum Lesen und Schreiben von Dateien.

VS Code process model before sandboxing in 2020

Wir haben schnell entschieden, dass wir an der Prozess-Sandboxing arbeiten wollten, ohne eine separate VS Code-Anwendung ausliefern zu müssen, die sandboxed ist. Wir wollten den VS Code Renderer-Prozess schrittweise Sandbox-fähig machen und dann am Ende den Schalter umlegen. In den letzten Jahren haben wir monatliche stabile VS Code-Releases mit Änderungen veröffentlicht, die zum Sandbox-Ziel beitragen, ohne es vollständig zu aktivieren. Stellen Sie sich vor, Sie fliegen ein Flugzeug, das im Flug grundlegend umgebaut wird. Und in unserem Fall waren sich die Benutzer der Änderungen an VS Code größtenteils nicht bewusst.

Unsere technologische Zeitachse

Die nächsten Abschnitte gehen ins Detail, wie das Sandboxing über die letzten Jahre zustande kam. Die Hauptaufgabe bestand darin, alle Node.js-Abhängigkeiten aus dem Renderer-Prozess zu entfernen. Unterwegs ergaben sich jedoch weitere Herausforderungen, wie die Entwicklung einer effizienten, Sandbox-fähigen IPC-Lösung mit Hilfe von MessagePort oder die Suche nach neuen Hosts für die verschiedenen Node.js-Kindprozesse, die wir vom Renderer-Prozess ableiten konnten.

Größtenteils folgt die Reihenfolge der Themen der tatsächlichen Zeitachse. Um jeden Abschnitt kurz zu halten, verlinken wir auf andere Dokumente und Tutorials, die einen bestimmten technischen Aspekt detaillierter erklären. Und obwohl wir diese Arbeit Anfang 2020 geplant hatten, ist es unfair, einige der früheren Arbeiten, die bei dieser Aufgabe geholfen haben, auszulassen. Schauen wir uns das genauer an...

Auf den Schultern von Giganten stehen

Als wir Anfang 2020 begannen, Sandboxing in Betracht zu ziehen, hatten wir bereits eine Version von VS Code ausgeliefert, die in Webbrowsern ausgeführt werden konnte. Sie können vscode.dev in Ihrem Browser ausführen und Visual Studio Code für das Web in Aktion sehen. Bei der Erstellung einer Webversion von VS Code hatten wir gelernt, wie man Node.js-Abhängigkeiten aus der Workbench – dem Hauptfenster der Benutzeroberfläche von VS Code – entfernt.

VS Code for Web running in the browser

Das Entfernen von Abhängigkeiten zu Node.js bedeutete, Alternativen zu finden. Zum Beispiel wurde unsere Abhängigkeit vom Node.js-Typ Buffer durch ein VSBuffer-Äquivalent ersetzt, das in Browserumgebungen auf Uint8Array zurückfiel. Wir waren auch in der Lage, einige Node.js-Module (oniguruma, iconv-lite) so zu verpacken, dass sie in Webumgebungen laufen.

VSBuffer utility class supporting both Node.js and web environments

Aber noch bevor VS Code für das Web Realität wurde, hatten wir die Unterstützung für Remote-Entwicklung aktiviert, die es ermöglicht, Quellcode auf einem Remote-Host zu bearbeiten, z. B. über eine SSH-Verbindung (und später sogar GitHub Codespaces ermöglichte). Für die Remote-Entwicklung mussten wir eine Lösung implementieren, bei der die UI-seitigen Teile von VS Code lokal laufen, während die eigentlichen Dateioperationen auf einer Remote-Maschine ausgeführt werden. Dieses Modell gilt auch für eine sandboxed Workbench, bei der privilegierte Operationen in einem anderen Prozess ausgeführt werden müssen. In beiden Fällen kommuniziert der Renderer-Prozess über IPC mit einem privilegierten Host, um die Operationen auszuführen.

Aktivierung eines Kommunikationskanals vom Renderer

Wenn ein Renderer-Prozess Node.js nicht verwenden kann, müssen Arbeiten an einen anderen Prozess delegiert werden, in dem Node.js verfügbar ist. Eine Lösung im Webkontext könnte die Nutzung von HTTP-Methoden sein, bei denen ein Server die Anfragen annimmt. Dies schien jedoch keine optimale Lösung für Desktop-Anwendungen zu sein, da die Ausführung eines lokalen Servers auf einem Port aus Sicherheitsgründen durch eine Firewall blockiert werden könnte.

Electron bietet die Möglichkeit, Preload-Skripte in den Renderer-Prozess einzubinden, die vor der Ausführung des Hauptskripts ausgeführt werden. Diese Skripte haben Zugriff auf den IPC-Mechanismus von Electron. Preload-Skripte können die für das Hauptskript des Renderers verfügbare API über die Context Bridge API erweitern. Während das Preload-Skript Electron IPC direkt verwenden kann, kann das Hauptskript dies nicht. Daher stellen wir bestimmte Methoden über die Context Bridge für das Hauptskript bereit. Im anfänglichen Beispiel, das wir verwendet haben, sehen Sie hier, wie eine Methode zum Aktualisieren von Einstellungen aus einem Preload-Skript in das Hauptskript exportiert werden könnte.

Exposing a method from preload script to the main script in Electron

Preload-Skripte sind unser grundlegender Baustein, um privilegierte von unprivilegierten Code zu trennen. Zum Beispiel bedeutet das Schreiben in eine Datei auf der Festplatte, dass eine IPC-Nachricht mit dem neuen Inhalt vom Hauptskript zum Preload-Skript und von dort zum Hauptprozess gelangt, der Zugriff auf Node.js hat.

IPC flow when preload scripts are involved in Electron

Schnelle Interprozesskommunikation über Message Ports

Mit der Einführung von Preload-Skripten haben wir eine Möglichkeit für den Renderer-Prozess, mit dem Electron-Hauptprozess zu kommunizieren, um Aufgaben zu planen. In Electron-Anwendungen ist es jedoch entscheidend, den Hauptprozess nicht mit zu vielen Aufgaben zu überlasten, da er auch für die Verarbeitung von Benutzereingaben, z. B. von Tastatur und Maus, zuständig ist. Ein ausgelasteter Hauptprozess kann zu einer nicht reagierenden Benutzeroberfläche führen.

Dies war ein Problem, das wir bereits zuvor gesehen hatten. Schon bevor wir uns mit Sandboxing beschäftigten, waren wir daran interessiert, performante Code in einen Hintergrundprozess, den VS Code Shared Process, auszulagern. Dieser Prozess ist ein verstecktes Fenster, mit dem alle Workbench-Fenster und der Hauptprozess kommunizieren können. Wenn Sie beispielsweise eine Erweiterung installieren, wird eine Anfrage an den Shared Process gesendet, um den gesamten Vorgang durchzuführen.

Die Kommunikation mit dem Shared Process wurde jedoch über Node.js-Sockets implementiert. Dies hatte den Vorteil, dass der Hauptprozess keinerlei Overhead hatte, da er überhaupt nicht an der Kommunikation beteiligt war. Der Nachteil ist, dass die Node.js-Socket-Kommunikation in sandboxed Renderern nicht möglich ist, da Sie keine Node.js-APIs verwenden können.

Message Ports bieten eine leistungsstarke Möglichkeit, zwei Prozesse miteinander zu verbinden, indem ein IPC-Kanal zwischen ihnen eingerichtet wird. Selbst ein vollständig sandboxed Renderer-Prozess kann einen Message Port verwenden, da diese als Web API in Browsern bereitgestellt werden. Durch den Ersatz der Node.js-Socket-Kommunikation durch Message Ports konnten wir eine Sandbox-kompatible IPC-Lösung erhalten und gleichzeitig den Performance-Aspekt beibehalten, den Hauptprozess nicht einbeziehen zu müssen.

Das Übergeben von Message Ports über Prozessgrenzen hinweg ist komplex, insbesondere in sandboxed Renderer-Prozesse mit Preload-Skripten. Die Reihenfolge ist im folgenden Diagramm dargestellt.

  • Der Shared Process erstellt die Message Ports P1 und P2 und behält P1.
  • P2 wird über Electron IPC an den Hauptprozess gesendet.
  • Der Hauptprozess leitet P2 an den anfragenden Renderer-Prozess weiter.
  • P2 landet im Preload-Skript dieses Renderer-Prozesses.
  • Das Preload-Skript leitet P2 in das Hauptskript des Renderers weiter.
  • Das Hauptskript empfängt P2 und kann es zum direkten Senden von Nachrichten verwenden.

Message ports exchange between shared and renderer process in VS Code

Änderung des Ursprungs des Renderers

In einem Webbrowser geben Sie eine URL ein, und Inhalte werden geladen und angezeigt. In Electron geben Sie keine URL ein, stattdessen entscheidet die Anwendung für Sie, welche Inhalte geladen und angezeigt werden. Wenn Sie also VS Code öffnen, lädt ein Fenster mit einer vorkonfigurierten URL, um den Inhalt der Workbench anzuzeigen.

Für VS Code verwendete diese URL das lokale Dateiprotokoll, das auf eine tatsächliche Datei auf der Festplatte zeigte (file://<Pfad zur Datei auf der Festplatte>). Als Teil der Sandboxing-Arbeiten haben wir diesen Ansatz überarbeitet, da er erhebliche Sicherheitsimplikationen hatte. Chromium trifft bestimmte Sicherheitsannahmen für das lokale Dateiprotokoll, die weniger streng sind als für das HTTPS-Protokoll. Beispielsweise werden für lokale Dateiprotokoll-URLs keine strengen Ursprungsprüfungen angewendet.

Mit Electron können Sie benutzerdefinierte Protokolle registrieren, die zum Laden von Inhalten in den Renderer-Prozess verwendet werden können. Die benutzerdefinierten Protokolle können so konfiguriert werden, dass sie sich in Bezug auf die Sicherheit wie HTTPS-Protokolle verhalten. Wir haben diesen Ansatz verwendet, um die Notwendigkeit eines lokalen Webservers zur Bereitstellung der Inhalte zu vermeiden.

Mit der Einführung des benutzerdefinierten Protokolls vscode-file für alle unsere Renderer-Prozesse konnten wir alle Verwendungen des Dateiprotokolls einstellen. Es ist konfiguriert, sich wie HTTPS zu verhalten, und bedeutete, dass wir uns dem näherten, wie VS Code für das Web tatsächlich funktioniert.

Anpassung unseres Code-Loaders

Historisch gesehen wurde unser gesamter TypeScript-Code in AMD-Module kompiliert und mit einem benutzerdefinierten Loader geladen, den wir über die Jahre gepflegt haben. Wir planen, von AMD wegzukommen und ESM zu übernehmen, aber diese Arbeit befindet sich noch in einem frühen Stadium.

Unser Code-Loader unterstützt sowohl Node.js- als auch Web-Umgebungen, indem er bestimmte vordefinierte Variablen abfragt, um die tatsächliche laufende Umgebung zu ermitteln. Ein sandboxed Renderer ist im Grunde wie eine Web-Umgebung, daher waren nur wenige Änderungen an unserem Loader erforderlich, um die Sandbox zu unterstützen.

Nachdem diese Änderungen vorgenommen waren, konnten wir eine frühe Version von VS Code mit aktivierter Sandbox-Modus ausführen. Da wir den Renderer-Prozess jedoch noch nicht von seinen Node.js-Abhängigkeiten befreit hatten, wurde nur eine leere Seite angezeigt, zusammen mit Fehlermeldungen in der Konsole.

Tools zur Unterstützung der Einführung

Nun, da wir eine Möglichkeit hatten, VS Code mit aktivierter Sandbox auszuführen, wollten wir in Tools investieren, um den Übergang von Quellcode, der von Node.js abhängt, zu Code, der "sandbox-ready" ist, zu erleichtern. Angesichts unserer Investition in VS Code für das Web hatten wir bereits statische Analysetools, die Node.js-Code am Versand an die Webversion hinderten. Diese Tools definierten eine Reihe von Zielumgebungen mit ihren Laufzeitanforderungen. Unsere Tools können die Verwendung von Node.js-globalen Objekten (wie Buffer), Node.js-APIs oder Node-Modulen in einer Zielumgebung erkennen und melden, die diese nicht zulassen. Für die Sandboxing-Arbeit haben wir eine neue Zielumgebung electron-sandbox hinzugefügt, die keine Node.js-Nutzung zulässt. Durch die Migration von Code in diese Umgebung konnten wir den Code schrittweise Sandbox-fähig machen.

In der folgenden Abbildung wird ein Warnsymbol im Editor angezeigt, das darauf hinweist, dass eine Datei aus der Zielumgebung browser von einer API aus Node.js abhängt. Die Warnung führt zum Fehlschlag unseres Builds und verhindert, dass dieser Code versehentlich in eine Veröffentlichung gelangt.

A warning in VS Code informing about a target environment violation

Unsere Dienstprogramme "Process Explorer" und "Issue Reporter" gehörten zu den ersten, die den Anforderungen der Zielumgebung electron-sandbox entsprachen. Wir konnten diese Fenster vollständig sandboxed ausführen, lange bevor die Workbench-Fenster die Umstellung abgeschlossen hatten.

Auslagerung von Prozessen aus dem Renderer

Wie die vorherigen Abschnitte ausführlich erklärt haben, kann die Auslagerung von Node.js-Funktionalitäten in einen anderen Prozess und die Nutzung von IPC zur Planung von Arbeiten und zum Empfang von Ergebnissen unkompliziert sein.

Einige der Komponenten in der Workbench, die von Node.js abhängen, sind jedoch komplexer, insbesondere solche, die einen Kindprozess starten, wie z. B.:

  • Extension Host
  • Integrierte Terminals
  • Dateiüberwachung
  • Volltextsuche
  • Aufgaben-Ausführung
  • Debugging

Da VS Code in Remote-Szenarien laufen kann, hatten wir bereits Mechanismen zur Ausführung einiger Aufgaben remote implementiert, nämlich: Suche, Debugging und Aufgaben-Ausführung. Diese Komponenten können innerhalb des Extension Host-Prozesses arbeiten, der naturgemäß lokal zu dem Code läuft, wo er sich befindet. Daher konnten wir die Zuständigkeit für diese Kindprozesse vom Renderer-Prozess auf den Extension Host übertragen, selbst wenn VS Code lokal ohne eine angehängte Remote-Verbindung lief.

Für den Extension Host hatten wir ehrgeizigere Pläne. Wir behandeln diese Änderungen in einem eigenen Abschnitt weiter unten, da dies die Hinzufügung einer neuen "Utility Process"-API zu Electron erforderte.

Integrierte Terminals und Dateiüberwachung wurden zu Kindprozessen des Shared Process verschoben. Jedes Fenster, das Dateiüberwachung oder integrierte Terminals benötigt, würde über Message Ports mit dem Shared Process kommunizieren, um diese Dienste zu erhalten.

Das folgende Diagramm zeigt unsere Prozessarchitektur Ende 2022, nachdem wir die Sandbox im Renderer-Prozess aktiviert hatten. Alle Node.js-Prozesse wurden entweder zu Kindprozessen des Shared Process oder zu Utility-Prozessen des Hauptprozesses verschoben. Message Ports werden für eine effiziente direkte Prozess-zu-Prozess-Kommunikation verwendet, ohne den Hauptprozess zu belasten.

VS Code process model after sandboxing in late 2022

Anpassung des Chromium-Code-Cachings

Wir wollten auch sicherstellen, dass die Aktivierung der Sandbox keine Performance-Regressionen verursacht. Wir haben gemessen, wie lange es vom Start bis zum Erscheinen eines blinkenden Cursors im Editor dauert, und eine kritische Menge an Zeit wird in der V8 JavaScript-Engine verbracht, um das Haupt-Workbench-Skript (ca. 11,5 MB minifizierter Code) zu laden, zu parsen und auszuführen. Sofern kein Update installiert ist, wird dasselbe Skript bei jedem Start geladen. Angesichts dieses Verhaltens kann V8 eine optimierte Version des Skripts auf der Festplatte speichern, die beim nächsten Mal mit Code-Caching schneller geladen werden kann.

Chromium selbst nutzt Code-Caching, um die Ladezeiten von Webseiten zu beschleunigen. Es löst dieselben Optimierungen in der V8-Engine aus wie unsere Lösung, jedoch tut dies die Chromium-Implementierung nur für Webseiten, die häufig über einen bestimmten Zeitraum besucht werden. Wir wollten eine Lösung, die immer Code-Caching nutzt, da unsere Anwendung eine Desktop-Anwendung und keine Webseite ist.

Wir haben das Code-Caching beim Start aktiviert, und es ist schnell zu unserer besten Lösung für verbesserte Startzeiten geworden. Leider war unsere Lösung abhängig von Node.js und war in sandboxed Renderer-Prozessen nicht anwendbar.

Durch die Freigabe von Code-Caching-Optionen in Electron können wir das Code-Caching in Chromium erzwingen, wenn wir die Option bypassHeatCheck verwenden. Zusätzlich haben wir eine zusätzliche Schutzschicht hinzugefügt, indem wir zuvor generierte Code-Caches verwerfen, wenn wir erkennen, dass der Benutzer eine neuere Version von VS Code ausführt.

Eine neue Electron API: UtilityProcess

Die letzte und wahrscheinlich komplexeste Aufgabe war die Suche nach einer Lösung für die Verlagerung des Extension Hosts. Ähnlich wie beim Shared Process wurde die Kommunikation über Node.js-Sockets implementiert. Es gibt einen Extension Host-Prozess pro Fenster, und Erweiterungen können beliebig viele Kindprozesse starten, wie sie benötigen.

Wir hatten darüber nachgedacht, den Extension Host wie Datei-Watcher und integrierte Terminals in unseren Shared Process zu verlagern, waren aber der Meinung, dass wir die Gelegenheit nutzen und etwas Flexibleres bauen sollten, das kein verstecktes Fenster als Host benötigt.

Zu diesem Zweck wollten wir eine robuste und skalierbare Lösung, die in sandboxed Renderern funktioniert, aber die meisten aktuellen Verhaltensweisen beibehält.

  • Isolierter Prozess mit Unterstützung zum Starten von Kindprozessen
  • Vollständige Node.js-Unterstützung
  • Verwendung von Message Ports für direkte IPC mit sandboxed Prozessen

Zu diesem Zeitpunkt konnte Electron uns keine API zur Verfügung stellen, die diese Anforderungen erfüllt, und so haben wir eine neue Utility Process API zu Electron beigetragen. Diese API ermöglichte es uns, den Extension Host vom Renderer-Prozess in einen Utility-Prozess zu verlagern, der vom Hauptprozess erstellt wird. Mithilfe von Message Ports können wir direkt zwischen dem Renderer und dem Extension Host kommunizieren, ohne andere Prozesse zu beeinträchtigen, wie z. B. den Hauptprozess, der die gesamte Benutzereingabe verarbeitet.

Umstellung vom Electron WebView-Element

Obwohl nicht unbedingt erforderlich, um die Sandbox zu aktivieren, nutzten wir die Gelegenheit, die Verwendung des Electron WebView-Tags in VS Code zu überdenken und es durch das iframe-Tag zu ersetzen, um VS Code besser mit der Funktionsweise im Web abzustimmen. Beide Tags sind ähnlich, da sie es der Workbench ermöglichen, unvertrauenswürdigen Code von Erweiterungen zu hosten und gleichzeitig die Workbench vor den Auswirkungen der Ausführung dieses Codes zu isolieren. Wenn Sie beispielsweise die Vorschau einer Markdown-Datei öffnen, werden die Inhalte in einem solchen Element gerendert, das von der integrierten Markdown-Erweiterung bereitgestellt wird.

In den meisten Fällen konnten wir das webview-Tag einfach durch das iframe-Tag ersetzen. Bei iframes fehlte jedoch eine Funktion: die Möglichkeit, textuelle Suchen im Inhalt durchzuführen und hervorzuheben. Diese Funktion war entscheidend, um beim Vorabansicht einer Markdown-Datei nach ihr suchen zu können. Obwohl Chromium diese Funktionalität intern implementierte, wurde sie nicht als Web-API zur Nutzung exportiert. Wir haben die notwendigen Änderungen vorgenommen, um die API in Electron verfügbar zu machen, und konnten alle Verwendungen von webview-Elementen einstellen.

Aktivierung der Wiederverwendung von Renderer-Prozessen

Ein Performance-Vorteil von sandboxed Renderer-Prozessen ist ihr Lebenszyklusverhalten in Electron. Traditionell wurde der Renderer-Prozess jedes Mal beendet und neu gestartet, wenn eine Navigation zu einer anderen URL erfolgte. Für VS Code bedeutete dies, dass der Wechsel zu einem anderen Arbeitsbereich oder das Neuladen des Fensters den Renderer-Prozess neu erzeugte, was in einigen Umgebungen und Konfigurationen langsam sein kann.

Sandboxed Renderer-Prozesse bleiben auch bei URL-Navigationen aktiv. Das Öffnen eines anderen Arbeitsbereichs oder das Neuladen des aktuellen ist wesentlich schneller. Damit dies jedoch funktioniert, müssen native Node.js-Module, die im Renderer-Prozess laufen, kontextsensitiv gemacht werden. Obwohl wir letztendlich alle nativen Module aus dem Renderer-Prozess ausgelagert haben, um Sandboxing zu ermöglichen, wollten wir die Wiederverwendung von Renderer-Prozessen frühzeitig testen und haben daher alle unsere nativen Module kontextsensitiv gemacht.

Alles zusammenfügen

Der letzte Schritt war die bedingte Aktivierung des Sandbox-Modus über eine Benutzereinstellung Einstellung. Wir wollten den Sandbox-Modus nicht für alle unsere Benutzer aktivieren, sondern ihm stattdessen Zeit geben, in unserer Insiders-Edition validiert zu werden. Mit der Einstellung window.experimental.useSandbox ist die Sandbox in Insiders standardmäßig aktiviert und kann in Stable aktiviert werden.

Wir planen, unsere Experimentierinfrastruktur zu nutzen, um die Aktivierung der Sandbox Anfang 2023 schrittweise in unsere Stable-Edition zu rollen. Dies ermöglicht es uns, den Sandbox-Modus auf einer wachsenden Benutzerbasis zu testen und zu validieren, während wir auf Probleme prüfen.

Nach Abschluss der experimentellen Phase wird der Sandbox-Modus standardmäßig für alle Benutzer aktiviert sein und der nicht-sandboxed Modus wird entfernt. Für spätere Iterationen sind noch einige Arbeiten geplant, zum Beispiel wollen wir den Shared Process in einen Utility-Prozess umwandeln, da er ein verstecktes Fenster ist und mehr Ressourcen als nötig verbraucht.

Dies war eine erstaunliche Reise, die nur mit der Hilfe und Motivation des gesamten VS Code-Teams möglich war. Es war großartig zu sehen, dass wir diese Änderungen schrittweise ausliefern und auf neue Electron-Versionen vorbereitet sein konnten, die Prozess-Sandboxing erfordern. Wir konnten unsere Prozessarchitektur erheblich verbessern und uns stärker am Webmodell orientieren, was eine robuste Grundlage für die Zukunft schafft.

Verwendete Terminologie

Electron ist das Hauptframework, das es VS Code für Desktops ermöglicht, auf all unseren unterstützten Plattformen (Windows, macOS und Linux) zu laufen. Es kombiniert Chromium mit Browser-APIs, der V8 JavaScript-Engine und Node.js APIs sowie plattformspezifischen Integrations-APIs, um plattformübergreifende Desktop-Anwendungen zu erstellen.

In diesem Blogbeitrag bezeichnen wir Electron Prozess-Sandboxing einfach als "Sandbox".

Es ist wichtig, das Prozessmodell zu verstehen, das Chromium und damit Electron bereitstellt. In diesem Blogbeitrag beziehen wir uns häufig auf die folgenden Prozesse:

  • main process - Der Haupteinstiegspunkt der Anwendung.
  • renderer process - Fenster, mit denen der Benutzer interagieren kann.

Während es immer nur einen Hauptprozess gibt, werden Renderer-Prozesse pro geöffnetem Fenster erstellt. Sie können mehr über das Prozessmodell in der Electron Prozessmodell-Dokumentation und diesem Chrome Developers Blogbeitrag erfahren.

Der "Shared Process" ist nicht spezifisch für Electron, sondern ein Implementierungsdetail von VS Code. Es handelt sich um ein verstecktes Electron-Fenster mit aktivierter Node.js, mit dem alle anderen Fenster kommunizieren können, um komplexe Aufgaben wie die Installation von Erweiterungen durchzuführen.

Der "Extension Host" ist ein Prozess, der alle installierten Erweiterungen isoliert vom Renderer-Prozess ausführt. Es gibt einen Extension Host pro geöffnetem Fenster.

Das VS Code "Workbench"-Fenster ist das Hauptfenster, mit dem Benutzer interagieren, um Dateien zu bearbeiten, zu suchen oder zu debuggen. In diesem Blogbeitrag bezeichnen wir es einfach als "Workbench". Die anderen Fenster sind Process Explorer und Issue Reporter, die über das Menü Hilfe aufgerufen werden können.

Wir verwenden den Begriff "IPC", um Interprozesskommunikation zu bezeichnen. IPC ist eine Methode, mit der ein Prozess mit einem anderen Prozess kommunizieren kann.

Wir veröffentlichen eine nächtliche Version von VS Code namens "Insiders", um die neuesten Änderungen an einer Teilmenge von Benutzern zu testen. Alle Mitglieder des VS Code-Teams verwenden die Insiders-Edition, und wir hoffen, dass Sie sie ebenfalls ausprobieren und uns Probleme melden.

Viel Spaß beim Programmieren!

Benjamin Pasero, @BenjaminPasero

© . This site is unofficial and not affiliated with Microsoft.