import importlib import json import os import sys import tempfile import types import unittest from unittest import mock import shutil import sqlite3 from pathlib import Path ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT)) class RuntimeSecretsTests(unittest.TestCase): def setUp(self): self.temp = tempfile.TemporaryDirectory() from app.utils import Secrets self.module = Secrets self.old_path = Secrets.RUNTIME_SECRETS_FILE self.old_cache = Secrets._CACHE Secrets.RUNTIME_SECRETS_FILE = Path(self.temp.name) / "runtime.json" Secrets._CACHE = None def tearDown(self): self.module.RUNTIME_SECRETS_FILE = self.old_path self.module._CACHE = self.old_cache self.temp.cleanup() def test_generates_unique_nonempty_secrets_without_public_config(self): internal = self.module.get_runtime_secret("internal_api_secret") media = self.module.get_runtime_secret("media_secret") self.assertGreaterEqual(len(internal), 40) self.assertGreaterEqual(len(media), 32) self.assertNotEqual(internal, media) data = json.loads(self.module.RUNTIME_SECRETS_FILE.read_text(encoding="utf-8")) self.assertEqual(data["internal_api_secret"], internal) def test_environment_value_overrides_file(self): key = "MONITOR_INTERNAL_API_SECRET" old = os.environ.get(key) os.environ[key] = "environment-secret-value" try: self.assertEqual( self.module.get_runtime_secret("internal_api_secret"), "environment-secret-value", ) finally: if old is None: os.environ.pop(key, None) else: os.environ[key] = old class _FakeRequest: def __init__(self, remote="127.0.0.1", method="POST", path="/inner/test", body=b"{}", headers=None, token=""): self.META = {"REMOTE_ADDR": remote} self.method = method self.path_info = path self.body = body self.headers = headers or {} self.GET = {"token": token} if token else {} class InternalRequestAuthTests(unittest.TestCase): @classmethod def setUpClass(cls): try: import django.conf # noqa: F401 except ImportError: django = types.ModuleType("django") conf = types.ModuleType("django.conf") conf.settings = types.SimpleNamespace(MONITOR_INTERNAL_API_SECRET="test-internal-secret") django.conf = conf sys.modules["django"] = django sys.modules["django.conf"] = conf cls.security = importlib.import_module("app.security") cls.security.settings.MONITOR_INTERNAL_API_SECRET = "test-internal-secret" def test_valid_hmac_from_loopback_is_accepted(self): now = 1_800_000_000 body = b'{"x":1}' headers = self.security.build_internal_auth_headers("POST", "/inner/test", body, timestamp=now) request = _FakeRequest(body=body, headers=headers) self.assertTrue(self.security.verify_internal_request(request, now=now)) def test_non_loopback_is_rejected_even_with_valid_hmac(self): now = 1_800_000_000 headers = self.security.build_internal_auth_headers("POST", "/inner/test", b"{}", timestamp=now) request = _FakeRequest(remote="192.168.1.10", headers=headers) self.assertFalse(self.security.verify_internal_request(request, now=now)) def test_tampered_body_and_stale_signature_are_rejected(self): now = 1_800_000_000 headers = self.security.build_internal_auth_headers("POST", "/inner/test", b"{}", timestamp=now) self.assertFalse(self.security.verify_internal_request( _FakeRequest(body=b'{"tampered":1}', headers=headers), now=now )) self.assertFalse(self.security.verify_internal_request( _FakeRequest(headers=headers), now=now + 61 )) def test_zlm_token_requires_loopback(self): self.assertTrue(self.security.verify_internal_request( _FakeRequest(token="test-internal-secret") )) self.assertFalse(self.security.verify_internal_request( _FakeRequest(remote="10.0.0.2", token="test-internal-secret") )) class SourceBoundaryTests(unittest.TestCase): def test_public_config_contains_no_runtime_credentials(self): config = json.loads((ROOT / "config.json").read_text(encoding="utf-8")) self.assertNotIn("safe", config) self.assertNotIn("mediaSecret", config) self.assertNotIn("sipServerPass", config.get("sipServer", {})) self.assertNotIn("sipServerNonce", config.get("sipServer", {})) def test_snap_uses_no_shell_and_middleware_has_no_safe_bypass(self): nvr = (ROOT / "app" / "views" / "NvrView.py").read_text(encoding="utf-8") middleware = (ROOT / "app" / "middleware.py").read_text(encoding="utf-8") self.assertIn("shell=False", nvr) self.assertNotIn("shell=True", nvr) self.assertNotIn("HTTP_SAFE", middleware) self.assertNotIn("'/nvr/openSnap'", middleware) def test_download_endpoint_no_longer_accepts_filename_parameter(self): storage = (ROOT / "app" / "views" / "StorageView.py").read_text(encoding="utf-8") self.assertIn('params.get("file_id"', storage) self.assertNotIn('params.get("filename"', storage) class SecureInitializeTests(unittest.TestCase): def test_replaces_users_clears_sessions_and_preserves_business_data(self): with tempfile.TemporaryDirectory() as temp_dir: database = Path(temp_dir) / "test.sqlite3" shutil.copy2(ROOT / "monitor.sqlite3", database) before_con = sqlite3.connect(database) try: before_streams = before_con.execute("select count(*) from av_stream").fetchone()[0] finally: before_con.close() spec = importlib.util.spec_from_file_location( "secure_initialize_test", ROOT / "scripts" / "secure_initialize.py" ) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) argv = [ "secure_initialize.py", "--database", str(database), "--admin-username", "secureadmin", "--admin-email", "admin@example.invalid", ] with mock.patch.object(sys, "argv", argv), \ mock.patch.object(module.getpass, "getpass", side_effect=["Long-Test-Password-42!", "Long-Test-Password-42!"]), \ mock.patch("app.utils.Secrets.rotate_runtime_secrets", return_value=""): module.main() con = sqlite3.connect(database) try: users = con.execute("select username,is_superuser,is_staff,is_active from auth_user").fetchall() sessions = con.execute("select count(*) from django_session").fetchone()[0] after_streams = con.execute("select count(*) from av_stream").fetchone()[0] finally: con.close() self.assertEqual(users, [("secureadmin", 1, 1, 1)]) self.assertEqual(sessions, 0) self.assertEqual(after_streams, before_streams) self.assertTrue(list(Path(temp_dir).glob("test.sqlite3.backup-*"))) if __name__ == "__main__": unittest.main()