""" Behavioral tests for security/module_whitelist module. Tests the whitelist validation logic, security error handling, and module/class name filtering. """ import pytest class TestAllowedModulePaths: """Tests for ALLOWED_MODULE_PATHS constant.""" def test_is_frozenset(self): """ALLOWED_MODULE_PATHS is a frozenset.""" from local_deep_research.security.module_whitelist import ( ALLOWED_MODULE_PATHS, ) assert isinstance(ALLOWED_MODULE_PATHS, frozenset) def test_is_non_empty(self): """ALLOWED_MODULE_PATHS is non-empty.""" from local_deep_research.security.module_whitelist import ( ALLOWED_MODULE_PATHS, ) assert len(ALLOWED_MODULE_PATHS) > 0 def test_all_paths_start_with_dot(self): """All paths start with '.' (relative imports only).""" from local_deep_research.security.module_whitelist import ( ALLOWED_MODULE_PATHS, ) for path in ALLOWED_MODULE_PATHS: assert path.startswith("."), ( f"Path {path!r} does not start with '.'" ) def test_all_paths_are_strings(self): """All paths are strings.""" from local_deep_research.security.module_whitelist import ( ALLOWED_MODULE_PATHS, ) for path in ALLOWED_MODULE_PATHS: assert isinstance(path, str) def test_contains_brave_engine(self): """Contains brave search engine module.""" from local_deep_research.security.module_whitelist import ( ALLOWED_MODULE_PATHS, ) assert ".engines.search_engine_brave" in ALLOWED_MODULE_PATHS def test_contains_searxng_engine(self): """Contains SearXNG search engine module.""" from local_deep_research.security.module_whitelist import ( ALLOWED_MODULE_PATHS, ) assert ".engines.search_engine_searxng" in ALLOWED_MODULE_PATHS def test_contains_base_engine(self): """Contains search_engine_base module.""" from local_deep_research.security.module_whitelist import ( ALLOWED_MODULE_PATHS, ) assert ".search_engine_base" in ALLOWED_MODULE_PATHS def test_does_not_contain_os(self): """Does not contain 'os' module.""" from local_deep_research.security.module_whitelist import ( ALLOWED_MODULE_PATHS, ) assert "os" not in ALLOWED_MODULE_PATHS def test_does_not_contain_subprocess(self): """Does not contain 'subprocess' module.""" from local_deep_research.security.module_whitelist import ( ALLOWED_MODULE_PATHS, ) assert "subprocess" not in ALLOWED_MODULE_PATHS def test_legacy_alias_matches(self): """ALLOWED_MODULES is the same as ALLOWED_MODULE_PATHS.""" from local_deep_research.security.module_whitelist import ( ALLOWED_MODULE_PATHS, ALLOWED_MODULES, ) assert ALLOWED_MODULES is ALLOWED_MODULE_PATHS class TestAllowedClassNames: """Tests for ALLOWED_CLASS_NAMES constant.""" def test_is_frozenset(self): """ALLOWED_CLASS_NAMES is a frozenset.""" from local_deep_research.security.module_whitelist import ( ALLOWED_CLASS_NAMES, ) assert isinstance(ALLOWED_CLASS_NAMES, frozenset) def test_is_non_empty(self): """ALLOWED_CLASS_NAMES is non-empty.""" from local_deep_research.security.module_whitelist import ( ALLOWED_CLASS_NAMES, ) assert len(ALLOWED_CLASS_NAMES) > 0 def test_all_names_are_strings(self): """All class names are strings.""" from local_deep_research.security.module_whitelist import ( ALLOWED_CLASS_NAMES, ) for name in ALLOWED_CLASS_NAMES: assert isinstance(name, str) def test_contains_brave_search_engine(self): """Contains BraveSearchEngine class.""" from local_deep_research.security.module_whitelist import ( ALLOWED_CLASS_NAMES, ) assert "BraveSearchEngine" in ALLOWED_CLASS_NAMES def test_contains_base_search_engine(self): """Contains BaseSearchEngine class.""" from local_deep_research.security.module_whitelist import ( ALLOWED_CLASS_NAMES, ) assert "BaseSearchEngine" in ALLOWED_CLASS_NAMES def test_does_not_contain_generic_names(self): """Does not contain generic dangerous class names.""" from local_deep_research.security.module_whitelist import ( ALLOWED_CLASS_NAMES, ) assert "Popen" not in ALLOWED_CLASS_NAMES assert "system" not in ALLOWED_CLASS_NAMES class TestSecurityError: """Tests for SecurityError exception.""" def test_is_exception_subclass(self): """SecurityError is an Exception subclass.""" from local_deep_research.security.module_whitelist import SecurityError assert issubclass(SecurityError, Exception) def test_can_be_raised_and_caught(self): """SecurityError can be raised and caught.""" from local_deep_research.security.module_whitelist import SecurityError with pytest.raises(SecurityError): raise SecurityError("test") def test_message_preserved(self): """Error message is preserved.""" from local_deep_research.security.module_whitelist import SecurityError err = SecurityError("blocked import") assert str(err) == "blocked import" def test_legacy_alias(self): """ModuleNotAllowedError is same as SecurityError.""" from local_deep_research.security.module_whitelist import ( SecurityError, ModuleNotAllowedError, ) assert ModuleNotAllowedError is SecurityError class TestValidateModuleImport: """Tests for validate_module_import function.""" def test_valid_module_and_class(self): """Returns True for whitelisted module and class.""" from local_deep_research.security.module_whitelist import ( validate_module_import, ) result = validate_module_import( ".engines.search_engine_brave", "BraveSearchEngine" ) assert result is True def test_invalid_module_path(self): """Returns False for non-whitelisted module.""" from local_deep_research.security.module_whitelist import ( validate_module_import, ) result = validate_module_import( ".engines.evil_module", "BraveSearchEngine" ) assert result is False def test_invalid_class_name(self): """Returns False for non-whitelisted class.""" from local_deep_research.security.module_whitelist import ( validate_module_import, ) result = validate_module_import( ".engines.search_engine_brave", "EvilClass" ) assert result is False def test_both_invalid(self): """Returns False when both module and class are invalid.""" from local_deep_research.security.module_whitelist import ( validate_module_import, ) result = validate_module_import(".evil.module", "EvilClass") assert result is False def test_empty_module_path(self): """Returns False for empty module path.""" from local_deep_research.security.module_whitelist import ( validate_module_import, ) result = validate_module_import("", "BraveSearchEngine") assert result is False def test_empty_class_name(self): """Returns False for empty class name.""" from local_deep_research.security.module_whitelist import ( validate_module_import, ) result = validate_module_import(".engines.search_engine_brave", "") assert result is False def test_none_module_path(self): """Returns False for None module path.""" from local_deep_research.security.module_whitelist import ( validate_module_import, ) result = validate_module_import(None, "BraveSearchEngine") assert result is False def test_none_class_name(self): """Returns False for None class name.""" from local_deep_research.security.module_whitelist import ( validate_module_import, ) result = validate_module_import(".engines.search_engine_brave", None) assert result is False def test_rejects_absolute_import_os(self): """Rejects absolute import of 'os' module.""" from local_deep_research.security.module_whitelist import ( validate_module_import, ) result = validate_module_import("os", "system") assert result is False def test_rejects_absolute_import_subprocess(self): """Rejects absolute import of 'subprocess' module.""" from local_deep_research.security.module_whitelist import ( validate_module_import, ) result = validate_module_import("subprocess", "Popen") assert result is False def test_rejects_non_relative_whitelisted_path(self): """Rejects non-relative path even if it looks similar to whitelisted one.""" from local_deep_research.security.module_whitelist import ( validate_module_import, ) result = validate_module_import( "engines.search_engine_brave", "BraveSearchEngine" ) assert result is False class TestGetSafeModuleClass: """Tests for get_safe_module_class function security validation.""" def test_raises_for_non_whitelisted_module(self): """Raises SecurityError for non-whitelisted module.""" from local_deep_research.security.module_whitelist import ( get_safe_module_class, SecurityError, ) with pytest.raises(SecurityError) as exc_info: get_safe_module_class(".evil.module", "EvilClass") assert "not in the security whitelist" in str(exc_info.value) def test_raises_for_non_whitelisted_class(self): """Raises SecurityError for non-whitelisted class name.""" from local_deep_research.security.module_whitelist import ( get_safe_module_class, SecurityError, ) with pytest.raises(SecurityError) as exc_info: get_safe_module_class(".engines.search_engine_brave", "EvilClass") assert "not in the security whitelist" in str(exc_info.value) def test_raises_for_absolute_import(self): """Raises SecurityError for absolute import path.""" from local_deep_research.security.module_whitelist import ( get_safe_module_class, SecurityError, ) with pytest.raises(SecurityError): get_safe_module_class("os", "system") def test_error_message_includes_module_path(self): """Error message includes the rejected module path.""" from local_deep_research.security.module_whitelist import ( get_safe_module_class, SecurityError, ) with pytest.raises(SecurityError) as exc_info: get_safe_module_class(".evil.module", "EvilClass") assert ".evil.module" in str(exc_info.value) def test_error_message_includes_class_name(self): """Error message includes the rejected class name.""" from local_deep_research.security.module_whitelist import ( get_safe_module_class, SecurityError, ) with pytest.raises(SecurityError) as exc_info: get_safe_module_class(".evil.module", "EvilClass") assert "EvilClass" in str(exc_info.value)