Execute JavaScript in a WebAssembly QuickJS Sandbox

sebastian_wessel

Sebastian Wessel

Posted on July 7, 2024

Execute JavaScript in a WebAssembly QuickJS Sandbox

Typescript QuickJS Package: Empowering Secure JavaScript Execution in AI and Beyond

As an experienced developer who understands the needs of the community, I've created the QuickJS package to address a critical challenge in modern software development: executing JavaScript securely while maintaining flexibility and performance across various platforms. This TypeScript library offers a robust solution for running JavaScript code in a secure sandbox environment, with particular benefits for AI and large language model (LLM) applications.

The Genesis of the QuickJS Package

My journey began with QuickJS, an impressive small and efficient JavaScript engine originally written in C by Fabrice Bellard and Charlie Gordon. While QuickJS itself is a C program, I saw an opportunity to make it more accessible to the JavaScript and TypeScript community.

The foundation of my package is built upon the excellent work done by the quickjs-emscripten project (https://github.com/justjake/quickjs-emscripten). They've done the crucial work of compiling QuickJS to WebAssembly, enabling its use in web and Node.js environments. What I've created is a high-level abstraction layer around this WebAssembly implementation, designed with developer experience in mind.

A Sandbox Built for Developers, by a Developer

As someone who's been in the trenches of software development, I understand the importance of a tool that's not only powerful but also easy to use. The QuickJS package creates an isolated environment where untrusted code can run freely, separated from your main application, whether it's running in a browser or on a server.

Features Designed with You in Mind

When developing this package, I focused on bringing together features that I, as a developer, would want:

  1. Node-like Module Support: I've included support for core modules similar to Node.js, including node:fs, node:assert, node:util, and node:path.
  2. Fetch API: Because making HTTP requests is such a common need, I've made sure fetch is available within the sandbox.
  3. Custom Module Support: Knowing that every project has unique needs, I've made it possible to use your own modules.
  4. Virtual File System: This allows for file operations without risking your actual file system - a must-have for secure environments.
  5. TypeScript Compatibility: As a TypeScript enthusiast, ensuring seamless integration with TypeScript projects was a priority.
  6. User-Friendly API: I've put a lot of effort into making the API intuitive and easy to use.
  7. Integrated TestRunner: Testing is crucial, so I've included a lightweight testing library right in the sandbox.

The QuickJS Package in Action

Here's a quick example of how you can use the package:

import { quickJS } from '@sebastianwessel/quickjs'

// General setup - do this once
const { createRuntime } = await quickJS()

// Create a runtime instance for code execution
const { evalCode } = await createRuntime({
  allowFetch: true,
  allowFs: true,
  env: {
    MY_ENV_VAR: 'env var value'
  },
})

// Execute code in the sandbox
const result = await evalCode(`
  import { join } from 'node:path'
  import { writeFileSync, readFileSync } from 'node:fs'
  import assert from 'node:assert'

  const fn = async () => {
    console.log(join('src','dist'))
    console.log(env.MY_ENV_VAR)

    // Using node:fs
    writeFileSync('/test.txt', 'Hello, QuickJS!')
    const content = readFileSync('/test.txt', 'utf8')
    assert.strictEqual(content, 'Hello, QuickJS!')

    // Using fetch
    const url = new URL('https://example.com')
    const f = await fetch(url)
    return f.text()
  }

  export default await fn()
`)

console.log(result)
Enter fullscreen mode Exit fullscreen mode

Leveraging Modern Tools for a Better Developer Experience

In developing this package, I've leveraged some of the most exciting tools in the JavaScript ecosystem:

  • Bun: This all-in-one JavaScript runtime has been instrumental in the development process, offering speed and simplicity.
  • Hono: For handling HTTP requests, Hono has been a game-changer with its lightweight and performant approach.
  • Biome: This toolchain has helped maintain code quality and consistency throughout the project.

I've also used other modern tools like poolifier-web-worker for efficient multithreading, tshy for TypeScript package building, and autocannon for performance testing.

Real-World Applications

From my experience, I've seen this package shine in various scenarios:

  • Running user-provided scripts safely in web or desktop applications
  • Executing plugins or extensions securely
  • Creating sandboxed environments for coding challenges or educational platforms
  • Testing potentially unsafe code snippets in development environments
  • Implementing secure scripting capabilities in backend services
  • Powering AI-driven code generation and execution in LLM applications

Performance and Security: No Compromises

As a developer, I know the importance of both performance and security. That's why I've ensured that this package, leveraging the lightweight QuickJS engine compiled to WebAssembly, offers excellent performance without sacrificing security.

Getting Started

I've made sure that getting started with the QuickJS package is straightforward:

npm install @sebastianwessel/quickjs
Enter fullscreen mode Exit fullscreen mode

For more detailed documentation and examples, check out the official documentation and the example repository.


Documentation: https://sebastianwessel.github.io/quickjs/
GitHub Repository: https://github.com/sebastianwessel/quickjs
Submit Feedback: Create an Issue on GitHub

💖 💪 🙅 🚩
sebastian_wessel
Sebastian Wessel

Posted on July 7, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related