figma

How to build a plugin system on the web and also sleep well at night | Figma Blog (opens in new tab)

Figma’s plugin system had to let untrusted third-party JavaScript interact with a powerful, browser-based design editor without compromising security, performance, or stability. The team evaluated several isolation strategies, ultimately favoring JavaScript Realms because they supported synchronous APIs and avoided the performance costs of a full interpreter. Figma later replaced that implementation with a JavaScript VM compiled to WebAssembly after a vulnerability was found in the third-party Realms shim.

Why Plugin Isolation Was Difficult

  • Plugins needed access to Figma’s document model while remaining isolated from:
    • User data and credentials
    • Figma’s internal application state
    • Other plugins
    • The host page and browser APIs
  • Simply calling eval(PLUGIN_CODE) would execute arbitrary code in Figma’s main environment.
  • Figma’s architecture added constraints:
    • The editor relied heavily on WebGL and WebAssembly.
    • Parts of the interface used TypeScript and React.
    • Multiple users could edit files simultaneously.
  • Plugins also needed to remain performant and avoid breaking as Figma evolved.

Attempt 1: The <iframe> Sandbox

  • The team first considered the browser’s standard isolation mechanism: sandboxed <iframe> elements.
  • An iframe could separate plugin code from Figma’s main page and restrict access using browser security policies.
  • Communication between the plugin and Figma would use mechanisms such as postMessage.
  • However, this created an important limitation: iframe communication is asynchronous.
  • Figma’s plugin API needed synchronous access to document operations, making an iframe-based architecture awkward and potentially expensive.
  • The iframe approach also introduced additional browser contexts and messaging overhead.

Attempt 2: A JavaScript Interpreter Compiled to WebAssembly

  • The second approach was to run plugin code inside a JavaScript interpreter rather than the browser’s native JavaScript engine.
  • The interpreter could expose only explicitly approved Figma APIs, providing a strong security boundary.
  • Compiling the interpreter to WebAssembly offered a way to integrate it efficiently with Figma’s existing WebAssembly-heavy architecture.
  • The drawbacks included:
    • Interpreted JavaScript would be slower than native execution.
    • The interpreter would require ongoing maintenance and compatibility work.
    • Supporting the full JavaScript language and modern features would be difficult.
  • Although attractive from a security perspective, this approach appeared to impose too much performance and implementation cost at the time.

Attempt 3: JavaScript Realms

  • Realms provided a separate JavaScript global environment within the same browser process.
  • Figma could execute plugin code in a distinct Realm while exposing a carefully controlled plugin API.
  • Unlike iframes, Realms allowed plugin calls to remain synchronous.
  • Unlike a custom interpreter, plugin code could use the browser’s native JavaScript engine.
  • The implementation required carefully controlling built-in objects and preventing plugins from escaping their isolated environment.
  • This approach offered the best balance of:
    • Native JavaScript performance
    • Synchronous API access
    • Isolation from Figma’s application state
    • A relatively small integration surface

Later Security Change

  • After publication, Figma discovered a security vulnerability in the third-party Realms shim used by its original implementation.
  • The vulnerability was fixed before public disclosure, and Figma reported no evidence that it had been exploited.
  • Figma subsequently changed its sandbox to use a JavaScript VM written in C and compiled to WebAssembly.

Figma’s experience shows that plugin systems require more than simply restricting access to browser APIs. The isolation boundary must also preserve performance and API usability, while being robust enough to withstand vulnerabilities in the underlying sandbox technology.