kern-sandbox 0.2.13 → 0.2.15

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.
Files changed (2) hide show
  1. package/index.js +35 -21
  2. package/package.json +1 -1
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.13";
39
+ const VERSION = "0.2.15";
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
@@ -1104,26 +1104,31 @@ function kernReportedOom(stderr) {
1104
1104
  return false;
1105
1105
  }
1106
1106
 
1107
+ /** The two prefixes kern's CLI writes AT COLUMN 0, which is the whole vocabulary of "kern is speaking".
1108
+ * `kern-cli/src/main.rs` reports every error through one `eprintln!("error: {}", ...)` and
1109
+ * `ui::scrub_message` indents every continuation line so a hostile value inside a message cannot forge a
1110
+ * line at column 0. Mirrors `_KERN_SPEAKING`.
1111
+ *
1112
+ * WHAT THIS REPLACED: a list of eleven message OPENINGS, each added after a caller measured a
1113
+ * `fault: null`. Enumerating the error texts of a binary with hundreds of them behind one printer cannot
1114
+ * be finished, and an external reviewer ended the argument with `image: ""`, whose
1115
+ * `error: bad image reference: empty` was in none of the eleven. */
1116
+ const KERN_SPEAKING = ["error: ", "kern:"];
1117
+
1118
+ /** True iff KERN ITSELF reported an error on this box, rather than the workload failing.
1119
+ *
1120
+ * It does NOT decide forgery from the text: a workload can print `error: anything` at column 0, and no
1121
+ * list of openings ever stopped that. The `KERN_STARTED_FD` byte does, and the callers pair it with this
1122
+ * predicate (`_runOne` drops a `startup_failed` when kern signalled a start; the kernel paths ask the
1123
+ * byte first). Mirrors `_looks_like_startup_failure`. */
1107
1124
  function looksLikeStartupFailure(stderr) {
1108
- const markers = [
1109
- "kern:",
1110
- "error: pull:",
1111
- "error: curl failed:",
1112
- "error: registry:",
1113
- "error: manifest:",
1114
- "error: sandbox:",
1115
- "error: box:",
1116
- "error: oci:",
1117
- "error: image:",
1118
- ];
1119
- // The OOM sentence is skipped for a sharper reason than the benign notes: it is a report about a box
1120
- // that RAN, and it is `kern:`-prefixed, so it used to satisfy this predicate. MEASURED, that is how a
1121
- // real OOM on a resident kernel came back as `startup_failed` and was THROWN instead of returning an
1122
- // `oom` fault.
1125
+ // kern's BENIGN lines are subtracted first, which is why this cannot be a bare prefix test: the
1126
+ // posture banner and `warning:`/`note:` carry `kern:` too. The OOM sentence is skipped for a sharper
1127
+ // reason: it is a report about a box that RAN, and MEASURED that is how a real OOM on a resident
1128
+ // kernel came back as `startup_failed` and was THROWN instead of returning an `oom` fault.
1123
1129
  for (const line of stderr.split("\n")) {
1124
- const s = line.replace(/^\s+/, "");
1125
- if (isKernDiagnostic(s) || kernReportedOom(s)) continue;
1126
- if (s.includes("sandbox setup failed") || markers.some((m) => s.startsWith(m))) return true;
1130
+ if (isKernDiagnostic(line) || kernReportedOom(line)) continue;
1131
+ if (KERN_SPEAKING.some((m) => line.startsWith(m)) || line.includes("sandbox setup failed")) return true;
1127
1132
  }
1128
1133
  return false;
1129
1134
  }
@@ -2805,7 +2810,13 @@ class Kernel {
2805
2810
  `the code crashed: the cell died on signal ${workloadSignal} (${SIGNAL_NAMES[workloadSignal]}), which took the kernel box with it because the interpreter is its PID 1. The sandbox did not act; the next call reopens a kernel`,
2806
2811
  rc,
2807
2812
  ];
2808
- if (looksLikeStartupFailure(err)) return ["startup_failed", "the kernel box failed to start", rc];
2813
+ // AND THE BYTE DECIDES WHETHER THE TEXT IS BELIEVED. kern writes the teardown payload only for a box
2814
+ // that existed, so `kernWrotePayload` is positive proof that this kernel STARTED and a
2815
+ // `startup_failed` here could only be a cell printing kern's prefix at column 0 and dying of
2816
+ // something else. The one-shot path gets this from `_runOne`, which drops the verdict when the start
2817
+ // byte is set; this path had no such guard, so the widened predicate gets it here.
2818
+ if (!kernWrotePayload && looksLikeStartupFailure(err))
2819
+ return ["startup_failed", "the kernel box failed to start", rc];
2809
2820
  if (capSignal === 2)
2810
2821
  return [
2811
2822
  "killed",
@@ -3241,7 +3252,10 @@ class WarmBox {
3241
3252
  if (oomVerdict(oomSignal, err, boxStarted)) {
3242
3253
  type = "oom";
3243
3254
  dflt = "the box exceeded its memory cap and was OOM-killed";
3244
- } else if (looksLikeStartupFailure(err)) {
3255
+ } else if (!boxStarted && looksLikeStartupFailure(err)) {
3256
+ // `!boxStarted` for the reason `_kernelDeathFault` states: kern writes the teardown payload only
3257
+ // for a box that existed, so with the byte set this THROW would be a cell's own column-0 line
3258
+ // deciding that the box never came up.
3245
3259
  throw new SandboxError(err.trim() || "the box failed to start");
3246
3260
  } else if (capSignal === 2) {
3247
3261
  dflt =
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kern-sandbox",
3
- "version": "0.2.13",
3
+ "version": "0.2.15",
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",