fix(#808): never borrow a session identity for non-tmux senders
ci/woodpecker/pr/ci Pipeline was successful
ci/woodpecker/pr/ci Pipeline was successful
When MOSAIC_AGENT_NAME is unset and the sender has no tmux client context ($TMUX empty), agent-send.sh fell back to 'tmux display-message -p #S', which outside tmux reports the LAST-ACTIVE session on the server. A nameless sender was silently stamped as whichever seat touched tmux most recently, forging that seat's identity in the preamble. Incident (2026-08-20, host sb-it-1-dt): a nameless non-tmux sender was stamped 'peggy', a live seat, on a message delivered TO peggy. Reproduced end-to-end before the fix; capture in the seat evidence note. Fix: the display-message fallback now runs only when $TMUX is set (inside tmux it resolves the caller's own session — the case #808 built it for). Outside tmux with no name, the label is host:unverified. Deliberate identity remains available via -S. Chosen over refusing the send: nameless senders are legitimate; the defect was borrowing an identity, not lacking one. Tests: suite made hermetic (TMUX controlled explicitly; results no longer depend on the caller's context — previously tests 9/10 inherited the caller's TMUX). New case 9b: no-tmux sender is labeled unverified and never a borrowed name (red on original: host:local-agent borrowed from the fake tmux server; green after). 19/19 in both caller contexts.
This commit is contained in:
@@ -126,7 +126,16 @@ if [ -z "$SRC_LABEL" ]; then
|
|||||||
src_host=$(hostname -s 2>/dev/null || echo "?")
|
src_host=$(hostname -s 2>/dev/null || echo "?")
|
||||||
src_sess=${MOSAIC_AGENT_NAME:-}
|
src_sess=${MOSAIC_AGENT_NAME:-}
|
||||||
if [ -z "$src_sess" ]; then
|
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
|
fi
|
||||||
SRC_LABEL="${src_host}:${src_sess}"
|
SRC_LABEL="${src_host}:${src_sess}"
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -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 the tool with the stub injected; echoes captured payload on stdout.
|
||||||
run() { AGENT_SEND_SENDER="$STUB" bash "$TOOL" -S a:src -n dsthost "$@"; }
|
run() { AGENT_SEND_SENDER="$STUB" bash "$TOOL" -S a:src -n dsthost "$@"; }
|
||||||
run_auto() {
|
# Hermetic auto-label runs: TMUX is controlled explicitly so results never
|
||||||
env -u MOSAIC_AGENT_NAME \
|
# 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" \
|
AGENT_SEND_SENDER="$STUB" PATH="$FAKE_BIN:$PATH" \
|
||||||
bash "$TOOL" -n dsthost "$@"
|
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]"
|
|| 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.
|
# 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"
|
want="[$src_host:local-agent -> dsthost:mos] local fallback"
|
||||||
[ "$got" = "$want" ] && ok "cross-socket fallback uses local sender session" \
|
[ "$got" = "$want" ] && ok "cross-socket fallback uses local sender session" \
|
||||||
|| no "cross-socket fallback uses local sender session" "got=[$got] want=[$want]"
|
|| 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" \
|
&& ok "cross-socket fallback rejects destination holder identity" \
|
||||||
|| no "cross-socket fallback rejects destination holder identity" "got=[$got]"
|
|| 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 '?'.
|
# 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"
|
want="[$src_host:? -> dsthost:mos] unknown fallback"
|
||||||
[ "$got" = "$want" ] && ok "unknown sender falls back to ?" \
|
[ "$got" = "$want" ] && ok "unknown sender falls back to ?" \
|
||||||
|| no "unknown sender falls back to ?" "got=[$got] want=[$want]"
|
|| no "unknown sender falls back to ?" "got=[$got] want=[$want]"
|
||||||
|
|||||||
Reference in New Issue
Block a user