crackerjack 0.31.10__py3-none-any.whl → 0.31.12__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.
- crackerjack/CLAUDE.md +288 -705
- crackerjack/__main__.py +22 -8
- crackerjack/agents/__init__.py +0 -3
- crackerjack/agents/architect_agent.py +0 -43
- crackerjack/agents/base.py +1 -9
- crackerjack/agents/coordinator.py +2 -148
- crackerjack/agents/documentation_agent.py +109 -81
- crackerjack/agents/dry_agent.py +122 -97
- crackerjack/agents/formatting_agent.py +3 -16
- crackerjack/agents/import_optimization_agent.py +1174 -130
- crackerjack/agents/performance_agent.py +956 -188
- crackerjack/agents/performance_helpers.py +229 -0
- crackerjack/agents/proactive_agent.py +1 -48
- crackerjack/agents/refactoring_agent.py +516 -246
- crackerjack/agents/refactoring_helpers.py +282 -0
- crackerjack/agents/security_agent.py +393 -90
- crackerjack/agents/test_creation_agent.py +1776 -120
- crackerjack/agents/test_specialist_agent.py +59 -15
- crackerjack/agents/tracker.py +0 -102
- crackerjack/api.py +145 -37
- crackerjack/cli/handlers.py +48 -30
- crackerjack/cli/interactive.py +11 -11
- crackerjack/cli/options.py +66 -4
- crackerjack/code_cleaner.py +808 -148
- crackerjack/config/global_lock_config.py +110 -0
- crackerjack/config/hooks.py +43 -64
- crackerjack/core/async_workflow_orchestrator.py +247 -97
- crackerjack/core/autofix_coordinator.py +192 -109
- crackerjack/core/enhanced_container.py +46 -63
- crackerjack/core/file_lifecycle.py +549 -0
- crackerjack/core/performance.py +9 -8
- crackerjack/core/performance_monitor.py +395 -0
- crackerjack/core/phase_coordinator.py +281 -94
- crackerjack/core/proactive_workflow.py +9 -58
- crackerjack/core/resource_manager.py +501 -0
- crackerjack/core/service_watchdog.py +490 -0
- crackerjack/core/session_coordinator.py +4 -8
- crackerjack/core/timeout_manager.py +504 -0
- crackerjack/core/websocket_lifecycle.py +475 -0
- crackerjack/core/workflow_orchestrator.py +343 -209
- crackerjack/dynamic_config.py +47 -6
- crackerjack/errors.py +3 -4
- crackerjack/executors/async_hook_executor.py +63 -13
- crackerjack/executors/cached_hook_executor.py +14 -14
- crackerjack/executors/hook_executor.py +100 -37
- crackerjack/executors/hook_lock_manager.py +856 -0
- crackerjack/executors/individual_hook_executor.py +120 -86
- crackerjack/intelligence/__init__.py +0 -7
- crackerjack/intelligence/adaptive_learning.py +13 -86
- crackerjack/intelligence/agent_orchestrator.py +15 -78
- crackerjack/intelligence/agent_registry.py +12 -59
- crackerjack/intelligence/agent_selector.py +31 -92
- crackerjack/intelligence/integration.py +1 -41
- crackerjack/interactive.py +9 -9
- crackerjack/managers/async_hook_manager.py +25 -8
- crackerjack/managers/hook_manager.py +9 -9
- crackerjack/managers/publish_manager.py +57 -59
- crackerjack/managers/test_command_builder.py +6 -36
- crackerjack/managers/test_executor.py +9 -61
- crackerjack/managers/test_manager.py +17 -63
- crackerjack/managers/test_manager_backup.py +77 -127
- crackerjack/managers/test_progress.py +4 -23
- crackerjack/mcp/cache.py +5 -12
- crackerjack/mcp/client_runner.py +10 -10
- crackerjack/mcp/context.py +64 -6
- crackerjack/mcp/dashboard.py +14 -11
- crackerjack/mcp/enhanced_progress_monitor.py +55 -55
- crackerjack/mcp/file_monitor.py +72 -42
- crackerjack/mcp/progress_components.py +103 -84
- crackerjack/mcp/progress_monitor.py +122 -49
- crackerjack/mcp/rate_limiter.py +12 -12
- crackerjack/mcp/server_core.py +16 -22
- crackerjack/mcp/service_watchdog.py +26 -26
- crackerjack/mcp/state.py +15 -0
- crackerjack/mcp/tools/core_tools.py +95 -39
- crackerjack/mcp/tools/error_analyzer.py +6 -32
- crackerjack/mcp/tools/execution_tools.py +1 -56
- crackerjack/mcp/tools/execution_tools_backup.py +35 -131
- crackerjack/mcp/tools/intelligence_tool_registry.py +0 -36
- crackerjack/mcp/tools/intelligence_tools.py +2 -55
- crackerjack/mcp/tools/monitoring_tools.py +308 -145
- crackerjack/mcp/tools/proactive_tools.py +12 -42
- crackerjack/mcp/tools/progress_tools.py +23 -15
- crackerjack/mcp/tools/utility_tools.py +3 -40
- crackerjack/mcp/tools/workflow_executor.py +40 -60
- crackerjack/mcp/websocket/app.py +0 -3
- crackerjack/mcp/websocket/endpoints.py +206 -268
- crackerjack/mcp/websocket/jobs.py +213 -66
- crackerjack/mcp/websocket/server.py +84 -6
- crackerjack/mcp/websocket/websocket_handler.py +137 -29
- crackerjack/models/config_adapter.py +3 -16
- crackerjack/models/protocols.py +162 -3
- crackerjack/models/resource_protocols.py +454 -0
- crackerjack/models/task.py +3 -3
- crackerjack/monitoring/__init__.py +0 -0
- crackerjack/monitoring/ai_agent_watchdog.py +25 -71
- crackerjack/monitoring/regression_prevention.py +28 -87
- crackerjack/orchestration/advanced_orchestrator.py +44 -78
- crackerjack/orchestration/coverage_improvement.py +10 -60
- crackerjack/orchestration/execution_strategies.py +16 -16
- crackerjack/orchestration/test_progress_streamer.py +61 -53
- crackerjack/plugins/base.py +1 -1
- crackerjack/plugins/managers.py +22 -20
- crackerjack/py313.py +65 -21
- crackerjack/services/backup_service.py +467 -0
- crackerjack/services/bounded_status_operations.py +627 -0
- crackerjack/services/cache.py +7 -9
- crackerjack/services/config.py +35 -52
- crackerjack/services/config_integrity.py +5 -16
- crackerjack/services/config_merge.py +542 -0
- crackerjack/services/contextual_ai_assistant.py +17 -19
- crackerjack/services/coverage_ratchet.py +44 -73
- crackerjack/services/debug.py +25 -39
- crackerjack/services/dependency_monitor.py +52 -50
- crackerjack/services/enhanced_filesystem.py +14 -11
- crackerjack/services/file_hasher.py +1 -1
- crackerjack/services/filesystem.py +1 -12
- crackerjack/services/git.py +71 -47
- crackerjack/services/health_metrics.py +31 -27
- crackerjack/services/initialization.py +276 -428
- crackerjack/services/input_validator.py +760 -0
- crackerjack/services/log_manager.py +16 -16
- crackerjack/services/logging.py +7 -6
- crackerjack/services/metrics.py +43 -43
- crackerjack/services/pattern_cache.py +2 -31
- crackerjack/services/pattern_detector.py +26 -63
- crackerjack/services/performance_benchmarks.py +20 -45
- crackerjack/services/regex_patterns.py +2887 -0
- crackerjack/services/regex_utils.py +537 -0
- crackerjack/services/secure_path_utils.py +683 -0
- crackerjack/services/secure_status_formatter.py +534 -0
- crackerjack/services/secure_subprocess.py +605 -0
- crackerjack/services/security.py +47 -10
- crackerjack/services/security_logger.py +492 -0
- crackerjack/services/server_manager.py +109 -50
- crackerjack/services/smart_scheduling.py +8 -25
- crackerjack/services/status_authentication.py +603 -0
- crackerjack/services/status_security_manager.py +442 -0
- crackerjack/services/thread_safe_status_collector.py +546 -0
- crackerjack/services/tool_version_service.py +1 -23
- crackerjack/services/unified_config.py +36 -58
- crackerjack/services/validation_rate_limiter.py +269 -0
- crackerjack/services/version_checker.py +9 -40
- crackerjack/services/websocket_resource_limiter.py +572 -0
- crackerjack/slash_commands/__init__.py +52 -2
- crackerjack/tools/__init__.py +0 -0
- crackerjack/tools/validate_input_validator_patterns.py +262 -0
- crackerjack/tools/validate_regex_patterns.py +198 -0
- {crackerjack-0.31.10.dist-info → crackerjack-0.31.12.dist-info}/METADATA +197 -12
- crackerjack-0.31.12.dist-info/RECORD +178 -0
- crackerjack/cli/facade.py +0 -104
- crackerjack-0.31.10.dist-info/RECORD +0 -149
- {crackerjack-0.31.10.dist-info → crackerjack-0.31.12.dist-info}/WHEEL +0 -0
- {crackerjack-0.31.10.dist-info → crackerjack-0.31.12.dist-info}/entry_points.txt +0 -0
- {crackerjack-0.31.10.dist-info → crackerjack-0.31.12.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
|