from __future__ import annotations import os import sys import tempfile import unittest from pathlib import Path from unittest.mock import patch from govoplan_core.server.config import import_object, validate_object_path from govoplan_core.server.registry import available_module_manifests class ImportTrustBoundaryTests(unittest.TestCase): def test_object_path_validation_rejects_invalid_shapes(self) -> None: with self.assertRaisesRegex(ValueError, "module:attribute"): validate_object_path("govoplan_core.server.default_config") with self.assertRaisesRegex(ValueError, "valid Python module"): validate_object_path("os;system:path") with self.assertRaisesRegex(ValueError, "public attribute"): validate_object_path("govoplan_core.server.default_config:_private") def test_import_object_allows_default_govoplan_config_path(self) -> None: value = import_object("govoplan_core.server.default_config:get_server_config") self.assertTrue(callable(value)) def test_import_object_rejects_untrusted_stdlib_module(self) -> None: with self.assertRaisesRegex(ValueError, "outside trusted import prefixes"): import_object("os:path") def test_import_object_allows_explicit_custom_prefix(self) -> None: with tempfile.TemporaryDirectory(prefix="govoplan-config-import-") as directory: root = Path(directory) (root / "custom_config.py").write_text("value = 42\n", encoding="utf-8") sys.path.insert(0, str(root)) try: with patch.dict(os.environ, {"GOVOPLAN_TRUSTED_IMPORT_PREFIXES": "custom_config"}): self.assertEqual(import_object("custom_config:value"), 42) finally: sys.path.remove(str(root)) sys.modules.pop("custom_config", None) def test_builtin_manifest_imports_are_loaded_from_fixed_targets(self) -> None: manifests = available_module_manifests(enabled_modules=("access",), ignore_load_errors=True) if "access" in manifests: self.assertEqual(manifests["access"].id, "access") if __name__ == "__main__": unittest.main()