crackerjack 0.31.10__py3-none-any.whl → 0.31.13__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of crackerjack might be problematic. Click here for more details.

Files changed (155) hide show
  1. crackerjack/CLAUDE.md +288 -705
  2. crackerjack/__main__.py +22 -8
  3. crackerjack/agents/__init__.py +0 -3
  4. crackerjack/agents/architect_agent.py +0 -43
  5. crackerjack/agents/base.py +1 -9
  6. crackerjack/agents/coordinator.py +2 -148
  7. crackerjack/agents/documentation_agent.py +109 -81
  8. crackerjack/agents/dry_agent.py +122 -97
  9. crackerjack/agents/formatting_agent.py +3 -16
  10. crackerjack/agents/import_optimization_agent.py +1174 -130
  11. crackerjack/agents/performance_agent.py +956 -188
  12. crackerjack/agents/performance_helpers.py +229 -0
  13. crackerjack/agents/proactive_agent.py +1 -48
  14. crackerjack/agents/refactoring_agent.py +516 -246
  15. crackerjack/agents/refactoring_helpers.py +282 -0
  16. crackerjack/agents/security_agent.py +393 -90
  17. crackerjack/agents/test_creation_agent.py +1776 -120
  18. crackerjack/agents/test_specialist_agent.py +59 -15
  19. crackerjack/agents/tracker.py +0 -102
  20. crackerjack/api.py +145 -37
  21. crackerjack/cli/handlers.py +48 -30
  22. crackerjack/cli/interactive.py +11 -11
  23. crackerjack/cli/options.py +66 -4
  24. crackerjack/code_cleaner.py +808 -148
  25. crackerjack/config/global_lock_config.py +110 -0
  26. crackerjack/config/hooks.py +43 -64
  27. crackerjack/core/async_workflow_orchestrator.py +247 -97
  28. crackerjack/core/autofix_coordinator.py +192 -109
  29. crackerjack/core/enhanced_container.py +46 -63
  30. crackerjack/core/file_lifecycle.py +549 -0
  31. crackerjack/core/performance.py +9 -8
  32. crackerjack/core/performance_monitor.py +395 -0
  33. crackerjack/core/phase_coordinator.py +281 -94
  34. crackerjack/core/proactive_workflow.py +9 -58
  35. crackerjack/core/resource_manager.py +501 -0
  36. crackerjack/core/service_watchdog.py +490 -0
  37. crackerjack/core/session_coordinator.py +4 -8
  38. crackerjack/core/timeout_manager.py +504 -0
  39. crackerjack/core/websocket_lifecycle.py +475 -0
  40. crackerjack/core/workflow_orchestrator.py +343 -209
  41. crackerjack/dynamic_config.py +50 -9
  42. crackerjack/errors.py +3 -4
  43. crackerjack/executors/async_hook_executor.py +63 -13
  44. crackerjack/executors/cached_hook_executor.py +14 -14
  45. crackerjack/executors/hook_executor.py +100 -37
  46. crackerjack/executors/hook_lock_manager.py +856 -0
  47. crackerjack/executors/individual_hook_executor.py +120 -86
  48. crackerjack/intelligence/__init__.py +0 -7
  49. crackerjack/intelligence/adaptive_learning.py +13 -86
  50. crackerjack/intelligence/agent_orchestrator.py +15 -78
  51. crackerjack/intelligence/agent_registry.py +12 -59
  52. crackerjack/intelligence/agent_selector.py +31 -92
  53. crackerjack/intelligence/integration.py +1 -41
  54. crackerjack/interactive.py +9 -9
  55. crackerjack/managers/async_hook_manager.py +25 -8
  56. crackerjack/managers/hook_manager.py +9 -9
  57. crackerjack/managers/publish_manager.py +57 -59
  58. crackerjack/managers/test_command_builder.py +6 -36
  59. crackerjack/managers/test_executor.py +9 -61
  60. crackerjack/managers/test_manager.py +17 -63
  61. crackerjack/managers/test_manager_backup.py +77 -127
  62. crackerjack/managers/test_progress.py +4 -23
  63. crackerjack/mcp/cache.py +5 -12
  64. crackerjack/mcp/client_runner.py +10 -10
  65. crackerjack/mcp/context.py +64 -6
  66. crackerjack/mcp/dashboard.py +14 -11
  67. crackerjack/mcp/enhanced_progress_monitor.py +55 -55
  68. crackerjack/mcp/file_monitor.py +72 -42
  69. crackerjack/mcp/progress_components.py +103 -84
  70. crackerjack/mcp/progress_monitor.py +122 -49
  71. crackerjack/mcp/rate_limiter.py +12 -12
  72. crackerjack/mcp/server_core.py +16 -22
  73. crackerjack/mcp/service_watchdog.py +26 -26
  74. crackerjack/mcp/state.py +15 -0
  75. crackerjack/mcp/tools/core_tools.py +95 -39
  76. crackerjack/mcp/tools/error_analyzer.py +6 -32
  77. crackerjack/mcp/tools/execution_tools.py +1 -56
  78. crackerjack/mcp/tools/execution_tools_backup.py +35 -131
  79. crackerjack/mcp/tools/intelligence_tool_registry.py +0 -36
  80. crackerjack/mcp/tools/intelligence_tools.py +2 -55
  81. crackerjack/mcp/tools/monitoring_tools.py +308 -145
  82. crackerjack/mcp/tools/proactive_tools.py +12 -42
  83. crackerjack/mcp/tools/progress_tools.py +23 -15
  84. crackerjack/mcp/tools/utility_tools.py +3 -40
  85. crackerjack/mcp/tools/workflow_executor.py +40 -60
  86. crackerjack/mcp/websocket/app.py +0 -3
  87. crackerjack/mcp/websocket/endpoints.py +206 -268
  88. crackerjack/mcp/websocket/jobs.py +213 -66
  89. crackerjack/mcp/websocket/server.py +84 -6
  90. crackerjack/mcp/websocket/websocket_handler.py +137 -29
  91. crackerjack/models/config_adapter.py +3 -16
  92. crackerjack/models/protocols.py +162 -3
  93. crackerjack/models/resource_protocols.py +454 -0
  94. crackerjack/models/task.py +3 -3
  95. crackerjack/monitoring/__init__.py +0 -0
  96. crackerjack/monitoring/ai_agent_watchdog.py +25 -71
  97. crackerjack/monitoring/regression_prevention.py +28 -87
  98. crackerjack/orchestration/advanced_orchestrator.py +44 -78
  99. crackerjack/orchestration/coverage_improvement.py +10 -60
  100. crackerjack/orchestration/execution_strategies.py +16 -16
  101. crackerjack/orchestration/test_progress_streamer.py +61 -53
  102. crackerjack/plugins/base.py +1 -1
  103. crackerjack/plugins/managers.py +22 -20
  104. crackerjack/py313.py +65 -21
  105. crackerjack/services/backup_service.py +467 -0
  106. crackerjack/services/bounded_status_operations.py +627 -0
  107. crackerjack/services/cache.py +7 -9
  108. crackerjack/services/config.py +35 -52
  109. crackerjack/services/config_integrity.py +5 -16
  110. crackerjack/services/config_merge.py +542 -0
  111. crackerjack/services/contextual_ai_assistant.py +17 -19
  112. crackerjack/services/coverage_ratchet.py +44 -73
  113. crackerjack/services/debug.py +25 -39
  114. crackerjack/services/dependency_monitor.py +52 -50
  115. crackerjack/services/enhanced_filesystem.py +14 -11
  116. crackerjack/services/file_hasher.py +1 -1
  117. crackerjack/services/filesystem.py +1 -12
  118. crackerjack/services/git.py +71 -47
  119. crackerjack/services/health_metrics.py +31 -27
  120. crackerjack/services/initialization.py +276 -428
  121. crackerjack/services/input_validator.py +760 -0
  122. crackerjack/services/log_manager.py +16 -16
  123. crackerjack/services/logging.py +7 -6
  124. crackerjack/services/metrics.py +43 -43
  125. crackerjack/services/pattern_cache.py +2 -31
  126. crackerjack/services/pattern_detector.py +26 -63
  127. crackerjack/services/performance_benchmarks.py +20 -45
  128. crackerjack/services/regex_patterns.py +2887 -0
  129. crackerjack/services/regex_utils.py +537 -0
  130. crackerjack/services/secure_path_utils.py +683 -0
  131. crackerjack/services/secure_status_formatter.py +534 -0
  132. crackerjack/services/secure_subprocess.py +605 -0
  133. crackerjack/services/security.py +47 -10
  134. crackerjack/services/security_logger.py +492 -0
  135. crackerjack/services/server_manager.py +109 -50
  136. crackerjack/services/smart_scheduling.py +8 -25
  137. crackerjack/services/status_authentication.py +603 -0
  138. crackerjack/services/status_security_manager.py +442 -0
  139. crackerjack/services/thread_safe_status_collector.py +546 -0
  140. crackerjack/services/tool_version_service.py +1 -23
  141. crackerjack/services/unified_config.py +36 -58
  142. crackerjack/services/validation_rate_limiter.py +269 -0
  143. crackerjack/services/version_checker.py +9 -40
  144. crackerjack/services/websocket_resource_limiter.py +572 -0
  145. crackerjack/slash_commands/__init__.py +52 -2
  146. crackerjack/tools/__init__.py +0 -0
  147. crackerjack/tools/validate_input_validator_patterns.py +262 -0
  148. crackerjack/tools/validate_regex_patterns.py +198 -0
  149. {crackerjack-0.31.10.dist-info → crackerjack-0.31.13.dist-info}/METADATA +197 -12
  150. crackerjack-0.31.13.dist-info/RECORD +178 -0
  151. crackerjack/cli/facade.py +0 -104
  152. crackerjack-0.31.10.dist-info/RECORD +0 -149
  153. {crackerjack-0.31.10.dist-info → crackerjack-0.31.13.dist-info}/WHEEL +0 -0
  154. {crackerjack-0.31.10.dist-info → crackerjack-0.31.13.dist-info}/entry_points.txt +0 -0
  155. {crackerjack-0.31.10.dist-info → crackerjack-0.31.13.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,492 @@
1
+ import json
2
+ import logging
3
+ import time
4
+ import typing as t
5
+ from enum import Enum
6
+ from pathlib import Path
7
+
8
+ from pydantic import BaseModel
9
+
10
+
11
+ class SecurityEventType(str, Enum):
12
+ PATH_TRAVERSAL_ATTEMPT = "path_traversal_attempt"
13
+ FILE_SIZE_EXCEEDED = "file_size_exceeded"
14
+ DANGEROUS_PATH_DETECTED = "dangerous_path_detected"
15
+ BACKUP_CREATED = "backup_created"
16
+ BACKUP_RESTORED = "backup_restored"
17
+ BACKUP_DELETED = "backup_deleted"
18
+ FILE_CLEANED = "file_cleaned"
19
+ ATOMIC_OPERATION = "atomic_operation"
20
+ VALIDATION_FAILED = "validation_failed"
21
+ TEMP_FILE_CREATED = "temp_file_created"
22
+ COMMAND_INJECTION_ATTEMPT = "command_injection_attempt"
23
+ SQL_INJECTION_ATTEMPT = "sql_injection_attempt"
24
+ CODE_INJECTION_ATTEMPT = "code_injection_attempt"
25
+ INPUT_SIZE_EXCEEDED = "input_size_exceeded"
26
+ INVALID_JSON_PAYLOAD = "invalid_json_payload"
27
+ RATE_LIMIT_EXCEEDED = "rate_limit_exceeded"
28
+ UNAUTHORIZED_ACCESS_ATTEMPT = "unauthorized_access_attempt"
29
+ SUBPROCESS_EXECUTION = "subprocess_execution"
30
+ SUBPROCESS_ENVIRONMENT_SANITIZED = "subprocess_environment_sanitized"
31
+ SUBPROCESS_COMMAND_VALIDATION = "subprocess_command_validation"
32
+ SUBPROCESS_TIMEOUT = "subprocess_timeout"
33
+ SUBPROCESS_FAILURE = "subprocess_failure"
34
+ DANGEROUS_COMMAND_BLOCKED = "dangerous_command_blocked"
35
+ ENVIRONMENT_VARIABLE_FILTERED = "environment_variable_filtered"
36
+ STATUS_ACCESS_ATTEMPT = "status_access_attempt"
37
+ SENSITIVE_DATA_SANITIZED = "sensitive_data_sanitized"
38
+ STATUS_INFORMATION_DISCLOSURE = "status_information_disclosure"
39
+ # Authentication and authorization events
40
+ ACCESS_DENIED = "access_denied"
41
+ API_KEY_CREATED = "api_key_created"
42
+ API_KEY_REVOKED = "api_key_revoked"
43
+ AUTH_EXPIRED = "auth_expired"
44
+ AUTH_FAILURE = "auth_failure"
45
+ AUTH_SUCCESS = "auth_success"
46
+ LOCAL_ACCESS_GRANTED = "local_access_granted"
47
+ INSUFFICIENT_PRIVILEGES = "insufficient_privileges"
48
+ # Circuit breaker and resilience events
49
+ CIRCUIT_BREAKER_CLOSED = "circuit_breaker_closed"
50
+ CIRCUIT_BREAKER_HALF_OPEN = "circuit_breaker_half_open"
51
+ CIRCUIT_BREAKER_OPEN = "circuit_breaker_open"
52
+ CIRCUIT_BREAKER_RESET = "circuit_breaker_reset"
53
+ # Collection and data processing events
54
+ COLLECTION_END = "collection_end"
55
+ COLLECTION_ERROR = "collection_error"
56
+ COLLECTION_START = "collection_start"
57
+ STATUS_COLLECTED = "status_collected"
58
+ FILE_READ_ERROR = "file_read_error"
59
+ # Connection and network events
60
+ CONNECTION_CLOSED = "connection_closed"
61
+ CONNECTION_ESTABLISHED = "connection_established"
62
+ CONNECTION_IDLE = "connection_idle"
63
+ CONNECTION_TIMEOUT = "connection_timeout"
64
+ # Request lifecycle events
65
+ REQUEST_END = "request_end"
66
+ REQUEST_START = "request_start"
67
+ REQUEST_TIMEOUT = "request_timeout"
68
+ # Resource and operation management events
69
+ RESOURCE_CLEANUP = "resource_cleanup"
70
+ RESOURCE_EXHAUSTED = "resource_exhausted"
71
+ RESOURCE_LIMIT_EXCEEDED = "resource_limit_exceeded"
72
+ SERVICE_CLEANUP = "service_cleanup"
73
+ SERVICE_START = "service_start"
74
+ SERVICE_STOP = "service_stop"
75
+ # Operation monitoring events
76
+ MONITORING_ERROR = "monitoring_error"
77
+ OPERATION_DURATION_EXCEEDED = "operation_duration_exceeded"
78
+ OPERATION_FAILURE = "operation_failure"
79
+ OPERATION_SUCCESS = "operation_success"
80
+ OPERATION_TIMEOUT = "operation_timeout"
81
+ # Input validation events
82
+ INVALID_INPUT = "invalid_input"
83
+
84
+
85
+ class SecurityEventLevel(str, Enum):
86
+ LOW = "low"
87
+ MEDIUM = "medium"
88
+ HIGH = "high"
89
+ CRITICAL = "critical"
90
+ INFO = "info"
91
+ WARNING = "warning"
92
+ ERROR = "error"
93
+
94
+
95
+ class SecurityEvent(BaseModel):
96
+ timestamp: float
97
+ event_type: SecurityEventType
98
+ level: SecurityEventLevel
99
+ message: str
100
+ file_path: str | None = None
101
+ user_id: str | None = None
102
+ session_id: str | None = None
103
+ additional_data: dict[str, t.Any] = {}
104
+
105
+ def to_dict(self) -> dict[str, t.Any]:
106
+ return {
107
+ "timestamp": self.timestamp,
108
+ "event_type": self.event_type.value,
109
+ "level": self.level.value,
110
+ "message": self.message,
111
+ "file_path": self.file_path,
112
+ "user_id": self.user_id,
113
+ "session_id": self.session_id,
114
+ "additional_data": self.additional_data,
115
+ }
116
+
117
+
118
+ class SecurityLogger:
119
+ def __init__(self, logger_name: str = "crackerjack.security"):
120
+ self.logger = logging.getLogger(logger_name)
121
+ self._setup_security_logger()
122
+
123
+ def _setup_security_logger(self) -> None:
124
+ self.logger.setLevel(logging.DEBUG)
125
+
126
+ if not self.logger.handlers:
127
+ console_handler = logging.StreamHandler()
128
+ console_handler.setLevel(logging.WARNING)
129
+
130
+ formatter = logging.Formatter(
131
+ "%(asctime)s - SECURITY - %(levelname)s-%(message)s"
132
+ )
133
+ console_handler.setFormatter(formatter)
134
+ self.logger.addHandler(console_handler)
135
+
136
+ def log_security_event(
137
+ self,
138
+ event_type: SecurityEventType,
139
+ level: SecurityEventLevel,
140
+ message: str,
141
+ file_path: Path | str | None = None,
142
+ user_id: str | None = None,
143
+ session_id: str | None = None,
144
+ **additional_data: t.Any,
145
+ ) -> None:
146
+ event = SecurityEvent(
147
+ timestamp=time.time(),
148
+ event_type=event_type,
149
+ level=level,
150
+ message=message,
151
+ file_path=str(file_path) if file_path else None,
152
+ user_id=user_id,
153
+ session_id=session_id,
154
+ additional_data=additional_data,
155
+ )
156
+
157
+ log_level = self._get_logging_level(level)
158
+ self.logger.log(
159
+ log_level, json.dumps(event.to_dict()), extra={"security_event": True}
160
+ )
161
+
162
+ def _get_logging_level(self, level: SecurityEventLevel) -> int:
163
+ level_mapping = {
164
+ SecurityEventLevel.LOW: logging.DEBUG,
165
+ SecurityEventLevel.MEDIUM: logging.INFO,
166
+ SecurityEventLevel.HIGH: logging.WARNING,
167
+ SecurityEventLevel.CRITICAL: logging.CRITICAL,
168
+ SecurityEventLevel.INFO: logging.INFO,
169
+ SecurityEventLevel.WARNING: logging.WARNING,
170
+ SecurityEventLevel.ERROR: logging.ERROR,
171
+ }
172
+ return level_mapping[level]
173
+
174
+ def log_path_traversal_attempt(
175
+ self,
176
+ attempted_path: Path | str,
177
+ base_directory: Path | str | None = None,
178
+ **kwargs: t.Any,
179
+ ) -> None:
180
+ self.log_security_event(
181
+ SecurityEventType.PATH_TRAVERSAL_ATTEMPT,
182
+ SecurityEventLevel.CRITICAL,
183
+ f"Path traversal attempt detected: {attempted_path}",
184
+ file_path=attempted_path,
185
+ base_directory=str(base_directory) if base_directory else None,
186
+ **kwargs,
187
+ )
188
+
189
+ def log_file_size_exceeded(
190
+ self, file_path: Path | str, file_size: int, max_size: int, **kwargs: t.Any
191
+ ) -> None:
192
+ self.log_security_event(
193
+ SecurityEventType.FILE_SIZE_EXCEEDED,
194
+ SecurityEventLevel.HIGH,
195
+ f"File size limit exceeded: {file_size} > {max_size}",
196
+ file_path=file_path,
197
+ file_size=file_size,
198
+ max_size=max_size,
199
+ **kwargs,
200
+ )
201
+
202
+ def log_dangerous_path_detected(
203
+ self, path: Path | str, dangerous_component: str, **kwargs: t.Any
204
+ ) -> None:
205
+ self.log_security_event(
206
+ SecurityEventType.DANGEROUS_PATH_DETECTED,
207
+ SecurityEventLevel.HIGH,
208
+ f"Dangerous path component detected: {dangerous_component} in {path}",
209
+ file_path=path,
210
+ dangerous_component=dangerous_component,
211
+ **kwargs,
212
+ )
213
+
214
+ def log_backup_created(
215
+ self, original_path: Path | str, backup_path: Path | str, **kwargs: t.Any
216
+ ) -> None:
217
+ self.log_security_event(
218
+ SecurityEventType.BACKUP_CREATED,
219
+ SecurityEventLevel.LOW,
220
+ f"Backup created: {original_path} -> {backup_path}",
221
+ file_path=original_path,
222
+ backup_path=str(backup_path),
223
+ **kwargs,
224
+ )
225
+
226
+ def log_file_cleaned(
227
+ self, file_path: Path | str, steps_completed: list[str], **kwargs: t.Any
228
+ ) -> None:
229
+ self.log_security_event(
230
+ SecurityEventType.FILE_CLEANED,
231
+ SecurityEventLevel.LOW,
232
+ f"File cleaned successfully: {file_path}",
233
+ file_path=file_path,
234
+ steps_completed=steps_completed,
235
+ **kwargs,
236
+ )
237
+
238
+ def log_atomic_operation(
239
+ self, operation: str, file_path: Path | str, success: bool, **kwargs: t.Any
240
+ ) -> None:
241
+ level = SecurityEventLevel.LOW if success else SecurityEventLevel.MEDIUM
242
+ status = "successful" if success else "failed"
243
+
244
+ self.log_security_event(
245
+ SecurityEventType.ATOMIC_OPERATION,
246
+ level,
247
+ f"Atomic {operation} {status}: {file_path}",
248
+ file_path=file_path,
249
+ operation=operation,
250
+ success=success,
251
+ **kwargs,
252
+ )
253
+
254
+ def log_validation_failed(
255
+ self, validation_type: str, file_path: Path | str, reason: str, **kwargs: t.Any
256
+ ) -> None:
257
+ self.log_security_event(
258
+ SecurityEventType.VALIDATION_FAILED,
259
+ SecurityEventLevel.MEDIUM,
260
+ f"Validation failed ({validation_type}): {reason} for {file_path}",
261
+ file_path=file_path,
262
+ validation_type=validation_type,
263
+ reason=reason,
264
+ **kwargs,
265
+ )
266
+
267
+ def log_temp_file_created(
268
+ self, temp_path: Path | str, purpose: str, **kwargs: t.Any
269
+ ) -> None:
270
+ self.log_security_event(
271
+ SecurityEventType.TEMP_FILE_CREATED,
272
+ SecurityEventLevel.LOW,
273
+ f"Secure temp file created for {purpose}: {temp_path}",
274
+ file_path=temp_path,
275
+ purpose=purpose,
276
+ **kwargs,
277
+ )
278
+
279
+ def log_rate_limit_exceeded(
280
+ self, limit_type: str, current_count: int, max_allowed: int, **kwargs: t.Any
281
+ ) -> None:
282
+ self.log_security_event(
283
+ SecurityEventType.RATE_LIMIT_EXCEEDED,
284
+ SecurityEventLevel.HIGH,
285
+ f"Rate limit exceeded for {limit_type}: {current_count} > {max_allowed}",
286
+ limit_type=limit_type,
287
+ current_count=current_count,
288
+ max_allowed=max_allowed,
289
+ **kwargs,
290
+ )
291
+
292
+ def log_subprocess_execution(
293
+ self,
294
+ command: list[str],
295
+ cwd: str | None = None,
296
+ env_vars_count: int = 0,
297
+ **kwargs: t.Any,
298
+ ) -> None:
299
+ """Log secure subprocess execution."""
300
+ self.log_security_event(
301
+ SecurityEventType.SUBPROCESS_EXECUTION,
302
+ SecurityEventLevel.LOW,
303
+ f"Subprocess executed: {' '.join(command[:3])}{'...' if len(command) > 3 else ''}",
304
+ command=command[:10], # Log first 10 args only
305
+ cwd=cwd,
306
+ env_vars_count=env_vars_count,
307
+ **kwargs,
308
+ )
309
+
310
+ def log_subprocess_environment_sanitized(
311
+ self,
312
+ original_count: int,
313
+ sanitized_count: int,
314
+ filtered_vars: list[str],
315
+ **kwargs: t.Any,
316
+ ) -> None:
317
+ """Log environment sanitization for subprocess."""
318
+ self.log_security_event(
319
+ SecurityEventType.SUBPROCESS_ENVIRONMENT_SANITIZED,
320
+ SecurityEventLevel.LOW,
321
+ f"Environment sanitized: {original_count} -> {sanitized_count} vars",
322
+ original_count=original_count,
323
+ sanitized_count=sanitized_count,
324
+ filtered_vars=filtered_vars[:20], # Log first 20 filtered vars
325
+ **kwargs,
326
+ )
327
+
328
+ def log_subprocess_command_validation(
329
+ self,
330
+ command: list[str],
331
+ validation_result: bool,
332
+ issues: list[str] | None = None,
333
+ **kwargs: t.Any,
334
+ ) -> None:
335
+ """Log subprocess command validation results."""
336
+ level = SecurityEventLevel.LOW if validation_result else SecurityEventLevel.HIGH
337
+ status = "passed" if validation_result else "failed"
338
+
339
+ self.log_security_event(
340
+ SecurityEventType.SUBPROCESS_COMMAND_VALIDATION,
341
+ level,
342
+ f"Command validation {status}: {' '.join(command[:2])}{'...' if len(command) > 2 else ''}",
343
+ command_preview=command[:5],
344
+ validation_result=validation_result,
345
+ issues=issues,
346
+ **kwargs,
347
+ )
348
+
349
+ def log_subprocess_timeout(
350
+ self,
351
+ command: list[str],
352
+ timeout_seconds: float,
353
+ actual_duration: float,
354
+ **kwargs: t.Any,
355
+ ) -> None:
356
+ """Log subprocess timeout events."""
357
+ self.log_security_event(
358
+ SecurityEventType.SUBPROCESS_TIMEOUT,
359
+ SecurityEventLevel.MEDIUM,
360
+ f"Subprocess timed out after {timeout_seconds}s: {' '.join(command[:2])}{'...' if len(command) > 2 else ''}",
361
+ command_preview=command[:3],
362
+ timeout_seconds=timeout_seconds,
363
+ actual_duration=actual_duration,
364
+ **kwargs,
365
+ )
366
+
367
+ def log_subprocess_failure(
368
+ self, command: list[str], exit_code: int, error_output: str, **kwargs: t.Any
369
+ ) -> None:
370
+ """Log subprocess execution failures."""
371
+ self.log_security_event(
372
+ SecurityEventType.SUBPROCESS_FAILURE,
373
+ SecurityEventLevel.MEDIUM,
374
+ f"Subprocess failed (exit code {exit_code}): {' '.join(command[:2])}{'...' if len(command) > 2 else ''}",
375
+ command_preview=command[:3],
376
+ exit_code=exit_code,
377
+ error_preview=error_output[:200], # Log first 200 chars of error
378
+ **kwargs,
379
+ )
380
+
381
+ def log_dangerous_command_blocked(
382
+ self,
383
+ command: list[str],
384
+ reason: str,
385
+ dangerous_patterns: list[str],
386
+ **kwargs: t.Any,
387
+ ) -> None:
388
+ """Log blocking of dangerous commands."""
389
+ self.log_security_event(
390
+ SecurityEventType.DANGEROUS_COMMAND_BLOCKED,
391
+ SecurityEventLevel.CRITICAL,
392
+ f"Dangerous command blocked: {reason}",
393
+ command_preview=command[:5],
394
+ reason=reason,
395
+ dangerous_patterns=dangerous_patterns,
396
+ **kwargs,
397
+ )
398
+
399
+ def log_environment_variable_filtered(
400
+ self,
401
+ variable_name: str,
402
+ reason: str,
403
+ value_preview: str | None = None,
404
+ **kwargs: t.Any,
405
+ ) -> None:
406
+ """Log filtering of environment variables."""
407
+ self.log_security_event(
408
+ SecurityEventType.ENVIRONMENT_VARIABLE_FILTERED,
409
+ SecurityEventLevel.LOW,
410
+ f"Environment variable filtered: {variable_name} ({reason})",
411
+ variable_name=variable_name,
412
+ reason=reason,
413
+ value_preview=value_preview[:50] if value_preview else None,
414
+ **kwargs,
415
+ )
416
+
417
+ def log_status_access_attempt(
418
+ self,
419
+ endpoint: str,
420
+ verbosity_level: str,
421
+ user_context: str | None = None,
422
+ data_keys: list[str] | None = None,
423
+ **kwargs: t.Any,
424
+ ) -> None:
425
+ """Log status endpoint access attempts."""
426
+ self.log_security_event(
427
+ SecurityEventType.STATUS_ACCESS_ATTEMPT,
428
+ SecurityEventLevel.LOW,
429
+ f"Status access: {endpoint} (verbosity: {verbosity_level})",
430
+ user_id=user_context,
431
+ endpoint=endpoint,
432
+ verbosity_level=verbosity_level,
433
+ data_keys=data_keys or [],
434
+ **kwargs,
435
+ )
436
+
437
+ def log_sensitive_data_sanitized(
438
+ self,
439
+ data_type: str,
440
+ sanitization_count: int,
441
+ verbosity_level: str,
442
+ patterns_matched: list[str] | None = None,
443
+ **kwargs: t.Any,
444
+ ) -> None:
445
+ """Log sensitive data sanitization events."""
446
+ self.log_security_event(
447
+ SecurityEventType.SENSITIVE_DATA_SANITIZED,
448
+ SecurityEventLevel.LOW,
449
+ f"Sensitive data sanitized: {sanitization_count} {data_type} items",
450
+ data_type=data_type,
451
+ sanitization_count=sanitization_count,
452
+ verbosity_level=verbosity_level,
453
+ patterns_matched=patterns_matched or [],
454
+ **kwargs,
455
+ )
456
+
457
+ def log_status_information_disclosure(
458
+ self,
459
+ disclosure_type: str,
460
+ sensitive_info: str,
461
+ endpoint: str,
462
+ severity: str = "medium",
463
+ **kwargs: t.Any,
464
+ ) -> None:
465
+ """Log potential information disclosure in status responses."""
466
+ level_map = {
467
+ "low": SecurityEventLevel.LOW,
468
+ "medium": SecurityEventLevel.MEDIUM,
469
+ "high": SecurityEventLevel.HIGH,
470
+ "critical": SecurityEventLevel.CRITICAL,
471
+ }
472
+
473
+ self.log_security_event(
474
+ SecurityEventType.STATUS_INFORMATION_DISCLOSURE,
475
+ level_map.get(severity, SecurityEventLevel.MEDIUM),
476
+ f"Potential information disclosure: {disclosure_type} in {endpoint}",
477
+ disclosure_type=disclosure_type,
478
+ sensitive_info_preview=sensitive_info[:100],
479
+ endpoint=endpoint,
480
+ severity=severity,
481
+ **kwargs,
482
+ )
483
+
484
+
485
+ _security_logger: SecurityLogger | None = None
486
+
487
+
488
+ def get_security_logger() -> SecurityLogger:
489
+ global _security_logger
490
+ if _security_logger is None:
491
+ _security_logger = SecurityLogger()
492
+ return _security_logger