#!/usr/bin/env bash # Hermetic suite for the fleet/bin/mosaic launcher (T110 / P5-RM-009). # # Arms cover the plan acceptance: split-home shipped-first positive, typed # failure on missing candidates, stale-worktree non-precedence, OFF # pass-through, and secure-descriptor refusal on the untrusted $HOME # fallback. No network, no real npm install, no node package build: the # "shipped mosaic" is a stub script and getent is PATH-stubbed (set # GETENT_STUB=fail to make the passwd lookup fail, exercising the guarded # $HOME fallback). set -euo pipefail SCRIPT_DIR=$(cd -- "$(dirname -- "$0")" && pwd) LAUNCHER="$SCRIPT_DIR/mosaic" fail() { echo "FAIL: $*" >&2 exit 1 } [ -f "$LAUNCHER" ] || fail "missing launcher" [ -x "$LAUNCHER" ] || fail "launcher is not executable" bash -n "$LAUNCHER" || fail "launcher fails bash -n" WORK=$(mktemp -d) cleanup() { rm -rf "$WORK"; } trap cleanup EXIT REAL_HOME="$WORK/real-home" SEAT_HOME="$WORK/seat-home" STUB_BIN="$WORK/stub-bin" mkdir -p "$REAL_HOME/.npm-global/bin" "$SEAT_HOME" "$STUB_BIN" cat >"$REAL_HOME/.npm-global/bin/mosaic" <<'SH' #!/bin/sh echo "0.0.0-shipped-stub" SH chmod +x "$REAL_HOME/.npm-global/bin/mosaic" # PATH-stubbed getent: reports the real home for the current uid, unless # GETENT_STUB=fail is in the launcher environment (exercises the guarded # $HOME fallback path). cat >"$STUB_BIN/getent" < [VAR=value ...] -- [args...] local home="$1"; shift [ "${1:-}" = "--" ] && shift env -i PATH="$STUB_BIN:/usr/bin:/bin" HOME="$home" TERM="${TERM:-dumb}" "$LAUNCHER" "$@" } # A1 — acceptance 1: split-home positive. HOME is an empty seat home; the # shipped mosaic resolves through the passwd real home. out="$(printf '' | run_launcher "$SEAT_HOME" -- --version)" [ "$out" = "0.0.0-shipped-stub" ] || fail "A1 split-home positive: got '$out', want shipped stub version" # A3 — acceptance 3: a stale worktree build is NEVER consulted without the # explicit opt-in, even when a worktree exists on disk. WT="$WORK/stale-wt" mkdir -p "$WT/packages/mosaic/dist" printf 'console.log("0.0.0-stale-worktree")\n' >"$WT/packages/mosaic/dist/cli.js" out="$(printf '' | run_launcher "$SEAT_HOME" -- --version)" [ "$out" = "0.0.0-shipped-stub" ] || fail "A3 stale worktree regained precedence without opt-in: got '$out'" # A2 (opt-in healthy) — explicit MOSAIC_CLI_WORKTREE reaches the worktree. out="$(printf '' | env MOSAIC_CLI_WORKTREE="$WT" HOME="$SEAT_HOME" PATH="$STUB_BIN:/usr/bin:/bin" "$LAUNCHER" --version)" [ "$out" = "0.0.0-stale-worktree" ] || fail "A2 opt-in worktree not used: got '$out'" # A2b (opt-in unhealthy) — absent dist falls back to shipped with a warning. out2="$(printf '' | env MOSAIC_CLI_WORKTREE="$WORK/empty-wt" HOME="$SEAT_HOME" PATH="$STUB_BIN:/usr/bin:/bin" "$LAUNCHER" --version 2>/dev/null)" [ "$out2" = "0.0.0-shipped-stub" ] || fail "A2b unhealthy worktree fallback output: '$out2'" err2="$(printf '' | env MOSAIC_CLI_WORKTREE="$WORK/empty-wt" HOME="$SEAT_HOME" PATH="$STUB_BIN:/usr/bin:/bin" "$LAUNCHER" --version 2>&1 >/dev/null)" case "$err2" in *"absent or unreadable"*|*"health check"*) ;; *) fail "A2b unhealthy worktree fallback warning missing: '$err2'" ;; esac # A4 — OFF pass-through: worktree opt-in is ignored when OFF is set. out="$(printf '' | env MOSAIC_FLEET_CLI_OFF=1 MOSAIC_CLI_WORKTREE="$WT" HOME="$SEAT_HOME" PATH="$STUB_BIN:/usr/bin:/bin" "$LAUNCHER" --version)" [ "$out" = "0.0.0-shipped-stub" ] || fail "A4 OFF did not force pass-through: got '$out'" # A5 — acceptance 4: typed failure when no candidate exists (passwd lookup # fails, seat home carries no npm prefix). Expect 127 + documented message. set +e err="$(printf '' | env GETENT_STUB=fail HOME="$SEAT_HOME" PATH="$STUB_BIN:/usr/bin:/bin" "$LAUNCHER" --version 2>&1 >/dev/null)" rc=$? set -e [ "$rc" = "127" ] || fail "A5 typed failure rc: got $rc, want 127" case "$err" in *"no runnable CLI"*) ;; *) fail "A5 typed failure message missing: '$err'" ;; esac # A6 — secure descriptor traversal: passwd lookup fails and a symlink-planted # $HOME/.npm-global is refused without execution. PLANT="$WORK/planted-target" mkdir -p "$PLANT/.npm-global/bin" cat >"$PLANT/.npm-global/bin/mosaic" <&1)" rc=$? set -e [ "$rc" = "127" ] || fail "A6 planted descriptor was followed (rc $rc, out '$err')" case "$err" in *"symlink component"*) ;; *) fail "A6 refusal diagnostic missing: '$err'" ;; esac [ ! -e "$WORK/planted-sentinel" ] || fail "A6 planted mosaic EXECUTED" echo "mosaic launcher suite: all arms passed"