simple-sandbox
Version:
A simple sandbox for Node.js using Linux namespaces and cgroup.
117 lines • 5.1 kB
JavaScript
;
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