harden: reject whitespace-only tmux socket_name in roster-v2 (silently selects default server) #794

Open
opened 2026-07-16 20:01:19 +00:00 by jason.woltje · 0 comments
Owner

Summary

requiredTmuxSocket() in packages/mosaic/src/fleet/roster-v2.ts trims socket_name before
validating it against a permissive regex that accepts the empty string, so a whitespace-only value
(e.g. socket_name: " ") is normalized to "" and silently selects the operator's default tmux
server instead of being rejected.

Detail (on main)

const TMUX_SOCKET_IDENTIFIER = /^[A-Za-z0-9_.-]*$/;   // '*' → empty string matches

function requiredTmuxSocket(value, label) {
  if (typeof value !== 'string') throw new RosterV2ValidationError(...);
  const result = value.trim();                         // " " → ""
  if (!TMUX_SOCKET_IDENTIFIER.test(result)) throw ...; // "" passes
  return result;                                        // returns ""
}

Consumption confirms the impact — comms-onboarding.ts:

if (rosterSocket) parts.push('-L', shellArg(rosterSocket));  // "" is falsy → -L omitted → default server

and the intended contract is explicit in tests: "omits -L only for the literal default socket."

So a whitespace-only socket_name bypasses the "explicit decision to use the default server" and can
point fleet tmux operations (message/session/runtime) at an unintended shared namespace.

Severity

Low. Requires an operator to hand-author a whitespace-only socket_name; no privilege escalation. This
is input-validation hardening, not an active exploit. (Flagged by an automated Codex pass whose
"medium/blocker" rating is inflated for this path.)

Proposed fix

  • Accept an empty socket only when the source string was exactly empty; reject any non-empty value
    whose trimmed form differs from the source (i.e. reject whitespace-only and whitespace-padded values).
  • Add YAML + JSON parser tests for whitespace-only and whitespace-padded socket_name, asserting
    RosterV2ValidationError.

Provenance

Surfaced as a byproduct of an independent review; pre-existing on main, not introduced by any
open PR (noted during PR #793 review, which it does not belong to). Filed to preserve the finding.

## Summary `requiredTmuxSocket()` in `packages/mosaic/src/fleet/roster-v2.ts` trims `socket_name` before validating it against a **permissive** regex that accepts the empty string, so a whitespace-only value (e.g. `socket_name: " "`) is normalized to `""` and silently selects the operator's **default** tmux server instead of being rejected. ## Detail (on `main`) ``` const TMUX_SOCKET_IDENTIFIER = /^[A-Za-z0-9_.-]*$/; // '*' → empty string matches function requiredTmuxSocket(value, label) { if (typeof value !== 'string') throw new RosterV2ValidationError(...); const result = value.trim(); // " " → "" if (!TMUX_SOCKET_IDENTIFIER.test(result)) throw ...; // "" passes return result; // returns "" } ``` Consumption confirms the impact — `comms-onboarding.ts`: ``` if (rosterSocket) parts.push('-L', shellArg(rosterSocket)); // "" is falsy → -L omitted → default server ``` and the intended contract is explicit in tests: *"omits -L only for the literal default socket."* So a whitespace-only `socket_name` bypasses the "explicit decision to use the default server" and can point fleet tmux operations (message/session/runtime) at an unintended shared namespace. ## Severity Low. Requires an operator to hand-author a whitespace-only `socket_name`; no privilege escalation. This is input-validation hardening, not an active exploit. (Flagged by an automated Codex pass whose "medium/blocker" rating is inflated for this path.) ## Proposed fix - Accept an empty socket only when the source string was **exactly** empty; reject any non-empty value whose trimmed form differs from the source (i.e. reject whitespace-only and whitespace-padded values). - Add YAML + JSON parser tests for whitespace-only and whitespace-padded `socket_name`, asserting `RosterV2ValidationError`. ## Provenance Surfaced as a byproduct of an independent review; **pre-existing on `main`**, not introduced by any open PR (noted during PR #793 review, which it does not belong to). Filed to preserve the finding.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mosaicstack/stack#794