
Research
Malicious npm Packages Impersonate Flashbots SDKs, Targeting Ethereum Wallet Credentials
Four npm packages disguised as cryptographic tools steal developer credentials and send them to attacker-controlled Telegram infrastructure.
A QuickJS runtime wrapper for Node.js that allows you to run untrusted ES6 code in a secure, sandboxed environment. Doesn't use WASM, compiles to a native module with Rust.
Installation of the Rust Programming Language is required to use this module.
Features include:
eval
interface.npm install quickjs-vm
import { QJSWorker } from "quickjs-vm";
const runtime = QJSWorker({
console: console, // Forward QuickJS logs to Node console
maxEvalMs: 1500, // Limit execution time for each eval
maxMemoryBytes: 256 * 1_000_000, // Limit runtime memory
maxStackSizeBytes: 10 * 1_000_000, // Limit stack size
globals: {
a_boolean: true,
addTwo: (a, b) => a + b,
},
});
(async () => {
const [result] = await runtime.eval("addTwo(2, 3)");
console.log(result); // 5
await runtime.close();
})();
Below is an example usage showing various features of quickjs-vm:
import { QJSWorker } from "quickjs-vm";
const runtime = QJSWorker({
console: {
log: (...args) => {
// capture console.log commands from the runtime
},
},
// Alternatively, just provide Node console to pass them through
// console: console,
maxEvalMs: 1500, // limit execution time of each eval call
maxMemoryBytes: 256 * 1000 * 1000, // limit runtime memory
maxStackSizeBytes: 10 * 1000 * 1000, // limit stack size
maxInterrupt: 10000, // prevent while(true) or similar lockups
// inject globals into the runtime
// supports primitives, JSON, arrays, and sync functions
globals: {
a_boolean: true,
addTwo: (a, b) => a + b,
json_value: {
state: "Texas",
},
array_value: [
{ nested: "object" },
{ foo: "bar" },
],
// Handle require() calls
require: (moduleName) => {
/* ... */
},
},
});
(async () => {
const [evalResult, evalStats] = await runtime.eval("2 + 2");
console.assert(evalResult == 4);
const [evalResultSync] = runtime.evalSync("2 + 3");
console.assert(evalResultSync == 5);
const [addTwoResult] = await runtime.eval("addTwo(2, 3)");
console.assert(addTwoResult == 5);
// args can be passed directly
const [addTwoResultWithArgs] = await runtime.eval(
"addTwo", // function name to call in the sandbox
"script.js", // optional script name
6, // arg1
6 // arg2
);
console.assert(addTwoResultWithArgs == 12);
// return nested properties
const [jsonValue] = await runtime.eval("json_value.state");
console.assert(jsonValue == "Texas");
// send a message to node from quickjs
let receivedMessage;
runtime.on("message", (msg) => {
receivedMessage = msg;
});
await runtime.eval(`postMessage({hello: "from QuickJS"})`);
console.assert(receivedMessage.hello == "from QuickJS");
// send a message to quickjs from node
await runtime.eval(`
let messageFromNode;
on('message', (msg) => { messageFromNode = msg; })
`);
await runtime.postMessage({ hello: "from Node" });
const [message] = await runtime.eval("messageFromNode");
console.assert(message.hello == "from Node");
// Promises are resolved before returning their internal value.
const [promise] = await runtime.eval("Promise.resolve({hello: 'world'})");
console.assert(promise.hello == "world");
// even nested promises are resolved
const [nestedPromise] = await runtime.eval(
"Promise.resolve(Promise.resolve({hello: 'world'}))"
);
console.assert(nestedPromise.hello == "world");
// get memory stats
const memStatus = await runtime.memory();
// shows number of bytes currently being used
console.log(memStats.memory_used_size);
// force garbage collector to run
await runtime.gc();
// bytecode, provides a Uint8Array
const byteCode = await runtime.getByteCode(
`const test = (a, b) => Promise.resolve(a+b)`
);
const runtime2 = QJSWorker();
await runtime2.loadByteCode(byteCode);
const [byteCodeFnResult] = await runtime2.eval("test(1, 2)");
console.assert(byteCodeFnResult == 3);
// make sure to close your runtimes or the node process will hang
await runtime.close();
await runtime2.close();
})();
const [evalResult, evalStats] = await runtime.eval("2 + 2");
console.assert(evalResult === 4);
console.log(evalStats);
const [result] = await runtime.eval(
"addTwo", // function name to call in the sandbox
"script.js", // optional script name
6, // arg1
6 // arg2
);
console.assert(result === 12);
const [promiseResult] = await runtime.eval("Promise.resolve({ hello: 'world' })");
console.assert(promiseResult.hello === "world");
From QuickJS to Node:
runtime.on("message", (msg) => {
console.log("Message from QuickJS:", msg);
});
await runtime.eval(`postMessage({ hello: "from QuickJS" })`);
And from Node to QuickJS:
await runtime.eval(`
let messageFromNode;
on('message', (msg) => { messageFromNode = msg; });
`);
await runtime.postMessage({ hello: "from Node" });
const [msg] = await runtime.eval("messageFromNode");
console.assert(msg.hello === "from Node");
const [nestedPromise] = await runtime.eval(
"Promise.resolve(Promise.resolve({ hello: 'world' }))"
);
console.assert(nestedPromise.hello === "world");
// Retrieve memory usage stats
const memStats = await runtime.memory();
// shows number of bytes currently being used
console.log(memStats.memory_used_size);
// Force garbage collection
await runtime.gc();
const byteCode = await runtime.getByteCode(`const test = (a, b) => a + b;`);
const runtime2 = QJSWorker();
await runtime2.loadByteCode(byteCode);
const [byteCodeFnResult] = await runtime2.eval("test(1, 2)");
console.assert(byteCodeFnResult === 3);
await runtime2.close();
QJSWorker(options)
Creates a new QuickJS runtime instance in its own thread/memory space.
Options (all optional):
console (object)
An object with methods like log, warn, error. Defaults to a no-op if not provided.
maxEvalMs (number)
Maximum evaluation time in milliseconds per call to eval.
maxMemoryBytes (number)
Maximum memory usage for the runtime (approximate).
maxStackSizeBytes (number)
Maximum stack size for the runtime (approximate).
maxInterrupt (number)
Interrupt count limit to break out of infinite loops.
globals (object)
Key-value pairs to inject into the global scope of QuickJS. Supports primitives, arrays, JSON objects, and sync functions.
globals: {
a_boolean: true,
addTwo: (a, b) => a + b,
json_value: { state: "Texas" },
require: (moduleName) => { /* custom require logic */ },
}
eval(code: string, fileName?: string, ...args: any[]): Promise<[any, EvalStats?]>
Evaluate JavaScript code asynchronously. Returns a Promise that resolves with [result, evalStats]
.
evalSync(code: string, fileName?: string, ...args: any[]): [any, EvalStats?]
Synchronously evaluate JavaScript code. Blocks the Node.js event loop until completion.
on(eventName: string, callback: (msg: any) => void): void
Subscribe to a named event from the QuickJS environment (e.g., "message").
postMessage(message: any): Promise<void>
Send a message to the QuickJS environment, triggering any on('message') listeners inside the sandbox.
memory(): Promise<MemoryStats>
Retrieve memory usage statistics from the QuickJS runtime.
gc(): Promise<void>
Trigger garbage collection in the QuickJS runtime.
getByteCode(code: string): Promise<Uint8Array>
Compile code into QuickJS bytecode and return it.
loadByteCode(byteCode: Uint8Array): Promise<void>
Load previously exported QuickJS bytecode into the runtime.\
close(): Promise<void>
Destroy the QuickJS runtime instance and free all resources.
Contributions are welcome! Here’s how you can help:
For major changes, please open an issue first to discuss your proposal.
This project is licensed under the MIT License. See the LICENSE file for more details.
Thanks for using QuickJS-VM! If you find it helpful, consider giving it a star on GitHub.
FAQs
Run a QuickJS worker from NodeJS
The npm package quickjs-vm receives a total of 0 weekly downloads. As such, quickjs-vm popularity was classified as not popular.
We found that quickjs-vm demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Four npm packages disguised as cryptographic tools steal developer credentials and send them to attacker-controlled Telegram infrastructure.
Security News
Ruby maintainers from Bundler and rbenv teams are building rv to bring Python uv's speed and unified tooling approach to Ruby development.
Security News
Following last week’s supply chain attack, Nx published findings on the GitHub Actions exploit and moved npm publishing to Trusted Publishers.