Skip to content

fix(core): quarantine WireMeshTransport sessions until connection approval - #60

Merged
Mearman merged 2 commits into
mainfrom
fix/wire-mesh-transport-quarantine-unapproved-sessions
Sep 11, 2026
Merged

fix(core): quarantine WireMeshTransport sessions until connection approval#60
Mearman merged 2 commits into
mainfrom
fix/wire-mesh-transport-quarantine-unapproved-sessions

Conversation

@Mearman

@Mearman Mearman commented Sep 11, 2026

Copy link
Copy Markdown
Member

Found while validating #59 (the remaining-bridges rollout) via Codex's own security review of that PR: any listener-accepted connection (the coordinator port, or any addListener-created listener, including a non-loopback one via mesh_listen) is promoted to a fully trusted, routed session the instant its TLS handshake completes, before any human ever approves it via connect_request. This is already live on main via #58 (the mcp bridge cutover), since it's a gap in WireMeshTransport itself (#57), not anything specific to #58 or #59 -- those PRs just widen how many bridges are exposed to it.

TLS only proves which key the far side holds, never that a human approved it as a mesh member. The old TlsTransport substrate's own coordinator socket only ever parsed introduce/connect_request, dropping anything else; the new unified accept path lost that boundary and routes every message type straight to mesh-store's own onMessage. An unapproved peer could forge a state_update (or a room send, a DM) into the mesh without ever sending connect_request at all.

Restores the boundary: a listener-accepted session is quarantined to introduce (unchanged, still ungated -- the existing coordinator-handoff trust boundary) or connect_request (now blocks in place on the human decision, resumed by acceptConnection/rejectConnection) until approved. A peer's own data-port dial, the second half of an already-approved pairing, is untouched. Added a test that forges a state_update from a connection that never sent connect_request, confirming it's refused rather than reaching mesh-store's state -- confirmed this test actually fails without the fix (reverted the gate locally, watched it hang/fail, restored it).

Second, unrelated thing found while verifying this in a clean worktree (not the same stale one I'd been building in all session, which had a leftover dist/ masking it): mesh-smoke.integration.test.ts require()s compiled dist/ output directly in its spawned subprocesses, so it fails with MODULE_NOT_FOUND under the plain-source pnpm test from #58 unless a build already happened. Renamed to mesh-smoke.runner.ts with its own pnpm test:smoke script (builds first) and a CI step for it, matching delivery-receipt.runner.ts's own precedent, so it isn't silently dropped from CI the same way federation/visibility briefly were.

…roval

Every listener (the coordinator port, and any addListener-created listener,
including a non-loopback one exposed via mesh_listen) promoted an accepted
connection to a fully trusted, routed session the instant its TLS handshake
completed. TLS only proves which key the far side holds, never that a human
approved it as a mesh member -- the only gate that ever existed was
intercepting connect_request specifically, but any other message type
(state_update, a room send, a DM) reached mesh-store's own onMessage
unfiltered, letting an unapproved peer forge arbitrary mesh state before ever
requesting a connection.

The previous TlsTransport substrate never had this gap: its own coordinator
socket only ever parsed introduce/connect_request, dropping anything else,
while a separate data connection (reachable only via a port learned through
an already-approved introduction) carried full routing. Restore that
boundary under the new substrate's unified accept path: a listener-accepted
session is quarantined -- restricted to introduce (the pre-existing,
ungated coordinator-handoff path, unchanged) or connect_request (now
blocking in place on the human decision Promise acceptConnection/
rejectConnection resolve) -- and only promoted to full trust once approved.
A session's own data-port dial (the second half of an already-approved
mesh pairing) is unaffected, matching its pre-existing trust model exactly.

Adds a test that forges a state_update from a connection which never sent
connect_request at all, confirming it's refused rather than reaching
mesh-store's state.
Every spawned child in this file require()s compiled dist/ output directly
(exercising the actual built artifact across a real process boundary, not
TS source), unlike every other *.test.ts file since the switch to running
the suite straight from source via tsx. Left matching that general glob, it
fails with a confusing MODULE_NOT_FOUND the moment dist/ isn't already
present from an earlier, unrelated build.

Renamed to mesh-smoke.runner.ts (matching delivery-receipt.runner.ts's own
naming for the same reason) so plain pnpm test never picks it up, with its
own pnpm test:smoke script that builds first. CI's test job runs it as an
explicit step so this coverage isn't silently dropped from CI the same way
federation/visibility coverage previously was.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 11, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
🔒 Security Review Completed 2026-09-11T19:14:25.260106Z c93ae99 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@Mearman
Mearman merged commit 003bba3 into main Sep 11, 2026
5 checks passed
@Mearman
Mearman deleted the fix/wire-mesh-transport-quarantine-unapproved-sessions branch September 11, 2026 19:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant