Build, patch and manifest for the bwrap sandbox, the credential check and the unit's EnvironmentFile= line. Gate 14 suites rc=0; 26 mutants killed. Co-Authored-By: Claude Opus 5.5 <[email protected]>
60 lines
4.4 KiB
Bash
60 lines
4.4 KiB
Bash
#!/usr/bin/env bash
|
||
# Row 92 (#1586) mutants. Run from the candidate worktree's root:
|
||
# LOG=<dir> bash mutants.sh /path/to/mut.mjs > mutants.txt
|
||
# Each mutant edits one site with mut.mjs (exactly one match, else exit 3),
|
||
# runs the named test files and restores the file. "killed" means the run
|
||
# failed; "survived" means it passed.
|
||
set -u
|
||
MUT=$1
|
||
run() {
|
||
local name=$1 file=$2 from=$3 to=$4; shift 4
|
||
cp -- "$file" "$file.orig"
|
||
if ! node "$MUT" "$file" "$from" "$to"; then
|
||
echo "$name: NO MATCH"; mv -- "$file.orig" "$file"; return
|
||
fi
|
||
if node --test "$@" > "${LOG:-.}/mut-$name.log" 2>&1; then
|
||
echo "$name: survived"
|
||
else
|
||
echo "$name: killed ($(grep -m1 -o 'ℹ fail [0-9]*' "${LOG:-.}/mut-$name.log"), $(grep -m1 '^✖' "${LOG:-.}/mut-$name.log" | cut -c1-90))"
|
||
fi
|
||
mv -- "$file.orig" "$file"
|
||
}
|
||
S=packages/seat/src/sandbox.mjs
|
||
E=packages/seat/src/session.mjs
|
||
L=packages/cli/src/launcher.mjs
|
||
U=packages/cli/systemd/mosaic-bus.service.in
|
||
ST=(packages/seat/tests/sandbox.test.mjs)
|
||
SE=(packages/seat/tests/session.test.mjs)
|
||
LT=(packages/cli/tests/launcher.test.mjs)
|
||
ALL=(packages/seat/tests/sandbox.test.mjs packages/seat/tests/session.test.mjs packages/cli/tests/launcher.test.mjs)
|
||
# Binds
|
||
run no-workspace-bind $S $' { kind: "rw", src: workspace, why: "the instance\'s workspace, where the harness works" },\n' '' "${ST[@]}"
|
||
run runDir-read-only $S '{ kind: "rw", src: runDir,' '{ kind: "ro", src: runDir,' "${ST[@]}"
|
||
run bind-all-runs $S '{ kind: "rw", src: runDir,' '{ kind: "rw", src: dirname(runDir),' "${ST[@]}"
|
||
run bundle-writable $S $' { kind: "ro", src: join(runDir, "bundle"), why: "the run\'s bundle stays as the host wrote it" },\n' '' "${ST[@]}"
|
||
run session-json-writable $S $' { kind: "ro", src: join(runDir, "session.json"), why: "the session file stays as the host wrote it" },\n' '' "${ST[@]}"
|
||
run no-private-tmp $S $' { kind: "rw", src: join(runDir, "tmp"), dest: "/tmp", why: "a private /tmp, on disk in the run directory" },\n' '' "${ST[@]}"
|
||
run bind-checkout $S ' for (const f of RUNNER_FILES) out.push(' $' out.push({ kind: "ro", src: REPO, why: "x" });\n for (const f of RUNNER_FILES) out.push(' "${ST[@]}"
|
||
run node-modules-always $S 'if (harness === "pi") out.push' 'if (true) out.push' "${ST[@]}"
|
||
run runner-file-missing $S $' "packages/business/src/vocabulary.mjs",\n' '' "${ST[@]}"
|
||
run no-remount-ro $S '...argsOf(list), "--remount-ro", "/", "--setenv"' '...argsOf(list), "--setenv"' "${ST[@]}"
|
||
run host-home $S '"--setenv", "HOME", join(runDir, "home")' '"--setenv", "HOME", process.env.HOME' "${ST[@]}"
|
||
# Namespaces and death
|
||
run no-pid-namespace $S '"--unshare-pid", "--unshare-ipc", "--as-pid-1"' '"--unshare-ipc"' "${ALL[@]}"
|
||
run die-with-parent $S '"--unshare-ipc", "--as-pid-1"]' '"--unshare-ipc", "--as-pid-1", "--die-with-parent"]' "${LT[@]}"
|
||
run no-pdeathsig $S 'const KILL_WITH_PARENT = ["/usr/bin/setpriv", "--pdeathsig", "KILL", "--"];' 'const KILL_WITH_PARENT = [];' "${LT[@]}"
|
||
run exit-code-not-mapped $E $' if (!signal && code > 128 && SIGNALS[code - 128]) return { code: null, signal: SIGNALS[code - 128] };\n' '' "${SE[@]}" "${LT[@]}"
|
||
run recover-matches-unshare $L '(cmdlineOf(s.pid) ?? [])[0] === bwrap)' '(cmdlineOf(s.pid) ?? [])[0] === "unshare")' "${LT[@]}"
|
||
# Refusals
|
||
run no-sandbox-check $L $' if (unavailable) throw new Refusal("sandbox-unavailable", unavailable);\n' '' "${LT[@]}"
|
||
run probe-always-ok $S $' if (!existsSync(bwrap)) return `${bwrap} is missing`;\n' $' return null;\n' "${ST[@]}" "${LT[@]}"
|
||
run no-credential-check $L 'if (model.harness === "claude-code" && !hasSessionCredential(env))' 'if (false)' "${LT[@]}"
|
||
run proxy-key-alone $E '["ANTHROPIC_AUTH_TOKEN", "ANTHROPIC_BASE_URL"]]' '["ANTHROPIC_AUTH_TOKEN"]]' "${SE[@]}" "${LT[@]}"
|
||
run proxy-key-ignored $E ', ["ANTHROPIC_AUTH_TOKEN", "ANTHROPIC_BASE_URL"]]' ']' "${SE[@]}" "${LT[@]}"
|
||
run any-variable-will-do $E 'SESSION_CREDENTIALS.some((set) => set.every(' 'SESSION_CREDENTIALS.some((set) => set.some(' "${SE[@]}" "${LT[@]}"
|
||
run claude-bin-not-bound $L 'claudeBin: env.MOSAIC_CLAUDE_BIN || null,' 'claudeBin: null,' "${LT[@]}"
|
||
run launch-socket-always $L 'launchSocket: resolved.launch ? launchSocketPath(dataRoot) : null,' 'launchSocket: launchSocketPath(dataRoot),' "${LT[@]}"
|
||
# The unit
|
||
run unit-no-envfile $U 'EnvironmentFile=-' '# EnvironmentFile=-' packages/cli/tests/host.test.mjs
|
||
run unit-envfile-required $U 'EnvironmentFile=-' 'EnvironmentFile=' packages/cli/tests/host.test.mjs
|