kern-sandbox 0.2.8 → 0.2.9
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 +1 -1
- package/index.js +28 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -129,7 +129,7 @@ A non-zero exit from *your code* is **not** a fault (`fault` stays `null`): it i
|
|
|
129
129
|
| `oom` | the kernel's OOM killer took the box against its own memory cap. Read from a descriptor the code in the box cannot write, so it is an observation and not a guess from the exit code |
|
|
130
130
|
| `killed` | SIGKILL with **no** OOM reported: an external kill (`kern stop`, a signal, the host out of memory), or a cap that did not bind here, which the message names |
|
|
131
131
|
| `exec_failed` | the box started, the command did not exist inside it. `{language:"node"}` on an image with no `node` is the ordinary way there; the message names the binary AND the image |
|
|
132
|
-
| `startup_failed` | your `timeoutS` fired while kern was still BUILDING the box,
|
|
132
|
+
| `startup_failed` | the box never ran, and kern said why in `stderr`. Two shapes: your `timeoutS` fired while kern was still BUILDING the box (run it again: a fast second call was a cold image read), or kern refused to build it at all (an image that cannot be pulled, a mount it will not make) |
|
|
133
133
|
|
|
134
134
|
```js
|
|
135
135
|
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.2.
|
|
39
|
+
const VERSION = "0.2.9";
|
|
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
|
|
@@ -446,6 +446,26 @@ const REFUSED_MOUNT_SOURCES = new Set([
|
|
|
446
446
|
"/run/docker.sock",
|
|
447
447
|
]);
|
|
448
448
|
|
|
449
|
+
/** Credential directories, refused as a COMPONENT anywhere in the source. The set above is absolute
|
|
450
|
+
* paths, so it refused `$HOME` and accepted `$HOME/.ssh`: MEASURED, a box mounted with `~/.ssh` listed
|
|
451
|
+
* `id_ed25519` and `authorized_keys`. Refusing the parent and allowing its most sensitive child is the
|
|
452
|
+
* wrong way round, and it is the scenario a prompt-injected agent is steered into ("read ~/.aws"). These
|
|
453
|
+
* match by NAME because they live under a per-user home. No escape hatch, same as `/etc`: a job that
|
|
454
|
+
* needs one credential should be given that one file in the workspace. */
|
|
455
|
+
const REFUSED_MOUNT_COMPONENTS = new Set([
|
|
456
|
+
".ssh",
|
|
457
|
+
".aws",
|
|
458
|
+
".gnupg",
|
|
459
|
+
".kube",
|
|
460
|
+
".docker",
|
|
461
|
+
".azure",
|
|
462
|
+
".password-store",
|
|
463
|
+
".netrc",
|
|
464
|
+
".git-credentials",
|
|
465
|
+
".pypirc",
|
|
466
|
+
".npmrc",
|
|
467
|
+
]);
|
|
468
|
+
|
|
449
469
|
/** A PROGRAMMER/config error, THROWN: bad argument, illegal mount, `kern` not installed, or the box
|
|
450
470
|
* FAILED TO START (kern exits 125 - a mount refused at runtime, an unmappable `--user`, a seccomp or
|
|
451
471
|
* AppArmor setup error). A box that never started ran no user code, so it rejects rather than resolve a
|
|
@@ -837,6 +857,13 @@ function validateMount(source, target) {
|
|
|
837
857
|
`refusing to mount the sensitive host path ${JSON.stringify(real)} into a sandbox ` +
|
|
838
858
|
"(this would defeat the isolation)",
|
|
839
859
|
);
|
|
860
|
+
for (const part of real.split(path.sep))
|
|
861
|
+
if (REFUSED_MOUNT_COMPONENTS.has(part))
|
|
862
|
+
throw new MountRefused(
|
|
863
|
+
`refusing to mount ${JSON.stringify(real)}: ${JSON.stringify(part)} holds credentials, and code ` +
|
|
864
|
+
"in the box would read them. If the job needs one secret, write THAT FILE into the workspace " +
|
|
865
|
+
"(sbx.writeFile) or mount a directory that holds only it",
|
|
866
|
+
);
|
|
840
867
|
return [real, target];
|
|
841
868
|
}
|
|
842
869
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kern-sandbox",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.9",
|
|
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",
|