diff --git a/packages/mosaic/framework/tools/tmux/agent-send.sh b/packages/mosaic/framework/tools/tmux/agent-send.sh index 9da54052..d44e46ca 100755 --- a/packages/mosaic/framework/tools/tmux/agent-send.sh +++ b/packages/mosaic/framework/tools/tmux/agent-send.sh @@ -126,7 +126,16 @@ if [ -z "$SRC_LABEL" ]; then src_host=$(hostname -s 2>/dev/null || echo "?") src_sess=${MOSAIC_AGENT_NAME:-} if [ -z "$src_sess" ]; then - src_sess=$(tmux display-message -p '#S' 2>/dev/null || echo "?") + if [ -n "${TMUX:-}" ]; then + # Inside tmux: display-message resolves against this client's own session. + src_sess=$(tmux display-message -p '#S' 2>/dev/null || echo "?") + else + # Outside tmux with no name: display-message reports the LAST-ACTIVE + # session — someone else's identity (measured 2026-08-20: a nameless + # non-tmux sender was stamped "peggy", a live seat, forged silently). + # Stamp an explicit unverified label instead; deliberate senders use -S. + src_sess="unverified" + fi fi SRC_LABEL="${src_host}:${src_sess}" fi diff --git a/packages/mosaic/framework/tools/tmux/agent-send.test.sh b/packages/mosaic/framework/tools/tmux/agent-send.test.sh index 87a1ed90..69fbc757 100755 --- a/packages/mosaic/framework/tools/tmux/agent-send.test.sh +++ b/packages/mosaic/framework/tools/tmux/agent-send.test.sh @@ -61,8 +61,15 @@ no() { FAIL=$((FAIL+1)); printf 'FAIL %s\n %s\n' "$1" "$2"; } # Run the tool with the stub injected; echoes captured payload on stdout. run() { AGENT_SEND_SENDER="$STUB" bash "$TOOL" -S a:src -n dsthost "$@"; } -run_auto() { - env -u MOSAIC_AGENT_NAME \ +# Hermetic auto-label runs: TMUX is controlled explicitly so results never +# depend on whether the caller running this suite sits inside tmux. +run_auto() { # models a sender OUTSIDE tmux (no client context) + env -u MOSAIC_AGENT_NAME -u TMUX \ + AGENT_SEND_SENDER="$STUB" PATH="$FAKE_BIN:$PATH" \ + bash "$TOOL" -n dsthost "$@" +} +run_auto_in_tmux() { # models a sender INSIDE tmux (client context exists) + env -u MOSAIC_AGENT_NAME TMUX=/fake/socket \ AGENT_SEND_SENDER="$STUB" PATH="$FAKE_BIN:$PATH" \ bash "$TOOL" -n dsthost "$@" } @@ -145,7 +152,9 @@ want="[$src_host:authoritative-agent -> dsthost:mos] env identity" || no "MOSAIC_AGENT_NAME is authoritative across sockets" "got=[$got] want=[$want]" # 9. Without the env identity, self-lookup uses local tmux, not destination -L. -got=$(FAKE_TMUX_MODE=sessions run_auto -L destination-socket -s mos -m "local fallback") +# Sender is INSIDE tmux: the only context where display-message self-lookup +# is safe (it resolves against this client's own session). +got=$(FAKE_TMUX_MODE=sessions run_auto_in_tmux -L destination-socket -s mos -m "local fallback") want="[$src_host:local-agent -> dsthost:mos] local fallback" [ "$got" = "$want" ] && ok "cross-socket fallback uses local sender session" \ || no "cross-socket fallback uses local sender session" "got=[$got] want=[$want]" @@ -153,8 +162,17 @@ want="[$src_host:local-agent -> dsthost:mos] local fallback" && ok "cross-socket fallback rejects destination holder identity" \ || no "cross-socket fallback rejects destination holder identity" "got=[$got]" +# 9b. NO tmux context: display-message answers with the LAST-ACTIVE session — +# someone else's identity (forgery vector). The label must be `unverified`, +# never a borrowed name, even though a tmux server exists here and the fake +# would confidently answer `local-agent`. +got=$(FAKE_TMUX_MODE=sessions run_auto -s mos -m "no tmux context") +want="[$src_host:unverified -> dsthost:mos] no tmux context" +[ "$got" = "$want" ] && ok "no-tmux sender labeled unverified, never borrowed" \ + || no "no-tmux sender labeled unverified, never borrowed" "got=[$got] want=[$want]" + # 10. If neither env nor local tmux identifies the sender, preserve '?'. -got=$(FAKE_TMUX_MODE=unavailable run_auto -L destination-socket -s mos -m "unknown fallback") +got=$(FAKE_TMUX_MODE=unavailable run_auto_in_tmux -L destination-socket -s mos -m "unknown fallback") want="[$src_host:? -> dsthost:mos] unknown fallback" [ "$got" = "$want" ] && ok "unknown sender falls back to ?" \ || no "unknown sender falls back to ?" "got=[$got] want=[$want]"