diff --git a/packages/mosaic/framework/tools/git/test-issue-comment-readback.sh b/packages/mosaic/framework/tools/git/test-issue-comment-readback.sh index b0178385..e8b1bb7c 100755 --- a/packages/mosaic/framework/tools/git/test-issue-comment-readback.sh +++ b/packages/mosaic/framework/tools/git/test-issue-comment-readback.sh @@ -61,15 +61,54 @@ STATE_FILE="$WORK_DIR/comments.json" # A dedicated scratch dir the wrapper is pointed at via TMPDIR, so the leak # check can assert every POST/GET body + metadata temp file is cleaned up. TMP_SCRATCH="$WORK_DIR/scratch" +HOME_DIR="$WORK_DIR/home" cleanup() { rm -rf "$WORK_DIR" } trap cleanup EXIT -mkdir -p "$REPO_DIR" "$BIN_DIR" "$XDG_DIR" "$TMP_SCRATCH" +mkdir -p "$REPO_DIR" "$BIN_DIR" "$XDG_DIR" "$TMP_SCRATCH" "$HOME_DIR" git -C "$REPO_DIR" init -q git -C "$REPO_DIR" remote add origin https://git.mosaicstack.dev/mosaicstack/stack.git +# HERMETICITY (#1007) — TWO mechanisms with DIFFERENT jobs; do not conflate them. +# +# OPERATIVE: the empty repo-local `mosaic.gitIdentity` below. get_gitea_token() +# step 0 resolves a per-agent identity from `git config --get mosaic.gitIdentity`, +# which on a provisioned agent seat is set GLOBALLY and so leaks into this fresh +# repo. It then reads a REAL per-slot token from $HOME and returns it WITHOUT ever +# consulting MOSAIC_CREDENTIALS_FILE, so the fixture credential below is silently +# ignored. The stub curl then rejects the unrecognised bearer, and this suite +# fails at its FIRST case with `Gitea authenticated-identity read failed with +# HTTP 401`. An empty repo-local value shadows the global one and reads back +# empty at rc=0. Measured: without this pin the suite is RED on every seat. +# +# CONTAINMENT: the sandboxed HOME in run_comment(). It only has to bound a +# failure that the pin should already have prevented. +# +# THIS SUITE WAS THE HARDEST OF THE FIVE TO SEE, and the reason is worth stating +# because it generalises: run_comment() sends the wrapper's stdout AND stderr to +# $OUTPUT_FILE, and the EXIT trap above deletes $WORK_DIR. So the 401 — the only +# thing that says what went wrong — exists only inside a directory that is gone +# by the time anyone looks. The suite exits 1 with ZERO bytes on stdout and +# stderr. A suite that discards or deletes its own evidence turns any post-hoc +# assay into a non-measurement: "nothing found" there means "no surviving +# trace", never "clean". It was found by intercepting the identity read at its +# SOURCE (a PATH shim over `git` logging every `mosaic.gitIdentity` read to a +# file outside $WORK_DIR), which is deletion-proof by construction, rather than +# by grepping for the symptom. +# +# NOTE FOR ANYONE AUDITING THIS SUITE: the sandboxed HOME is containment, NOT an +# assay. Running a suite under a decoy HOME to test for this defect REMOVES the +# trigger — ~/.gitconfig is where the global identity lives, so step 0 is skipped +# by construction and every suite reads clean however vulnerable it is. To +# measure, REPLICATE a seat (a decoy HOME whose .gitconfig sets +# mosaic.gitIdentity, with no per-slot token) so step 0 reaches its fail-loud +# branch — or intercept the read as described above. +# +# Note the env-var route does NOT work: detect-platform.sh reads +# "${MOSAIC_GIT_IDENTITY:-}", and `:-` treats set-but-empty identically to unset. +git -C "$REPO_DIR" config mosaic.gitIdentity "" ISSUE_NUMBER=7 REPO_SLUG="mosaicstack/stack" @@ -366,6 +405,7 @@ run_comment() { cd "$REPO_DIR" PATH="$BIN_DIR:$PATH" \ TMPDIR="$TMP_SCRATCH" \ + HOME="$HOME_DIR" \ XDG_CONFIG_HOME="$XDG_DIR" \ MOSAIC_CREDENTIALS_FILE="$CREDENTIALS_FILE" \ ISSUE_COMMENT_TEA_LOG="$TEA_LOG" \