fix: say why a sandbox failed to launch, and observe a run instead of an instant
The cgroup test read the live process tree with one `ps` per pid and asserted while the provider was running. That was a race it used to win only because the sandbox was slow; now a whole run finishes in a few hundred milliseconds and `systemctl show` alone costs longer than the thing it describes. It records the tree from `/proc` every 5ms and asserts on the recording once the run is over, because the assertions were always about what the run contained. That restructuring immediately paid for itself: the supervisor had been failing to launch the sandbox at all, and the test was dying on the observation before it ever checked the exit code. It could not say why, because the supervisor consumed the child's output solely to enforce a byte cap and then discarded it — `exit=1` and nothing else. It now keeps the lines the sandbox tooling itself emits (`bwrap:`, `prlimit:`, `systemd-run:`, `systemctl:`), which cannot carry provider credentials because the provider command and its secrets travel in the args file. The failure now reads: sandboxed external provider failed: exit=1; sandbox reported: bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted which is a host restriction — `kernel.apparmor_restrict_unprivileged_userns=1` — reproducible in two lines of shell containing none of this repository's code, and recorded in the ledger as such rather than carried as a product defect. Suites that spawn processes, build archives and sign evidence were given a 30s budget. The 10s default is sized for pure-JS unit tests; raising it globally would hide a genuinely hung test. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
711d61e73f
commit
9ca5c3f668
@@ -373,7 +373,25 @@ async function waitForProvider(
|
||||
PROVIDER_MAX_OUTPUT_BYTES,
|
||||
() => terminate("output"),
|
||||
);
|
||||
/**
|
||||
* Lines the sandbox tooling itself emits, kept so a launch failure can say
|
||||
* why. Everything else the child writes is provider output and may carry
|
||||
* credentials, so it is counted and discarded as before.
|
||||
*
|
||||
* Without this a sandbox that never started reported only `exit=1`, and the
|
||||
* actual cause — `bwrap: loopback: Failed RTM_NEWADDR: Operation not
|
||||
* permitted` on a host with `kernel.apparmor_restrict_unprivileged_userns=1`
|
||||
* — was invisible. That turned a host restriction into an unexplained
|
||||
* product failure.
|
||||
*/
|
||||
const SANDBOX_DIAGNOSTIC = /^(?:bwrap|prlimit|systemd-run|systemctl):\s.*$/gmu;
|
||||
const sandboxDiagnostics: string[] = [];
|
||||
const capture = (chunk: Buffer | string): void => {
|
||||
for (const line of String(chunk).matchAll(SANDBOX_DIAGNOSTIC)) {
|
||||
if (sandboxDiagnostics.length < 8 && !sandboxDiagnostics.includes(line[0])) {
|
||||
sandboxDiagnostics.push(line[0]);
|
||||
}
|
||||
}
|
||||
if (termination) return;
|
||||
outputLimiter.consume(chunk);
|
||||
};
|
||||
@@ -427,7 +445,13 @@ async function waitForProvider(
|
||||
await collection;
|
||||
if (result.error) throw result.error;
|
||||
if (result.code !== 0 || result.signal !== null) {
|
||||
throw new Error(`sandboxed external provider failed: exit=${result.code ?? "none"}, signal=${result.signal ?? "none"}`);
|
||||
throw new Error(
|
||||
`sandboxed external provider failed: exit=${result.code ?? "none"}, ` +
|
||||
`signal=${result.signal ?? "none"}` +
|
||||
(sandboxDiagnostics.length > 0
|
||||
? `; sandbox reported: ${sandboxDiagnostics.join("; ")}`
|
||||
: ""),
|
||||
);
|
||||
}
|
||||
if (inputError) throw inputError;
|
||||
} finally {
|
||||
|
||||
Reference in New Issue
Block a user