summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/tests/test_security_regressions.py
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-node/tests/test_security_regressions.py')
-rw-r--r--packages/meshbay-node/tests/test_security_regressions.py75
1 files changed, 75 insertions, 0 deletions
diff --git a/packages/meshbay-node/tests/test_security_regressions.py b/packages/meshbay-node/tests/test_security_regressions.py
index 9552848..a6d69c6 100644
--- a/packages/meshbay-node/tests/test_security_regressions.py
+++ b/packages/meshbay-node/tests/test_security_regressions.py
@@ -10,6 +10,7 @@ If one of these starts failing, a fix has been reverted. Do not "fix" the test.
"""
import base64
+import struct
from pathlib import Path
import pytest
@@ -465,6 +466,80 @@ def test_peer_errors_do_not_leak_internals():
assert '"detail": str(e)' not in source
+def test_pre_handshake_message_budget_is_small():
+ """
+ H6: the frame limit was a flat 64 MB applied before authentication, so an
+ unauthenticated peer could announce a huge frame and dribble bytes into it.
+ """
+ from meshbay_node.transport.webrtc_server import (
+ MAX_MSG, PRE_HANDSHAKE_MAX_MSG, _DataChannelBuffer,
+ )
+ assert PRE_HANDSHAKE_MAX_MSG <= 1024 * 1024
+ assert PRE_HANDSHAKE_MAX_MSG < MAX_MSG
+
+ buf = _DataChannelBuffer(max_message=PRE_HANDSHAKE_MAX_MSG)
+ buf.feed(struct.pack(">I", PRE_HANDSHAKE_MAX_MSG + 1) + b"x")
+ with pytest.raises(ValueError):
+ list(buf.messages())
+
+
+def test_stream_segment_is_not_synchronous():
+ """
+ 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.
+
+ 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.
+ """
+ import ast
+ import inspect
+ from meshbay_node.transport.webrtc_server import WebRTCPeerSession
+
+ assert inspect.iscoroutinefunction(WebRTCPeerSession._do_stream_segment_async)
+
+ source = (Path(__file__).parent.parent / "src" / "meshbay_node"
+ / "transport" / "webrtc_server.py").read_text()
+ tree = ast.parse(source)
+ blocking = [
+ node for node in ast.walk(tree)
+ if isinstance(node, ast.Call)
+ and isinstance(node.func, ast.Attribute)
+ and node.func.attr == "run"
+ and isinstance(node.func.value, ast.Name)
+ and node.func.value.id == "subprocess"
+ ]
+ assert not blocking, "blocking subprocess.run() in the event loop"
+ assert "_transcode_sem" in source, "ffmpeg spawns must be capped"
+
+
+def test_pre_proof_fetches_are_bounded():
+ """C4: the pre-proof bundle window is a disclosure surface; bound it."""
+ from meshbay_node.transport.webrtc_server import MAX_PRE_PROOF_FETCHES
+ assert 0 < MAX_PRE_PROOF_FETCHES <= 10
+
+
+def test_node_admin_ui_requires_token():
+ """
+ 11.5.3: "localhost only" is not authentication. Any local process — or a
+ rebound browser page — could re-initialise a group's GEK and read the audit log.
+ """
+ from fastapi.testclient import TestClient
+ from meshbay_node.ui.app import create_ui_app
+
+ app = create_ui_app({"status": "running", "groups_ctx": {},
+ "indexes": {}, "ui_token": "secret-token"})
+ client = TestClient(app)
+
+ assert client.get("/api/status").status_code == 403
+ assert client.get("/api/status?t=wrong").status_code == 403
+ assert client.get("/api/config?t=wrong").status_code == 403
+ assert client.get("/api/status?t=secret-token").status_code == 200
+ assert client.get(
+ "/api/status", headers={"X-MeshBay-Token": "secret-token"}
+ ).status_code == 200
+
+
def test_admin_ui_escapes_filenames(tmp_path):
"""
H2: filenames are chosen by any group member and were rendered into the