npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

modbox

v0.1.7

Published

Securely box untrusted JS modules

Readme

modbox

Securely box untrusted JS modules, with CPU and memory limiting


modbox lets you run untrusted JavaScript by enclosing modules in a "box". It was made with JS smart contracts in mind, but can also be used anywhere you want to securely run someone else's code.

WARNING: This module is experimental and should not yet be used in production!

Features:

  • CPU usage limiting ("gas metering" in Ethereum terminology)
  • Memory limiting
  • Modules are only allowed to access the data or APIs you explicitly give them
  • Fully deterministic - code will always execute the same way no matter what
  • Runs in-process - boxes are cheap to create
  • Interact with boxed modules as you would any other module (e.g. call its exported functions)
  • Powered by @Agoric's SES (Secure EcmaScript)

Usage

npm install modbox

let modbox = require('modbox')

let box = modbox(`
  module.exports = {
    n: 0,

    increment () {
      this.n += 1
    }
  }
`)

console.log(box.n) // 0
box.increment()
console.log(box.n) // 1
box.n++ // throws - box's state is read-only

let box = modbox(code, [opts])

Creates a boxed module by evaluating the code string passed in as the code argument. The code should export a module by setting it as module.exports.

opts can be an options object containing:

  • allowConsole (default: false) - Set to true to expose console.log inside the box.
  • globals (default: {}) - An object which contains global variables that will be accessible from inside the box.
  • computeLimit (default: 10,000,000) - A number representing the amount of compute units to allow the boxed module to use. This is a rough unit, see the CPU and Memory Limit Caveats section for more info.
  • memoryLimit (default: 10,000,000) - A number representing the amount of memory units to allow the boxed module to use. This is a rough unit (it is NOT measured in bytes), see the CPU and Memory Limit Caveats section for more info.
  • onBurn(value) - A function which gets called continuously as the module executes code (both when initially created, and when its exported functions get called). value will be the result of each JS expression (e.g. for 5 + 5, onBurn will be called 3 times - twice with value = 5, and once with value = 10). If this throws, the module will be terminated and further execution will be prevented.

Execution Caveats

To prevent DoS attacks and non-determinism, this module is pretty conservative about what features are accessible from inside the box. The following are currently not allowed:

  • require - instead, pass in other modules through the globals option
  • JSON (e.g. JSON.stringify(...))
  • Regular expressions
  • Buffer
  • Date.now() (returns NaN)
  • new Date() (returns new Date(NaN), still works if you pass in a timestamp)
  • setTimeout()/setInterval()/setImmediate()
  • Math.random() (throws)
  • eval() (not defined)
  • The Function constructor
  • Typed arrays (Uint8Array, etc.)

Some of these, e.g. Buffer and a limited form of require will likely be enabled in the future.

CPU and Memory Limit Caveats

CPU

CPU usage limiting works by counting up as the code executes (both in the initial eval that happens when the module is created, and when the module's functions get called from outside the box), then halting all execution once the limit is reached. This is the same as "gas" in the Ethereum world.

The metering is done by transpiling the module's source code to add calls to a "burn handler" function for every expression. The resulting code is functionally equivalent, but now calls out to this burn handler as it executes in order to measure CPU resource and memory usage.

Example:

Original code:

module.exports = {
  n: 0,

  increment () {
    this.n += 1
  }
}

Instrumented output:

burn(burn(module).exports = burn({
  n: burn(0),

  increment() {
    burn()

    burn(burn(this).n += burn(1));
  }
}));

In the resulting code, the initial module creation burns a small amount of gas, then each subsequent call to increment() burns some more. (In the real code, the name of burn is actually a random string to prevent shadowing or name conflicts).

Memory

Memory usage measurement is rough and errs on the side of counting too high in order to minimize memory exhaustion attacks. Also, it counts up monotonically so usage is counted even after it is freed by garbage collection (this is because the module is intended for use with smart contracts where the box is created, run, and thrown away too quickly for GC to kick in).

Since we don't know what optimizations the JS engine is doing, we simply count from a high-level, e.g. arrays and strings are counted based on their length, objects are counted based on the number of keys, etc.

This will likely improve over time, but our targeted smart-contract use case usually doesn't require a lot of memory and the goal is simply to prevent crashing the JS engine by exhausting its memory.