from __future__ import annotations import json import threading import urllib.error import urllib.request from email.message import Message from http.server import ThreadingHTTPServer from cognitive_agent.auth import AuthConfig, AuthRuntime from cognitive_agent.server import SeatDemoHandler def _auth(*, enabled: bool) -> AuthRuntime: return AuthRuntime( AuthConfig( enabled=enabled, username="test-user" if enabled else "", password_hash="unused" if enabled else "", session_secret=b"s" * 64 if enabled else b"", session_ttl_seconds=3600, login_attempts=5, login_window_seconds=300, requests_per_minute=120, trust_proxy_headers=False, ) ) class MutationSecurityHandler(SeatDemoHandler): auth = _auth(enabled=False) assistant_calls = 0 internal_calls = 0 last_hardware_bypass = True def _handle_assistant_stream(self) -> None: type(self).assistant_calls += 1 type(self).last_hardware_bypass = self._hardware_access_bypass_allowed() self._read_json_body() self._json({"handled": True}) def _handle_codex_vehicle_control(self) -> None: type(self).internal_calls += 1 self._read_json_body() self._json({"handled": True}) class TestMutationRequestSecurity: @classmethod def setup_class(cls) -> None: cls.server = ThreadingHTTPServer(("127.0.0.1", 0), MutationSecurityHandler) cls.thread = threading.Thread(target=cls.server.serve_forever, daemon=True) cls.thread.start() cls.authority = f"127.0.0.1:{cls.server.server_port}" cls.base_url = f"http://{cls.authority}" @classmethod def teardown_class(cls) -> None: cls.server.shutdown() cls.server.server_close() cls.thread.join(timeout=3) def setup_method(self) -> None: MutationSecurityHandler.assistant_calls = 0 MutationSecurityHandler.internal_calls = 0 MutationSecurityHandler.last_hardware_bypass = True def post( self, path: str, *, content_type: str | None = "application/json", origin: str | None = None, ) -> tuple[int, dict[str, object]]: headers = {} if content_type is not None: headers["Content-Type"] = content_type if origin is not None: headers["Origin"] = origin request = urllib.request.Request( f"{self.base_url}{path}", data=b"{}", headers=headers, method="POST", ) try: with urllib.request.urlopen(request, timeout=3) as response: return response.status, json.loads(response.read().decode("utf-8")) except urllib.error.HTTPError as exc: return exc.code, json.loads(exc.read().decode("utf-8")) def get(self, path: str) -> tuple[int, dict[str, object]]: try: with urllib.request.urlopen(f"{self.base_url}{path}", timeout=3) as response: return response.status, json.loads(response.read().decode("utf-8")) except urllib.error.HTTPError as exc: return exc.code, json.loads(exc.read().decode("utf-8")) def test_same_origin_json_assistant_request_is_accepted(self) -> None: status, body = self.post( "/api/assistant/stream", content_type="application/json; charset=utf-8", origin=self.base_url, ) assert status == 200 assert body == {"handled": True} assert MutationSecurityHandler.assistant_calls == 1 def test_cross_origin_alias_is_rejected_before_assistant_execution(self) -> None: status, body = self.post( "/api/assistant/stream", origin=f"http://localhost:{self.server.server_port}", ) assert status == 403 assert body["error"] == "cross_origin_request" assert MutationSecurityHandler.assistant_calls == 0 def test_origin_scheme_and_port_must_match(self) -> None: for origin in ( f"https://{self.authority}", f"http://127.0.0.1:{self.server.server_port + 1}", "null", ): status, body = self.post("/api/assistant/stream", origin=origin) assert status == 403 assert body["error"] == "cross_origin_request" assert MutationSecurityHandler.assistant_calls == 0 def test_non_json_assistant_request_is_rejected_before_body_parsing(self) -> None: for content_type in (None, "text/plain", "application/x-www-form-urlencoded"): status, body = self.post( "/api/assistant/stream", content_type=content_type, origin=self.base_url, ) assert status == 415 assert body["error"] == "unsupported_media_type" assert MutationSecurityHandler.assistant_calls == 0 def test_no_origin_local_json_requests_remain_compatible(self) -> None: assistant_status, _ = self.post("/api/assistant/stream") internal_status, _ = self.post("/api/internal/codex/vehicle-control") assert assistant_status == 200 assert internal_status == 200 assert MutationSecurityHandler.assistant_calls == 1 assert MutationSecurityHandler.internal_calls == 1 def test_auth_disabled_never_grants_implicit_hardware_bypass(self) -> None: status, _ = self.post("/api/assistant/stream") assert status == 200 assert MutationSecurityHandler.last_hardware_bypass is False def test_vehicle_ota_download_is_public_but_workbench_state_remains_private(self) -> None: MutationSecurityHandler.auth = _auth(enabled=True) try: download_status, download_body = self.get( "/api/firmware-workbench/releases/fw-rel-missing1234/download" ) latest_status, latest_body = self.get( "/api/firmware-workbench/releases/latest" ) state_status, state_body = self.get("/api/firmware-workbench/state") finally: MutationSecurityHandler.auth = _auth(enabled=False) assert download_status == 404 assert download_body["error"] == "not_found" assert latest_status in {200, 404} if latest_status == 404: assert latest_body["error"] == "not_found" else: assert latest_body["download_url"] assert state_status == 401 assert state_body["error"] == "authentication_required" def test_hardware_bypass_requires_auth_enabled_and_direct_loopback(self) -> None: handler = object.__new__(SeatDemoHandler) handler.client_address = ("127.0.0.1", 12345) handler.headers = Message() handler.auth = _auth(enabled=False) assert handler._hardware_access_bypass_allowed() is False handler.auth = _auth(enabled=True) assert handler._hardware_access_bypass_allowed() is False session = handler.auth.sessions.issue("test-user") handler.headers["Cookie"] = f"{handler.auth.config.cookie_name}={session}" assert handler._hardware_access_bypass_allowed() is True handler.headers["X-Forwarded-For"] = "203.0.113.10" assert handler._hardware_access_bypass_allowed() is False