aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/tests/test_security_regressions.py
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-09-07 17:45:54 +0200
committerChristophe Besson <cbesson@gmail.com>2026-09-07 17:45:54 +0200
commit77dd077491aea50e71e21e0d17555a2f91cf818b (patch)
tree87b88674b3210541f413b4992750dcb2807b8f12 /packages/meshbay-node/tests/test_security_regressions.py
parentd1f998b42137465b610667439527917a00030b4d (diff)
downloadmeshbay-77dd077491aea50e71e21e0d17555a2f91cf818b.tar.gz
refactor(mnp)!: remove stream_seg, the last unencrypted content message
`stream_seg` answered with an MPEG-TS segment as base64 with no encryption at all — the one message on the content plane that never went through a GEK-derived key. Live on both transports, answering any authenticated member. It predates `stream_data`, which does the same job properly (`chunk_ciphertext`, keyed per segment, AES-256-GCM) and has since Phase 12. Its only browser caller, `fetchStreamSegment`, was defined and never once invoked — a plaintext media endpoint with no client. Removed rather than repaired. Gone with it: `_extract_segment` and the ffmpeg semaphore in quic_server, the `fetch_stream_segment` QUIC client method, and `_b64decode` in transport.js, which had no other caller. The H6 regression test lived on this handler — it pinned `_do_stream_segment_async` to a coroutine so `subprocess.run` could not stall the event loop for thirty seconds per request. It is replaced by the property that outlives the handler: no transport carries media outside an AEAD, asserted on `stream_seg` and `data_b64` across all three transport modules. The half of H6 that survives — the live streaming path still spawns ffmpeg — keeps its own test. BREAKING CHANGE: `stream_seg` is no longer answered on either transport. No shipping client sends it. Recorded as part of MNP 2.0, whose other half — the sealed upload — carries the version bump. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AsoWC3GmhNdwVFomW3QjH3
Diffstat (limited to 'packages/meshbay-node/tests/test_security_regressions.py')
-rw-r--r--packages/meshbay-node/tests/test_security_regressions.py48
1 files changed, 38 insertions, 10 deletions
diff --git a/packages/meshbay-node/tests/test_security_regressions.py b/packages/meshbay-node/tests/test_security_regressions.py
index 1a318f7..fa821d9 100644
--- a/packages/meshbay-node/tests/test_security_regressions.py
+++ b/packages/meshbay-node/tests/test_security_regressions.py
@@ -743,20 +743,48 @@ def test_pre_handshake_message_budget_is_small():
list(buf.messages())
-def test_stream_segment_is_not_synchronous():
+def test_no_transport_ships_media_outside_the_aead():
"""
- H6: _do_stream_segment ran subprocess.run(timeout=30) inside the event loop,
- stalling every peer on the node for up to thirty seconds per request.
+ `stream_seg` served an MPEG-TS segment as base64 with no encryption at all
+ — the one content-plane message that never went through a GEK-derived key,
+ on both transports, answering any authenticated member. Its browser caller
+ was defined and never invoked. Removed in MNP 2.0 rather than repaired:
+ `stream_data` already does the job under `chunk_ciphertext`.
- Asserts the property (the worker is a coroutine, ffmpeg is spawned through
- asyncio) rather than grepping for "subprocess.run" — which also matches the
- comment that documents the old behaviour.
+ Asserted as the property, not as "the function is gone": what matters is
+ that no transport has a field carrying media bytes past the AEAD. The old
+ H6 test lived here — it pinned `_do_stream_segment_async` to a coroutine so
+ ffmpeg could not block the event loop — and the handler outliving that
+ concern is exactly what this replaces.
"""
- import ast
- import inspect
- from meshbay_node.transport.webrtc_server import WebRTCPeerSession
+ import re
+
+ from meshbay_common.protocol import MNP
+
+ assert not hasattr(MNP, "STREAM_SEGMENT"), (
+ "the constant outliving the handlers is how a deleted endpoint keeps "
+ "looking like part of the wire contract")
+
+ root = Path(__file__).parent.parent / "src" / "meshbay_node" / "transport"
+ for name in ("webrtc_server.py", "quic_server.py", "quic_client.py"):
+ source = (root / name).read_text(encoding="utf-8")
+ # Word boundaries: `_stream_segments` and `STREAM_SEGMENT_SIZE` belong
+ # to the live `stream_data` path, which is encrypted and stays.
+ assert not re.search(r"\bstream_seg\b", source), (
+ f"{name} still speaks stream_seg")
+ assert not re.search(r"\bSTREAM_SEGMENT\b", source), (
+ f"{name} still names the removed type")
+ assert "data_b64" not in source, (
+ f"{name} carries a base64 media field — media leaves this node "
+ "encrypted or not at all")
- assert inspect.iscoroutinefunction(WebRTCPeerSession._do_stream_segment_async)
+
+def test_ffmpeg_never_blocks_the_event_loop():
+ """
+ H6, the half that survives `stream_seg`: the live streaming path still
+ spawns ffmpeg, and a synchronous spawn stalls every peer on the node.
+ """
+ import ast
source = (Path(__file__).parent.parent / "src" / "meshbay_node"
/ "transport" / "webrtc_server.py").read_text(encoding="utf-8")