test_q1_app_security.py python
97 lines 2.7 KB
Raw
sha256:a78e7e5a8740e03315f325d19edeb3aa1b306b3337d04abbaa9a9e0f3bbeb7a1 docs: MuseHub-first before ISR #74 — staging solidify NEXT Human 1 day ago
1 """Security tests for Track Q / Q1 overseer app (§Q0.12)."""
2
3 from __future__ import annotations
4
5 from pathlib import Path
6
7 import pytest
8
9 from tests.fixtures.app import seed_app_repo, start_test_app
10
11
12 FORBIDDEN_ROUTES = [
13 "/api/init",
14 "/api/sync",
15 "/api/verify-step",
16 "/api/route",
17 "/api/merge",
18 ]
19
20
21 def test_missing_bearer_returns_401(tmp_path: Path) -> None:
22 seed_app_repo(tmp_path)
23 handle, client = start_test_app(tmp_path)
24 try:
25 status, payload = client.request("GET", "/api/status", headers={})
26 assert status == 401
27 assert payload["error"] == "auth"
28 assert payload["exit_code"] is None
29 finally:
30 handle.shutdown()
31
32
33 def test_bad_csrf_on_post_returns_403(tmp_path: Path) -> None:
34 seed_app_repo(tmp_path)
35 handle, client = start_test_app(tmp_path)
36 try:
37 status, payload = client.request(
38 "POST",
39 "/api/governance-sync",
40 body={"write": False},
41 headers={
42 "Authorization": f"Bearer {client.session}",
43 "X-Overseer-CSRF": "wrong",
44 "Content-Type": "application/json",
45 },
46 )
47 assert status == 403
48 assert payload["error"] == "csrf"
49 finally:
50 handle.shutdown()
51
52
53 def test_disallowed_origin_returns_403(tmp_path: Path) -> None:
54 seed_app_repo(tmp_path)
55 handle, client = start_test_app(tmp_path)
56 try:
57 status, payload = client.get(
58 "/api/health",
59 origin="http://evil.example:8765",
60 )
61 assert status == 403
62 assert payload["error"] == "origin"
63 finally:
64 handle.shutdown()
65
66
67 @pytest.mark.parametrize("path", FORBIDDEN_ROUTES)
68 def test_forbidden_routes_not_present(tmp_path: Path, path: str) -> None:
69 seed_app_repo(tmp_path)
70 handle, client = start_test_app(tmp_path)
71 try:
72 status, payload = client.get(path)
73 assert status == 404
74 assert payload["error"] == "not_found"
75 finally:
76 handle.shutdown()
77
78
79 def test_static_ui_has_no_local_storage_persistence() -> None:
80 from tools.app.server import STATIC_ROOT
81
82 js = (STATIC_ROOT / "assets" / "app.js").read_text(encoding="utf-8")
83 assert "localStorage" not in js
84 assert "sessionStorage" not in js
85 assert "document.cookie" not in js
86
87
88 def test_doc_path_traversal_refused(tmp_path: Path) -> None:
89 seed_app_repo(tmp_path)
90 handle, client = start_test_app(tmp_path)
91 try:
92 # Closed endpoint set — no arbitrary path query API.
93 status, payload = client.get("/api/docs/roadmap")
94 assert status == 200
95 assert ".." not in payload["result"]["path"]
96 finally:
97 handle.shutdown()
File History 1 commit
sha256:a78e7e5a8740e03315f325d19edeb3aa1b306b3337d04abbaa9a9e0f3bbeb7a1 docs: MuseHub-first before ISR #74 — staging solidify NEXT Human 1 day ago