UNPKG

hakojs

Version:

A secure, embeddable JavaScript engine that runs untrusted code inside WebAssembly sandboxes with fine-grained permissions and resource limits

118 lines (116 loc) 4.05 kB
// src/index.ts import { useClock, useRandom, useStdio, WASI } from "uwasi"; import { HakoError } from "./etc/errors"; import { CallbackManager } from "./host/callback"; import { Container } from "./host/container"; import { HakoRuntime } from "./host/runtime"; import { MemoryManager } from "./mem/memory"; import { default as default2 } from "./variants/hako.g"; import { default as default3 } from "./variants/hako-debug.g"; var defaultInitialMemory = 25165824; var defaultMaximumMemory = 268435456; async function createHakoRuntime(options) { const memConfig = options.wasm?.memory || {}; const initialMemory = memConfig.initial || defaultInitialMemory; const maximumMemory = memConfig.maximum || defaultMaximumMemory; let wasmMemory; if (memConfig.byom) { wasmMemory = memConfig.byom; if (wasmMemory.buffer instanceof SharedArrayBuffer) { throw new HakoError("Hako memory cannot be a SharedArrayBuffer. Use a regular ArrayBuffer instead."); } } else { const initialPages = Math.ceil(initialMemory / 65536); const maximumPages = Math.ceil(maximumMemory / 65536); wasmMemory = new WebAssembly.Memory({ initial: initialPages, maximum: maximumPages, shared: false }); } const memory = new MemoryManager; const callbacks = new CallbackManager(memory); const wasi = new WASI({ features: [ useStdio({ stdout: options.wasm?.io?.stdout || ((lines) => console.log(lines)), stderr: options.wasm?.io?.stderr || ((lines) => console.error(lines)) }), useClock, useRandom({ randomFillSync: (buffer) => { return crypto.getRandomValues(buffer); } }) ], args: options.wasm?.args || [], env: options.wasm?.env || {}, preopens: options.wasm?.preopens || {} }); const imports = { wasi_snapshot_preview1: wasi.wasiImport, env: { memory: wasmMemory }, ...callbacks.getImports() }; let instance; if (options.loader.binary) { const result = await WebAssembly.instantiate(options.loader.binary, imports); instance = result.instance; } else if (options.loader.fetch && options.loader.src) { const result = await WebAssembly.instantiateStreaming(options.loader.fetch(options.loader.src), imports); instance = result.instance; } else { throw new HakoError("No WebAssembly binary provided"); } wasi.initialize(instance); const exports = instance.exports; const container = new Container(exports, memory, callbacks); const rtPtr = container.exports.HAKO_NewRuntime(); if (rtPtr === 0) { throw new HakoError("Failed to create runtime"); } const runtime = new HakoRuntime(container, rtPtr); if (options.runtime?.interruptHandler) { runtime.enableInterruptHandler(options.runtime.interruptHandler); } if (options.runtime?.memoryLimit) { runtime.setMemoryLimit(options.runtime.memoryLimit); } return runtime; } var decodeVariant = (encoded) => { let module; if (typeof Uint8Array.fromBase64 === "function") { module = Uint8Array.fromBase64(encoded, { lastChunkHandling: "strict" }); } else { const decoded = atob(encoded); module = new Uint8Array(decoded.length); for (let i = 0;i < decoded.length; i++) { module[i] = decoded.charCodeAt(i); } } if (module.length < 8) { throw new HakoError("Buffer too small to be a valid WebAssembly module"); } const isMagicValid = module[0] === 0 && module[1] === 97 && module[2] === 115 && module[3] === 109; if (!isMagicValid) { throw new HakoError("Invalid WebAssembly module"); } const isVersionValid = module[4] === 1 && module[5] === 0 && module[6] === 0 && module[7] === 0; if (!isVersionValid) { throw new HakoError(`Unsupported WebAssembly version: ${module[4]}.${module[5]}.${module[6]}.${module[7]}`); } return module; }; export { decodeVariant, createHakoRuntime, default2 as HAKO_PROD, default3 as HAKO_DEBUG }; //# debugId=7B89BBB074C8C5AC64756E2164756E21 //# sourceMappingURL=index.js.map