aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/tests
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-node/tests')
-rw-r--r--packages/meshbay-node/tests/test_audit.py21
-rw-r--r--packages/meshbay-node/tests/test_cli_dispatch.py6
-rw-r--r--packages/meshbay-node/tests/test_security_regressions.py62
3 files changed, 46 insertions, 43 deletions
diff --git a/packages/meshbay-node/tests/test_audit.py b/packages/meshbay-node/tests/test_audit.py
index b2ed15e..8755be2 100644
--- a/packages/meshbay-node/tests/test_audit.py
+++ b/packages/meshbay-node/tests/test_audit.py
@@ -53,6 +53,27 @@ async def test_filter_by_user(audit):
@pytest.mark.asyncio
+async def test_pagination_newest_first(audit):
+ for i in range(5):
+ await audit.log_event(user_id="u1", event="connect", detail=f"e{i}")
+ # distinct timestamps so ORDER BY is deterministic
+ await audit._db.execute(
+ "UPDATE audit_log SET timestamp = ? WHERE detail = ?",
+ (1000 + i, f"e{i}"))
+ await audit._db.commit()
+
+ page1 = await audit.get_entries(limit=2, offset=0)
+ page2 = await audit.get_entries(limit=2, offset=2)
+ page3 = await audit.get_entries(limit=2, offset=4)
+
+ assert [e.detail for e in page1] == ["e4", "e3"] # newest first
+ assert [e.detail for e in page2] == ["e2", "e1"]
+ assert [e.detail for e in page3] == ["e0"] # last, partial page
+ # offset past the end is empty, not an error
+ assert await audit.get_entries(limit=2, offset=99) == []
+
+
+@pytest.mark.asyncio
async def test_entry_count(audit):
assert await audit.entry_count() == 0
await audit.log_event(user_id="u1", event="connect")
diff --git a/packages/meshbay-node/tests/test_cli_dispatch.py b/packages/meshbay-node/tests/test_cli_dispatch.py
index d912b43..b676546 100644
--- a/packages/meshbay-node/tests/test_cli_dispatch.py
+++ b/packages/meshbay-node/tests/test_cli_dispatch.py
@@ -24,7 +24,6 @@ from meshbay_node import daemon as daemon_mod
# would otherwise stop for a confirmation nobody can type in a test.
VERBS = [
["status"],
- ["ui"],
["group", "list"],
["group", "add"], # missing --dir: usage, then exit
["gek", "init"],
@@ -135,6 +134,11 @@ def test_the_verb_list_here_matches_the_parser():
f"CLI verbs with no dispatch test: {sorted(untested)} — add them to "
f"VERBS above")
+ # The server-rendered admin UI (and its `ui` verb) were removed in
+ # docs/refactor-node-ui.md phase 5. The control API stays; the browser
+ # page does not.
+ assert "ui" not in declared, "the `ui` verb came back"
+
@pytest.mark.parametrize("argv,verb", [
(["reload"], "reload"),
diff --git a/packages/meshbay-node/tests/test_security_regressions.py b/packages/meshbay-node/tests/test_security_regressions.py
index 725b800..10182d3 100644
--- a/packages/meshbay-node/tests/test_security_regressions.py
+++ b/packages/meshbay-node/tests/test_security_regressions.py
@@ -650,56 +650,34 @@ def test_node_admin_ui_requires_token():
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/groups?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):
+def test_node_control_api_serves_no_html():
"""
- H2: filenames are chosen by any group member and were rendered into the
- localhost admin UI unescaped, giving script execution against an
- unauthenticated admin API.
- """
- from meshbay_node.ui.app import _render_page
-
- payload = '<img src=x onerror="fetch(1)">'
- index = GroupIndex(group_id="g" * 32, sk_node=Ed25519PrivateKey.generate())
- index.add_entry(IndexEntry(
- id="0" * 64, name=payload, path="", size=1, type="video", added_at=0,
- ))
-
- html = _render_page({
- "status": "running",
- "groups_ctx": {"g" * 32: {"index": index, "roots": one_root(tmp_path)}},
- "indexes": {"g" * 32: index},
- })
-
- assert payload not in html, "filename rendered unescaped — stored XSS (H2)"
- assert "&lt;img" in html, "filename should appear escaped"
+ H2 was stored XSS in the server-rendered admin dashboard: a member-chosen
+ filename, or a hub-supplied username, landed in an HTML page on the
+ operator's machine unescaped. That dashboard is gone
+ (docs/refactor-node-ui.md phase 5) — the control API is JSON only, so there
+ is no server-side template to inject into. The Node page that replaced it
+ ships in the desktop client and escapes by default (Preact).
-
-def test_admin_ui_escapes_roster_usernames(tmp_path):
- """
- H2 again, for the roster: usernames originate at the hub and land on the
- operator's own admin page, which can re-key groups and read the audit log.
+ This locks the removal in: the HTML routes stay 404, and the render helpers
+ stay deleted so nothing reintroduces a template by importing one.
"""
- from meshbay_node.ui.app import _render_page
+ import meshbay_node.ui.app as ui_app
+ from fastapi.testclient import TestClient
- payload = '<img src=x onerror="fetch(1)">'
- html = _render_page(
- {"status": "running", "groups_ctx": {}, "indexes": {}},
- {
- "identities": {"u1": {"user_id": "u1", "username": payload,
- "pk_ed25519": "AAA", "pinned_at": "now",
- "pinned_via": "code"}},
- "members": [{"group_id": "", "user_id": "u1", "role": "operator",
- "status": "active"}],
- "invites": [],
- },
- )
+ app = ui_app.create_ui_app({"status": "running", "groups_ctx": {},
+ "indexes": {}, "ui_token": "t"})
+ client = TestClient(app)
+ for path in ("/", "/audit", "/dashboard"):
+ assert client.get(f"{path}?t=t").status_code == 404, path
- assert payload not in html, "username rendered unescaped — stored XSS (H2)"
- assert "&lt;img" in html
+ for gone in ("_render_page", "_render_audit_page", "_render_roster",
+ "_AUDIT_HTML"):
+ assert not hasattr(ui_app, gone), f"{gone} came back — HTML surface"