fix(installer): harden greenfield detector contracts
This commit is contained in:
@@ -4,6 +4,8 @@ set -euo pipefail
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
TMP="$(mktemp -d "${TMPDIR:-/tmp}/mosaic-next-install-test-XXXXXX")"
|
||||
trap 'rm -rf "$TMP"' EXIT
|
||||
export TMPDIR="$TMP/runtime-tmp"
|
||||
mkdir -p "$TMPDIR"
|
||||
|
||||
FAKE_BIN="$TMP/bin"
|
||||
HOME_DIR="$TMP/home"
|
||||
@@ -13,6 +15,48 @@ STATE="$TMP/state"
|
||||
LOG="$TMP/npm.log"
|
||||
mkdir -p "$FAKE_BIN" "$HOME_DIR" "$STATE"
|
||||
|
||||
# Model the supported non-root/glibc target explicitly even when this harness
|
||||
# itself runs as root in Alpine/BusyBox CI.
|
||||
cat > "$FAKE_BIN/id" <<'FAKE_ID'
|
||||
#!/usr/bin/env bash
|
||||
case "${1:-}" in
|
||||
-u) echo 1001 ;;
|
||||
-g) echo 1001 ;;
|
||||
-un) echo fixture-user ;;
|
||||
*) exec /bin/id "$@" ;;
|
||||
esac
|
||||
FAKE_ID
|
||||
cat > "$FAKE_BIN/getent" <<FAKE_GETENT
|
||||
#!/usr/bin/env bash
|
||||
printf 'fixture-user:x:1001:1001::%s:/bin/bash\n' '$HOME_DIR'
|
||||
FAKE_GETENT
|
||||
cat > "$FAKE_BIN/ldd" <<'FAKE_LDD'
|
||||
#!/usr/bin/env bash
|
||||
printf 'ldd (GNU libc) 2.36\n'
|
||||
FAKE_LDD
|
||||
cat > "$FAKE_BIN/stat" <<'FAKE_STAT'
|
||||
#!/usr/bin/env bash
|
||||
if [[ "${1:-} ${2:-}" == '-c %u' ]]; then
|
||||
[[ "${3:-}" == "${MOSAIC_TEST_WRONG_OWNER_PATH:-__none__}" ]] && echo 9999 || echo 1001
|
||||
exit 0
|
||||
fi
|
||||
if [[ "${1:-} ${2:-}" == '-c %g' ]]; then
|
||||
[[ "${3:-}" == "${MOSAIC_TEST_WRONG_GROUP_PATH:-__none__}" ]] && echo 9999 || echo 1001
|
||||
exit 0
|
||||
fi
|
||||
exec /bin/stat "$@"
|
||||
FAKE_STAT
|
||||
cat > "$FAKE_BIN/realpath" <<'FAKE_REALPATH'
|
||||
#!/usr/bin/env python3
|
||||
import os, sys
|
||||
args=sys.argv[1:]
|
||||
mode=args.pop(0) if args and args[0] in ('-e','-m') else '-m'
|
||||
if args and args[0]=='--': args.pop(0)
|
||||
if len(args)!=1 or (mode=='-e' and not os.path.exists(args[0])): raise SystemExit(1)
|
||||
print(os.path.realpath(args[0]))
|
||||
FAKE_REALPATH
|
||||
chmod 0755 "$FAKE_BIN/id" "$FAKE_BIN/getent" "$FAKE_BIN/ldd" "$FAKE_BIN/stat" "$FAKE_BIN/realpath"
|
||||
|
||||
cat > "$FAKE_BIN/npm" <<'FAKE_NPM'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
@@ -31,6 +75,15 @@ install_cli() {
|
||||
mkdir -p "${MOSAIC_PREFIX:?}/bin"
|
||||
cat > "$MOSAIC_PREFIX/bin/mosaic" <<CLI
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
if [[ "\${1:-}" == "wizard" ]]; then
|
||||
printf 'wizard\n' >> "\${MOSAIC_TEST_NPM_LOG:?}"
|
||||
mkdir -p "\${MOSAIC_HOME:?}" "\${HOME:?}/.config/mosaic-gateway"
|
||||
printf '# Soul\\n\\nConfigured.\\n' > "\$MOSAIC_HOME/SOUL.md"
|
||||
printf '# User\\n\\nConfigured.\\n' > "\$MOSAIC_HOME/USER.md"
|
||||
chmod 0600 "\$MOSAIC_HOME/SOUL.md" "\$MOSAIC_HOME/USER.md"
|
||||
exit 0
|
||||
fi
|
||||
printf '%s\\n' '$version'
|
||||
CLI
|
||||
chmod +x "$MOSAIC_PREFIX/bin/mosaic"
|
||||
@@ -51,6 +104,12 @@ if [[ "$1" == "view" ]]; then
|
||||
fi
|
||||
|
||||
if [[ "$1" == "install" ]]; then
|
||||
if [[ -n "${MOSAIC_INSTALL_SECRET_CANARY:-}" ]]; then
|
||||
printf 'registry diagnostic authToken=%s\n' "$MOSAIC_INSTALL_SECRET_CANARY"
|
||||
printf 'urls=https://alice:p@[email protected]/a https://bob:pa:[email protected]/b https://carol:p%%[email protected]/c https://[email protected]/d https://user%%[email protected]/e\n'
|
||||
printf 'Authorization: Basic QWxhZGRpbjpvcGVu\n//registry/:_auth=Ym9iOnNlY3JldA==\nCookie: session=abc123\nSet-Cookie: sid=xyz789\n'
|
||||
printf '%s\n' "$MOSAIC_INSTALL_SECRET_CANARY" > "${MOSAIC_TEST_CANARY_OBSERVATION:?}"
|
||||
fi
|
||||
case "$*" in
|
||||
*"@mosaicstack/[email protected]"*)
|
||||
install_cli "0.0.49-next.999"
|
||||
@@ -141,7 +200,21 @@ if [[ -z "$dest" ]]; then
|
||||
echo "fake tar missing -C destination" >&2
|
||||
exit 1
|
||||
fi
|
||||
mkdir -p "$dest/stack/packages/mosaic" "$dest/stack/apps/gateway"
|
||||
mkdir -p "$dest/stack/packages/mosaic/framework" "$dest/stack/apps/gateway"
|
||||
cat > "$dest/stack/packages/mosaic/framework/install.sh" <<'FRAMEWORK'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
expected="${MOSAIC_PREFIX:?}/bin/mosaic"
|
||||
[[ "${MOSAIC_CLI_PATH:-}" == "$expected" && -x "$MOSAIC_CLI_PATH" ]] || {
|
||||
echo "framework did not receive P3 absolute CLI (got=${MOSAIC_CLI_PATH:-unset} expected=$expected)" >&2
|
||||
exit 61
|
||||
}
|
||||
printf 'framework-cli=%s version=%s\n' "$MOSAIC_CLI_PATH" "$($MOSAIC_CLI_PATH --version)" >> "${MOSAIC_TEST_NPM_LOG:?}"
|
||||
mkdir -p "${MOSAIC_HOME:?}/credentials"
|
||||
chmod 0700 "$MOSAIC_HOME/credentials"
|
||||
printf '# framework fixture\n' > "$MOSAIC_HOME/AGENTS.md"
|
||||
FRAMEWORK
|
||||
chmod 0755 "$dest/stack/packages/mosaic/framework/install.sh"
|
||||
FAKE_TAR
|
||||
chmod +x "$FAKE_BIN/tar"
|
||||
|
||||
@@ -187,15 +260,28 @@ reset_state() {
|
||||
rm -f "$STATE"/*
|
||||
}
|
||||
|
||||
prefix_fingerprint() {
|
||||
if [[ ! -d "$PREFIX" ]]; then printf 'ABSENT\n'; return; fi
|
||||
(
|
||||
cd "$PREFIX"
|
||||
find . -mindepth 1 -printf '%P|%y|%m|%l\n' | LC_ALL=C sort
|
||||
find . -type f -print0 | LC_ALL=C sort -z | xargs -0 -r sha256sum
|
||||
) | sha256sum | awk '{print $1}'
|
||||
tree_fingerprint() {
|
||||
local root="$1"
|
||||
if [[ ! -d "$root" ]]; then printf 'ABSENT\n'; return; fi
|
||||
python3 - "$root" <<'PY'
|
||||
import hashlib, os, stat, sys
|
||||
root=os.path.abspath(sys.argv[1]); rows=[]
|
||||
for current, dirs, files in os.walk(root, topdown=True, followlinks=False):
|
||||
for name in dirs + files:
|
||||
path=os.path.join(current,name); meta=os.lstat(path)
|
||||
rel=os.path.relpath(path,root)
|
||||
target=os.readlink(path) if stat.S_ISLNK(meta.st_mode) else ''
|
||||
digest=''
|
||||
if stat.S_ISREG(meta.st_mode):
|
||||
with open(path,'rb') as handle: digest=hashlib.sha256(handle.read()).hexdigest()
|
||||
rows.append((rel,stat.S_IFMT(meta.st_mode),stat.S_IMODE(meta.st_mode),target,digest))
|
||||
payload='\n'.join('|'.join(map(str,row)) for row in sorted(rows)).encode()
|
||||
print(hashlib.sha256(payload).hexdigest())
|
||||
PY
|
||||
}
|
||||
|
||||
prefix_fingerprint() { tree_fingerprint "$PREFIX"; }
|
||||
|
||||
reset_state
|
||||
echo "[test] --next fast path pins resolved package versions"
|
||||
OUTPUT="$(
|
||||
@@ -356,4 +442,131 @@ set -e
|
||||
[[ "$CHECK_STATUS" -ne 0 ]]
|
||||
grep -q '^\[P2\] FAIL: resolved_version=unavailable' <<<"$OUTPUT"
|
||||
|
||||
printf '[test] full framework path receives P3 absolute CLI without relying on PATH\n'
|
||||
rm -rf "$HOME_DIR" "$STATE"; mkdir -p "$HOME_DIR" "$STATE"; reset_state
|
||||
set +e
|
||||
OUTPUT="$(
|
||||
HOME="$HOME_DIR" MOSAIC_HOME="$MOSAIC_HOME" MOSAIC_PREFIX="$PREFIX" \
|
||||
MOSAIC_INSTALL_STATE_DIR="$TMP/full-state" MOSAIC_NO_COLOR=1 \
|
||||
MOSAIC_TEST_NPM_LOG="$LOG" MOSAIC_TEST_STATE="$STATE" \
|
||||
PATH="$FAKE_BIN:/usr/local/bin:/usr/bin:/bin" \
|
||||
bash "$ROOT/tools/install.sh" --next --yes --no-auto-launch 2>&1
|
||||
)"
|
||||
FULL_STATUS=$?
|
||||
set -e
|
||||
[[ "$FULL_STATUS" -ne 0 ]] # P4 remains intentionally undeclared until C5.
|
||||
grep -qF "framework-cli=$PREFIX/bin/mosaic version=0.0.49-next.999" "$LOG"
|
||||
if grep -q "CLI not found on PATH\|did not receive P3 absolute CLI" <<<"$OUTPUT"; then
|
||||
echo "internal framework phase depended on PATH instead of P3 absolute CLI" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf '[test] captured diagnostics redact seeded credential canary everywhere\n'
|
||||
rm -rf "$HOME_DIR" "$STATE"; mkdir -p "$HOME_DIR" "$STATE"; reset_state
|
||||
canary='C1_SECRET_CANARY_7df4c2'
|
||||
OUTPUT="$(
|
||||
HOME="$HOME_DIR" MOSAIC_HOME="$MOSAIC_HOME" MOSAIC_PREFIX="$PREFIX" \
|
||||
MOSAIC_INSTALL_STATE_DIR="$TMP/secret-state" MOSAIC_NO_COLOR=1 \
|
||||
MOSAIC_INSTALL_SECRET_CANARY="$canary" MOSAIC_TEST_CANARY_OBSERVATION="$TMP/canary-observed" \
|
||||
MOSAIC_TEST_NPM_LOG="$LOG" MOSAIC_TEST_STATE="$STATE" \
|
||||
PATH="$FAKE_BIN:/usr/local/bin:/usr/bin:/bin" \
|
||||
bash "$ROOT/tools/install.sh" --cli --next --yes --no-auto-launch 2>&1
|
||||
)"
|
||||
if grep -qF "$canary" <<<"$OUTPUT"; then echo 'credential canary leaked to terminal output' >&2; exit 1; fi
|
||||
if grep -Eq 'alice:p@ss|bob:pa:ss|carol:p%40ss|token@example|user%3Apass|QWxhZGRpbjpvcGVu|Ym9iOnNlY3JldA|session=abc123|sid=xyz789' <<<"$OUTPUT"; then
|
||||
echo 'credentialed URL userinfo leaked to terminal output' >&2; exit 1
|
||||
fi
|
||||
[[ "$(grep -oF '[REDACTED]@' <<<"$OUTPUT" | wc -l | tr -d ' ')" -ge 5 ]] \
|
||||
|| { echo 'credentialed URL redaction controls were not all exercised' >&2; exit 1; }
|
||||
secret_active="$TMP/secret-state/active.json"
|
||||
secret_journal="$(node -p "require('$secret_active').journal")"
|
||||
secret_command_log="$(dirname "$secret_journal")/commands.log"
|
||||
if grep -R -F "$canary" "$secret_command_log" "$HOME_DIR" 2>/dev/null; then
|
||||
echo 'credential canary leaked to persistent installer output' >&2; exit 1
|
||||
fi
|
||||
if grep -E 'alice:p@ss|bob:pa:ss|carol:p%40ss|token@example|user%3Apass|QWxhZGRpbjpvcGVu|Ym9iOnNlY3JldA|session=abc123|sid=xyz789' "$secret_command_log" >/dev/null; then
|
||||
echo 'credentialed URL userinfo leaked to persistent installer output' >&2; exit 1
|
||||
fi
|
||||
if [[ "$(cat "$TMP/canary-observed" 2>/dev/null || true)" != "$canary" ]]; then
|
||||
echo 'credential canary positive control was not exercised' >&2; exit 1
|
||||
fi
|
||||
if find "$TMPDIR" -maxdepth 1 -type f \( -name 'mosaic-phase-redacted.*' -o -name 'mosaic-post-redacted.*' \) -print -quit | grep -q .; then
|
||||
echo 'redacted diagnostic staging file survived normal completion' >&2; exit 1
|
||||
fi
|
||||
|
||||
printf '[test] framework nested capture redacts the same canary and URL variants\n'
|
||||
framework_test_home="$TMP/framework-redact-home"
|
||||
framework_target="$framework_test_home/.config/mosaic"
|
||||
framework_cli="$TMP/framework-redact-cli"
|
||||
framework_log="$TMP/framework-redact-commands.log"
|
||||
framework_status="$TMP/framework-redact-status.tsv"
|
||||
mkdir -p "$framework_test_home"; : > "$framework_log"; : > "$framework_status"
|
||||
cat > "$framework_cli" <<'FRAMEWORK_CLI'
|
||||
#!/usr/bin/env bash
|
||||
printf 'nested authToken=%s\n' "${MOSAIC_INSTALL_SECRET_CANARY:?}"
|
||||
printf 'nested=https://alice:p@[email protected]/a https://bob:pa:[email protected]/b https://carol:p%%[email protected]/c https://[email protected]/d https://user%%[email protected]/e\n'
|
||||
printf 'Authorization: Basic QWxhZGRpbjpvcGVu\n//registry/:_auth=Ym9iOnNlY3JldA==\nCookie: session=abc123\nSet-Cookie: sid=xyz789\n'
|
||||
exit 1
|
||||
FRAMEWORK_CLI
|
||||
chmod 0755 "$framework_cli"
|
||||
set +e
|
||||
FRAMEWORK_OUTPUT="$(
|
||||
HOME="$framework_test_home" MOSAIC_HOME="$framework_target" MOSAIC_INSTALL_MODE=overwrite \
|
||||
MOSAIC_CLI_PATH="$framework_cli" MOSAIC_INSTALL_SECRET_CANARY="$canary" \
|
||||
MOSAIC_INSTALL_COMMAND_LOG="$framework_log" MOSAIC_INSTALL_PHASE_STATUS_FILE="$framework_status" \
|
||||
MOSAIC_ALLOW_MISSING_SEQUENTIAL_THINKING=1 MOSAIC_SKIP_SKILLS_SYNC=1 \
|
||||
bash "$ROOT/packages/mosaic/framework/install.sh" 2>&1
|
||||
)"
|
||||
framework_install_status=$?
|
||||
set -e
|
||||
[[ "$framework_install_status" -eq 0 ]]
|
||||
if grep -Eq "$canary|alice:p@ss|bob:pa:ss|carol:p%40ss|token@example|user%3Apass|QWxhZGRpbjpvcGVu|Ym9iOnNlY3JldA|session=abc123|sid=xyz789" <<<"$FRAMEWORK_OUTPUT" \
|
||||
|| grep -Eq "$canary|alice:p@ss|bob:pa:ss|carol:p%40ss|token@example|user%3Apass|QWxhZGRpbjpvcGVu|Ym9iOnNlY3JldA|session=abc123|sid=xyz789" "$framework_log"; then
|
||||
echo 'framework nested capture leaked credential diagnostics' >&2; exit 1
|
||||
fi
|
||||
[[ "$(grep -oF '[REDACTED]@' "$framework_log" | wc -l | tr -d ' ')" -ge 5 ]] \
|
||||
|| { echo 'framework URL redaction controls were not exercised' >&2; exit 1; }
|
||||
|
||||
printf '[test] real P2-P8 actions run under fault injection and restore actual surfaces\n'
|
||||
for phase in P2 P3 P4 P5 P6 P7 P8; do
|
||||
rm -rf "$HOME_DIR" "$STATE" "$TMP/fault-$phase"; mkdir -p "$HOME_DIR" "$STATE" "$TMP/fault-$phase"
|
||||
printf 'operator-sentinel\n' > "$HOME_DIR/operator.txt"
|
||||
reset_state
|
||||
before="$(tree_fingerprint "$HOME_DIR")"
|
||||
set +e
|
||||
HOME="$HOME_DIR" MOSAIC_HOME="$MOSAIC_HOME" MOSAIC_PREFIX="$PREFIX" \
|
||||
MOSAIC_INSTALL_STATE_DIR="$TMP/fault-$phase" MOSAIC_INSTALL_FAULT_AFTER="$phase" \
|
||||
MOSAIC_INSTALL_SELF_TEST_ALLOW=1 MOSAIC_NO_COLOR=1 \
|
||||
MOSAIC_TEST_NPM_LOG="$LOG" MOSAIC_TEST_STATE="$STATE" \
|
||||
PATH="$FAKE_BIN:/usr/local/bin:/usr/bin:/bin" \
|
||||
bash "$ROOT/tools/install.sh" --state-machine-self-test --next --yes \
|
||||
>"$TMP/fault-$phase.log" 2>&1
|
||||
status=$?
|
||||
set -e
|
||||
[[ "$status" -eq 97 ]] || { echo "$phase real fault expected 97, got $status" >&2; exit 1; }
|
||||
[[ -s "$LOG" ]] || { echo "$phase fault never entered the real action path" >&2; exit 1; }
|
||||
[[ "$(tree_fingerprint "$HOME_DIR")" == "$before" ]] || { echo "$phase real rollback mismatch" >&2; exit 1; }
|
||||
grep -q "phase=$phase" "$TMP/fault-$phase.log"
|
||||
if find "$TMP/fault-$phase" -type f -exec grep -l '"status"[[:space:]]*:[[:space:]]*"in-progress"' {} + 2>/dev/null | grep -q .; then
|
||||
echo "$phase left an in-progress transaction" >&2; exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
printf '[test] stale projection is preserved while the real fault path acquires a free OS lock\n'
|
||||
rm -rf "$HOME_DIR" "$STATE" "$TMP/stale-state"; mkdir -p "$HOME_DIR" "$STATE" "$TMP/stale-state"
|
||||
printf '{"status":"in-progress","journal":"%s"}\n' "$TMP/stale-state/dead-run/journal.ndjson" > "$TMP/stale-state/active.json"
|
||||
reset_state
|
||||
set +e
|
||||
HOME="$HOME_DIR" MOSAIC_HOME="$MOSAIC_HOME" MOSAIC_PREFIX="$PREFIX" \
|
||||
MOSAIC_INSTALL_STATE_DIR="$TMP/stale-state" MOSAIC_INSTALL_FAULT_AFTER=P2 \
|
||||
MOSAIC_INSTALL_SELF_TEST_ALLOW=1 MOSAIC_NO_COLOR=1 \
|
||||
MOSAIC_TEST_NPM_LOG="$LOG" MOSAIC_TEST_STATE="$STATE" \
|
||||
PATH="$FAKE_BIN:/usr/local/bin:/usr/bin:/bin" \
|
||||
bash "$ROOT/tools/install.sh" --state-machine-self-test --next --yes >"$TMP/stale.log" 2>&1
|
||||
stale_status=$?
|
||||
set -e
|
||||
[[ "$stale_status" -eq 97 ]]
|
||||
find "$TMP/stale-state" -name prior-active.json -type f -print -quit | grep -q .
|
||||
[[ "$(node -p "require('$TMP/stale-state/active.json').status")" == rolled-back ]]
|
||||
|
||||
echo "[test] installer next lane tests passed"
|
||||
|
||||
Reference in New Issue
Block a user