kern-sandbox 0.1.15 → 0.1.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/index.js +21 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -99,7 +99,10 @@ A non-zero exit from *your code* is **not** a fault (`fault` stays `null`): it i
|
|
|
99
99
|
| `timeout` | the call exceeded `timeoutS`; the binding killed the box |
|
|
100
100
|
| `escape_blocked` | a syscall was blocked by the seccomp filter (SIGSYS) |
|
|
101
101
|
| `killed` | the box was SIGKILLed, most often the cgroup OOM-killer |
|
|
102
|
-
|
|
102
|
+
|
|
103
|
+
A box that fails to **start** (kern exits 125: a mount refused at runtime, an unmappable `--user`, a
|
|
104
|
+
seccomp/AppArmor/cgroup setup error, or a pull/image error) is **thrown** as a `SandboxError`, not
|
|
105
|
+
returned as a fault, because the code never ran.
|
|
103
106
|
|
|
104
107
|
```js
|
|
105
108
|
const r = await kern.runCode("while True: pass", { timeoutS: 5 });
|
package/index.js
CHANGED
|
@@ -36,7 +36,7 @@ const crypto = require("crypto");
|
|
|
36
36
|
const zlib = require("zlib");
|
|
37
37
|
const { spawn, spawnSync } = require("child_process");
|
|
38
38
|
|
|
39
|
-
const VERSION = "0.1.
|
|
39
|
+
const VERSION = "0.1.16";
|
|
40
40
|
|
|
41
41
|
const DEFAULT_IMAGE = "python:3.12-slim";
|
|
42
42
|
const WORKSPACE = "/workspace"; // where the persistent workspace is mounted inside every box
|
|
@@ -387,8 +387,11 @@ const REFUSED_MOUNT_SOURCES = new Set([
|
|
|
387
387
|
"/run/docker.sock",
|
|
388
388
|
]);
|
|
389
389
|
|
|
390
|
-
/** A PROGRAMMER/config error, THROWN: bad argument, illegal mount,
|
|
391
|
-
*
|
|
390
|
+
/** A PROGRAMMER/config error, THROWN: bad argument, illegal mount, `kern` not installed, or the box
|
|
391
|
+
* FAILED TO START (kern exits 125 - a mount refused at runtime, an unmappable `--user`, a seccomp or
|
|
392
|
+
* AppArmor setup error). A box that never started ran no user code, so it rejects rather than resolve a
|
|
393
|
+
* hollow result. Runtime sandbox events where the code DID run (timeout, blocked escape, OOM-kill) are
|
|
394
|
+
* NOT thrown - they are data on `result.fault`. */
|
|
392
395
|
class SandboxError extends Error {
|
|
393
396
|
constructor(message) {
|
|
394
397
|
super(message);
|
|
@@ -1057,6 +1060,13 @@ class Sandbox {
|
|
|
1057
1060
|
const stderr = err.buffer().toString("utf8");
|
|
1058
1061
|
const rc = toRc(code, signal);
|
|
1059
1062
|
const fault = this._classify(rc, signal, stderr, timedOut, timeoutS);
|
|
1063
|
+
// A box that FAILED TO START (kern exits 125) ran no user code, so REJECT rather than resolve a
|
|
1064
|
+
// hollow ExecutionResult (empty stdout, exit 125). Mirrors the mount/config errors this SDK
|
|
1065
|
+
// already throws up front; runtime events where the code DID run (timeout, OOM, escape) stay as
|
|
1066
|
+
// data on `.fault`.
|
|
1067
|
+
if (fault && fault.type === "startup_failed") {
|
|
1068
|
+
return reject(new SandboxError(fault.message || "the box failed to start"));
|
|
1069
|
+
}
|
|
1060
1070
|
const files = before ? this._diff(before) : [];
|
|
1061
1071
|
resolve(
|
|
1062
1072
|
new ExecutionResult({
|
|
@@ -1129,6 +1139,11 @@ class Sandbox {
|
|
|
1129
1139
|
return sandboxFault("killed", "the box was killed (SIGKILL) - likely out of memory (exit 137)");
|
|
1130
1140
|
if (rc === EXIT_SIGTERM || signal === "SIGTERM")
|
|
1131
1141
|
return sandboxFault("timeout", "the box exceeded its time limit (reaped by kern's timeout backstop)");
|
|
1142
|
+
// DETERMINISTIC box-not-started: kern exits 125 (Docker's convention) when it could not BUILD the
|
|
1143
|
+
// box (a mount refused at runtime, an unmappable --user, a seccomp/AppArmor/cgroup setup error), so
|
|
1144
|
+
// the workload never ran. Decided by exit code, no stderr marker needed; the caller REJECTS on it.
|
|
1145
|
+
if (rc === 125) return sandboxFault("startup_failed", stderr.trim().slice(0, 500) || "the box failed to start");
|
|
1146
|
+
// LAST resort heuristic (also catches an OLDER kern that still exits 127 for a setup failure).
|
|
1132
1147
|
if (rc !== 0 && looksLikeStartupFailure(stderr))
|
|
1133
1148
|
return sandboxFault("startup_failed", stderr.trim().slice(0, 500));
|
|
1134
1149
|
// Any other non-zero exit (incl. 139 SIGSEGV) is the USER's code failing - a normal Result.
|
|
@@ -1752,6 +1767,9 @@ class Kernel {
|
|
|
1752
1767
|
|
|
1753
1768
|
_teardownResult(type, message, started) {
|
|
1754
1769
|
this._kill();
|
|
1770
|
+
// Same rule as the one-shot path: a box that never STARTED (the kernel failed to boot) throws, it
|
|
1771
|
+
// does not return a hollow result. timeout/killed stay as data on the returned result.
|
|
1772
|
+
if (type === "startup_failed") throw new SandboxError(message || "the box failed to start");
|
|
1755
1773
|
return new ExecutionResult({
|
|
1756
1774
|
stdout: "",
|
|
1757
1775
|
stderr: "",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kern-sandbox",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.16",
|
|
4
4
|
"description": "kern is a fast, rootless sandbox and virtual resource runtime for any workload, including untrusted and AI-generated code; kern-sandbox is its Node/TypeScript binding. Run untrusted or agent-generated code (Python/JS/Bash) in a real, kernel-enforced box in single-digit milliseconds, with no cloud, no account and no VM.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sandbox",
|