UNPKG

simple-sandbox

Version:

A simple sandbox for Node.js using Linux namespaces and cgroup.

117 lines 5.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SandboxProcess = void 0; const interfaces_1 = require("./interfaces"); const nativeAddon_1 = require("./nativeAddon"); const utils = require("./utils"); class SandboxProcess { constructor(parameter, pid, execParam) { this.parameter = parameter; this.pid = pid; this.cancellationToken = null; this.countedCpuTime = 0; this.actualCpuTime = 0; this.timeout = false; this.cancelled = false; this.waitPromise = null; this.running = true; const myFather = this; this.stopCallback = () => { myFather.stop(); }; let checkIfTimedOut = () => { }; if (this.parameter.time !== -1) { const checkInterval = Math.min(this.parameter.time / 10, 50); let lastCheck = new Date().getTime(); checkIfTimedOut = () => { let current = new Date().getTime(); const spent = current - lastCheck; lastCheck = current; const val = Number(nativeAddon_1.default.getCgroupProperty("cpuacct", myFather.parameter.cgroup, "cpuacct.usage")); myFather.countedCpuTime += Math.max(val - myFather.actualCpuTime, utils.milliToNano(spent) * 0.4); myFather.actualCpuTime = val; if (myFather.countedCpuTime > utils.milliToNano(parameter.time)) { myFather.timeout = true; myFather.stop(); } }; this.cancellationToken = setInterval(checkIfTimedOut, checkInterval); } this.waitPromise = new Promise((res, rej) => { nativeAddon_1.default.waitForProcess(pid, execParam, (err, runResult) => { if (err) { try { myFather.stop(); myFather.cleanup(); } catch (e) { console.log("Error cleaning up error sandbox:", e); } rej(err); } else { try { const memUsageWithCache = Number(nativeAddon_1.default.getCgroupProperty("memory", myFather.parameter.cgroup, "memory.memsw.max_usage_in_bytes")); const cache = Number(nativeAddon_1.default.getCgroupProperty2("memory", myFather.parameter.cgroup, "memory.stat", "cache")); const memUsage = memUsageWithCache - cache; myFather.actualCpuTime = Number(nativeAddon_1.default.getCgroupProperty("cpuacct", myFather.parameter.cgroup, "cpuacct.usage")); myFather.cleanup(); const result = { status: interfaces_1.SandboxStatus.Unknown, time: myFather.actualCpuTime, memory: memUsage, code: runResult.code }; if (myFather.timeout || myFather.actualCpuTime > utils.milliToNano(myFather.parameter.time)) { result.status = interfaces_1.SandboxStatus.TimeLimitExceeded; } else if (myFather.cancelled) { result.status = interfaces_1.SandboxStatus.Cancelled; } else if (myFather.parameter.memory != -1 && memUsage > myFather.parameter.memory) { result.status = interfaces_1.SandboxStatus.MemoryLimitExceeded; } else if (runResult.status === 'signaled') { result.status = interfaces_1.SandboxStatus.RuntimeError; } else if (runResult.status === 'exited') { result.status = interfaces_1.SandboxStatus.OK; } res(result); } catch (e) { rej(e); } } }); }); } removeCgroup() { nativeAddon_1.default.removeCgroup("memory", this.parameter.cgroup); nativeAddon_1.default.removeCgroup("cpuacct", this.parameter.cgroup); nativeAddon_1.default.removeCgroup("pids", this.parameter.cgroup); } cleanup() { if (this.running) { if (this.cancellationToken) { clearInterval(this.cancellationToken); } process.removeListener('exit', this.stopCallback); this.removeCgroup(); this.running = false; } } stop() { this.cancelled = true; try { process.kill(this.pid, "SIGKILL"); } catch (err) { } } async waitForStop() { return await this.waitPromise; } } exports.SandboxProcess = SandboxProcess; ; //# sourceMappingURL=sandboxProcess.js.map