Coverage for src/local_deep_research/security/__init__.py: 100%
34 statements
« prev ^ index » next coverage.py v7.16.0, created at 2026-09-06 15:42 +0000
« prev ^ index » next coverage.py v7.16.0, created at 2026-09-06 15:42 +0000
1"""Security utilities for Local Deep Research."""
3from .data_sanitizer import (
4 DataSanitizer,
5 filter_research_metadata,
6 redact_data,
7 sanitize_data,
8 strip_settings_snapshot,
9)
10from .egress.audit_hook import (
11 active_egress_context,
12 clear_active_context,
13 get_active_context,
14 install_audit_hook,
15 is_installed as is_audit_hook_installed,
16 set_active_context,
17)
18from .security_settings import get_security_default
20# FileIntegrityManager / FAISSIndexVerifier are re-exported lazily in
21# ``__getattr__`` below. Eager imports form a cycle through database.encrypted_db
22# when the security package is imported during database bootstrap.
23from .notification_validator import (
24 NotificationURLValidator,
25 NotificationURLValidationError,
26)
27from .safe_requests import safe_get, safe_post, SafeSession
28from .ssrf_validator import (
29 assert_base_url_safe,
30 get_safe_url,
31 is_ip_blocked,
32 redact_url_for_log,
33 validate_url,
34)
35from .url_validator import URLValidator
36from .account_lockout import AccountLockoutManager, get_account_lockout_manager
37from .password_validator import PasswordValidator
38from .log_sanitizer import (
39 redact_secrets,
40 sanitize_error_details,
41 sanitize_error_for_client,
42 sanitize_error_message,
43 sanitize_for_log,
44 scrub_error,
45 strip_control_chars,
46)
47from .filename_sanitizer import sanitize_filename, UnsafeFilenameError
48from .module_whitelist import (
49 get_safe_module_class,
50 ModuleNotAllowedError,
51 ALLOWED_MODULES,
52)
54# PathValidator requires werkzeug (Flask dependency), import conditionally
55try:
56 from .path_validator import PathValidator
58 _has_path_validator = True
59except ImportError:
60 PathValidator = None # type: ignore
61 _has_path_validator = False
63# FileUploadValidator requires pdfplumber, import conditionally
64try:
65 from .file_upload_validator import FileUploadValidator
67 _has_file_upload_validator = True
68except ImportError:
69 FileUploadValidator = None # type: ignore
70 _has_file_upload_validator = False
72__all__ = [
73 "PathValidator",
74 "DataSanitizer",
75 "active_egress_context",
76 "clear_active_context",
77 "get_active_context",
78 "install_audit_hook",
79 "is_audit_hook_installed",
80 "set_active_context",
81 "sanitize_data",
82 "redact_data",
83 "filter_research_metadata",
84 "strip_settings_snapshot",
85 "get_security_default",
86 "FileIntegrityManager",
87 "FAISSIndexVerifier",
88 "FileUploadValidator",
89 "NotificationURLValidator",
90 "NotificationURLValidationError",
91 "URLValidator",
92 "safe_get",
93 "safe_post",
94 "SafeSession",
95 "validate_url",
96 "get_safe_url",
97 "is_ip_blocked",
98 "assert_base_url_safe",
99 "redact_url_for_log",
100 "get_safe_module_class",
101 "ModuleNotAllowedError",
102 "ALLOWED_MODULES",
103 "AccountLockoutManager",
104 "get_account_lockout_manager",
105 "PasswordValidator",
106 "redact_secrets",
107 "sanitize_error_details",
108 "sanitize_error_for_client",
109 "sanitize_error_message",
110 "sanitize_for_log",
111 "scrub_error",
112 "strip_control_chars",
113 "sanitize_filename",
114 "UnsafeFilenameError",
115]
118def __getattr__(name: str):
119 """Lazily provide file-integrity exports without a package import cycle."""
120 if name == "FileIntegrityManager":
121 from .file_integrity.integrity_manager import FileIntegrityManager
123 return FileIntegrityManager
124 if name == "FAISSIndexVerifier":
125 from .file_integrity.verifiers.faiss_verifier import FAISSIndexVerifier
127 return FAISSIndexVerifier
128 raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
131# Install the process-wide socket.connect audit hook after all imports
132# resolve. The hook is a no-op until a worker thread calls
133# ``set_active_context(ctx)`` — importing this module has zero
134# behavioral effect on code that does not opt in. Idempotent: subsequent
135# imports do not re-install.
136install_audit_hook()