crackerjack 0.33.0__py3-none-any.whl → 0.33.2__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 (198) hide show
  1. crackerjack/__main__.py +1350 -34
  2. crackerjack/adapters/__init__.py +17 -0
  3. crackerjack/adapters/lsp_client.py +358 -0
  4. crackerjack/adapters/rust_tool_adapter.py +194 -0
  5. crackerjack/adapters/rust_tool_manager.py +193 -0
  6. crackerjack/adapters/skylos_adapter.py +231 -0
  7. crackerjack/adapters/zuban_adapter.py +560 -0
  8. crackerjack/agents/base.py +7 -3
  9. crackerjack/agents/coordinator.py +271 -33
  10. crackerjack/agents/documentation_agent.py +9 -15
  11. crackerjack/agents/dry_agent.py +3 -15
  12. crackerjack/agents/formatting_agent.py +1 -1
  13. crackerjack/agents/import_optimization_agent.py +36 -180
  14. crackerjack/agents/performance_agent.py +17 -98
  15. crackerjack/agents/performance_helpers.py +7 -31
  16. crackerjack/agents/proactive_agent.py +1 -3
  17. crackerjack/agents/refactoring_agent.py +16 -85
  18. crackerjack/agents/refactoring_helpers.py +7 -42
  19. crackerjack/agents/security_agent.py +9 -48
  20. crackerjack/agents/test_creation_agent.py +356 -513
  21. crackerjack/agents/test_specialist_agent.py +0 -4
  22. crackerjack/api.py +6 -25
  23. crackerjack/cli/cache_handlers.py +204 -0
  24. crackerjack/cli/cache_handlers_enhanced.py +683 -0
  25. crackerjack/cli/facade.py +100 -0
  26. crackerjack/cli/handlers.py +224 -9
  27. crackerjack/cli/interactive.py +6 -4
  28. crackerjack/cli/options.py +642 -55
  29. crackerjack/cli/utils.py +2 -1
  30. crackerjack/code_cleaner.py +58 -117
  31. crackerjack/config/global_lock_config.py +8 -48
  32. crackerjack/config/hooks.py +53 -62
  33. crackerjack/core/async_workflow_orchestrator.py +24 -34
  34. crackerjack/core/autofix_coordinator.py +3 -17
  35. crackerjack/core/enhanced_container.py +4 -13
  36. crackerjack/core/file_lifecycle.py +12 -89
  37. crackerjack/core/performance.py +2 -2
  38. crackerjack/core/performance_monitor.py +15 -55
  39. crackerjack/core/phase_coordinator.py +104 -204
  40. crackerjack/core/resource_manager.py +14 -90
  41. crackerjack/core/service_watchdog.py +62 -95
  42. crackerjack/core/session_coordinator.py +149 -0
  43. crackerjack/core/timeout_manager.py +14 -72
  44. crackerjack/core/websocket_lifecycle.py +13 -78
  45. crackerjack/core/workflow_orchestrator.py +171 -174
  46. crackerjack/docs/INDEX.md +11 -0
  47. crackerjack/docs/generated/api/API_REFERENCE.md +10895 -0
  48. crackerjack/docs/generated/api/CLI_REFERENCE.md +109 -0
  49. crackerjack/docs/generated/api/CROSS_REFERENCES.md +1755 -0
  50. crackerjack/docs/generated/api/PROTOCOLS.md +3 -0
  51. crackerjack/docs/generated/api/SERVICES.md +1252 -0
  52. crackerjack/documentation/__init__.py +31 -0
  53. crackerjack/documentation/ai_templates.py +756 -0
  54. crackerjack/documentation/dual_output_generator.py +765 -0
  55. crackerjack/documentation/mkdocs_integration.py +518 -0
  56. crackerjack/documentation/reference_generator.py +977 -0
  57. crackerjack/dynamic_config.py +55 -50
  58. crackerjack/executors/async_hook_executor.py +10 -15
  59. crackerjack/executors/cached_hook_executor.py +117 -43
  60. crackerjack/executors/hook_executor.py +8 -34
  61. crackerjack/executors/hook_lock_manager.py +26 -183
  62. crackerjack/executors/individual_hook_executor.py +13 -11
  63. crackerjack/executors/lsp_aware_hook_executor.py +270 -0
  64. crackerjack/executors/tool_proxy.py +417 -0
  65. crackerjack/hooks/lsp_hook.py +79 -0
  66. crackerjack/intelligence/adaptive_learning.py +25 -10
  67. crackerjack/intelligence/agent_orchestrator.py +2 -5
  68. crackerjack/intelligence/agent_registry.py +34 -24
  69. crackerjack/intelligence/agent_selector.py +5 -7
  70. crackerjack/interactive.py +17 -6
  71. crackerjack/managers/async_hook_manager.py +0 -1
  72. crackerjack/managers/hook_manager.py +79 -1
  73. crackerjack/managers/publish_manager.py +44 -8
  74. crackerjack/managers/test_command_builder.py +1 -15
  75. crackerjack/managers/test_executor.py +1 -3
  76. crackerjack/managers/test_manager.py +98 -7
  77. crackerjack/managers/test_manager_backup.py +10 -9
  78. crackerjack/mcp/cache.py +2 -2
  79. crackerjack/mcp/client_runner.py +1 -1
  80. crackerjack/mcp/context.py +191 -68
  81. crackerjack/mcp/dashboard.py +7 -5
  82. crackerjack/mcp/enhanced_progress_monitor.py +31 -28
  83. crackerjack/mcp/file_monitor.py +30 -23
  84. crackerjack/mcp/progress_components.py +31 -21
  85. crackerjack/mcp/progress_monitor.py +50 -53
  86. crackerjack/mcp/rate_limiter.py +6 -6
  87. crackerjack/mcp/server_core.py +17 -16
  88. crackerjack/mcp/service_watchdog.py +2 -1
  89. crackerjack/mcp/state.py +4 -7
  90. crackerjack/mcp/task_manager.py +11 -9
  91. crackerjack/mcp/tools/core_tools.py +173 -32
  92. crackerjack/mcp/tools/error_analyzer.py +3 -2
  93. crackerjack/mcp/tools/execution_tools.py +8 -10
  94. crackerjack/mcp/tools/execution_tools_backup.py +42 -30
  95. crackerjack/mcp/tools/intelligence_tool_registry.py +7 -5
  96. crackerjack/mcp/tools/intelligence_tools.py +5 -2
  97. crackerjack/mcp/tools/monitoring_tools.py +33 -70
  98. crackerjack/mcp/tools/proactive_tools.py +24 -11
  99. crackerjack/mcp/tools/progress_tools.py +5 -8
  100. crackerjack/mcp/tools/utility_tools.py +20 -14
  101. crackerjack/mcp/tools/workflow_executor.py +62 -40
  102. crackerjack/mcp/websocket/app.py +8 -0
  103. crackerjack/mcp/websocket/endpoints.py +352 -357
  104. crackerjack/mcp/websocket/jobs.py +40 -57
  105. crackerjack/mcp/websocket/monitoring_endpoints.py +2935 -0
  106. crackerjack/mcp/websocket/server.py +7 -25
  107. crackerjack/mcp/websocket/websocket_handler.py +6 -17
  108. crackerjack/mixins/__init__.py +0 -2
  109. crackerjack/mixins/error_handling.py +1 -70
  110. crackerjack/models/config.py +12 -1
  111. crackerjack/models/config_adapter.py +49 -1
  112. crackerjack/models/protocols.py +122 -122
  113. crackerjack/models/resource_protocols.py +55 -210
  114. crackerjack/monitoring/ai_agent_watchdog.py +13 -13
  115. crackerjack/monitoring/metrics_collector.py +426 -0
  116. crackerjack/monitoring/regression_prevention.py +8 -8
  117. crackerjack/monitoring/websocket_server.py +643 -0
  118. crackerjack/orchestration/advanced_orchestrator.py +11 -6
  119. crackerjack/orchestration/coverage_improvement.py +3 -3
  120. crackerjack/orchestration/execution_strategies.py +26 -6
  121. crackerjack/orchestration/test_progress_streamer.py +8 -5
  122. crackerjack/plugins/base.py +2 -2
  123. crackerjack/plugins/hooks.py +7 -0
  124. crackerjack/plugins/managers.py +11 -8
  125. crackerjack/security/__init__.py +0 -1
  126. crackerjack/security/audit.py +6 -35
  127. crackerjack/services/anomaly_detector.py +392 -0
  128. crackerjack/services/api_extractor.py +615 -0
  129. crackerjack/services/backup_service.py +2 -2
  130. crackerjack/services/bounded_status_operations.py +15 -152
  131. crackerjack/services/cache.py +127 -1
  132. crackerjack/services/changelog_automation.py +395 -0
  133. crackerjack/services/config.py +15 -9
  134. crackerjack/services/config_merge.py +19 -80
  135. crackerjack/services/config_template.py +506 -0
  136. crackerjack/services/contextual_ai_assistant.py +48 -22
  137. crackerjack/services/coverage_badge_service.py +171 -0
  138. crackerjack/services/coverage_ratchet.py +27 -25
  139. crackerjack/services/debug.py +3 -3
  140. crackerjack/services/dependency_analyzer.py +460 -0
  141. crackerjack/services/dependency_monitor.py +14 -11
  142. crackerjack/services/documentation_generator.py +491 -0
  143. crackerjack/services/documentation_service.py +675 -0
  144. crackerjack/services/enhanced_filesystem.py +6 -5
  145. crackerjack/services/enterprise_optimizer.py +865 -0
  146. crackerjack/services/error_pattern_analyzer.py +676 -0
  147. crackerjack/services/file_hasher.py +1 -1
  148. crackerjack/services/git.py +8 -25
  149. crackerjack/services/health_metrics.py +10 -8
  150. crackerjack/services/heatmap_generator.py +735 -0
  151. crackerjack/services/initialization.py +11 -30
  152. crackerjack/services/input_validator.py +5 -97
  153. crackerjack/services/intelligent_commit.py +327 -0
  154. crackerjack/services/log_manager.py +15 -12
  155. crackerjack/services/logging.py +4 -3
  156. crackerjack/services/lsp_client.py +628 -0
  157. crackerjack/services/memory_optimizer.py +19 -87
  158. crackerjack/services/metrics.py +42 -33
  159. crackerjack/services/parallel_executor.py +9 -67
  160. crackerjack/services/pattern_cache.py +1 -1
  161. crackerjack/services/pattern_detector.py +6 -6
  162. crackerjack/services/performance_benchmarks.py +18 -59
  163. crackerjack/services/performance_cache.py +20 -81
  164. crackerjack/services/performance_monitor.py +27 -95
  165. crackerjack/services/predictive_analytics.py +510 -0
  166. crackerjack/services/quality_baseline.py +234 -0
  167. crackerjack/services/quality_baseline_enhanced.py +646 -0
  168. crackerjack/services/quality_intelligence.py +785 -0
  169. crackerjack/services/regex_patterns.py +618 -524
  170. crackerjack/services/regex_utils.py +43 -123
  171. crackerjack/services/secure_path_utils.py +5 -164
  172. crackerjack/services/secure_status_formatter.py +30 -141
  173. crackerjack/services/secure_subprocess.py +11 -92
  174. crackerjack/services/security.py +9 -41
  175. crackerjack/services/security_logger.py +12 -24
  176. crackerjack/services/server_manager.py +124 -16
  177. crackerjack/services/status_authentication.py +16 -159
  178. crackerjack/services/status_security_manager.py +4 -131
  179. crackerjack/services/thread_safe_status_collector.py +19 -125
  180. crackerjack/services/unified_config.py +21 -13
  181. crackerjack/services/validation_rate_limiter.py +5 -54
  182. crackerjack/services/version_analyzer.py +459 -0
  183. crackerjack/services/version_checker.py +1 -1
  184. crackerjack/services/websocket_resource_limiter.py +10 -144
  185. crackerjack/services/zuban_lsp_service.py +390 -0
  186. crackerjack/slash_commands/__init__.py +2 -7
  187. crackerjack/slash_commands/run.md +2 -2
  188. crackerjack/tools/validate_input_validator_patterns.py +14 -40
  189. crackerjack/tools/validate_regex_patterns.py +19 -48
  190. {crackerjack-0.33.0.dist-info → crackerjack-0.33.2.dist-info}/METADATA +196 -25
  191. crackerjack-0.33.2.dist-info/RECORD +229 -0
  192. crackerjack/CLAUDE.md +0 -207
  193. crackerjack/RULES.md +0 -380
  194. crackerjack/py313.py +0 -234
  195. crackerjack-0.33.0.dist-info/RECORD +0 -187
  196. {crackerjack-0.33.0.dist-info → crackerjack-0.33.2.dist-info}/WHEEL +0 -0
  197. {crackerjack-0.33.0.dist-info → crackerjack-0.33.2.dist-info}/entry_points.txt +0 -0
  198. {crackerjack-0.33.0.dist-info → crackerjack-0.33.2.dist-info}/licenses/LICENSE +0 -0
@@ -1,10 +1,3 @@
1
- """
2
- Secure subprocess execution with environment sanitization and command validation.
3
-
4
- This module provides production-ready security for all subprocess operations in Crackerjack,
5
- implementing comprehensive validation, sanitization, and logging to prevent injection attacks.
6
- """
7
-
8
1
  import os
9
2
  import re
10
3
  import subprocess
@@ -16,26 +9,18 @@ from .security_logger import SecurityEventLevel, SecurityEventType, get_security
16
9
 
17
10
 
18
11
  class SecurityError(Exception):
19
- """Raised when a security violation is detected."""
20
-
21
12
  pass
22
13
 
23
14
 
24
15
  class CommandValidationError(SecurityError):
25
- """Raised when command validation fails."""
26
-
27
16
  pass
28
17
 
29
18
 
30
19
  class EnvironmentValidationError(SecurityError):
31
- """Raised when environment validation fails."""
32
-
33
20
  pass
34
21
 
35
22
 
36
23
  class SubprocessSecurityConfig:
37
- """Configuration for secure subprocess execution."""
38
-
39
24
  def __init__(
40
25
  self,
41
26
  max_command_length: int = 10000,
@@ -44,7 +29,7 @@ class SubprocessSecurityConfig:
44
29
  max_env_vars: int = 1000,
45
30
  allowed_executables: set[str] | None = None,
46
31
  blocked_executables: set[str] | None = None,
47
- max_timeout: float = 3600, # 1 hour max
32
+ max_timeout: float = 3600,
48
33
  enable_path_validation: bool = True,
49
34
  enable_command_logging: bool = True,
50
35
  ):
@@ -92,24 +77,20 @@ class SubprocessSecurityConfig:
92
77
 
93
78
 
94
79
  class SecureSubprocessExecutor:
95
- """Secure subprocess executor with comprehensive validation and logging."""
96
-
97
80
  def __init__(self, config: SubprocessSecurityConfig | None = None):
98
81
  self.config = config or SubprocessSecurityConfig()
99
82
  self.security_logger = get_security_logger()
100
83
 
101
- # Dangerous patterns for command injection detection
102
84
  self.dangerous_patterns = [
103
- r"[;&|`$(){}[\]<>*?~]", # Shell metacharacters
104
- r"\.\./", # Path traversal
105
- r"\$\{.*\}", # Variable expansion
106
- r"`.*`", # Command substitution
107
- r"\$\(.*\)", # Command substitution
108
- r">\s*/", # Redirect to system paths
109
- r"<\s*/", # Redirect from system paths
85
+ r"[;&|`$(){}[\]<>*?~]",
86
+ r"\.\./",
87
+ r"\$\{.*\}",
88
+ r"`.*`",
89
+ r"\$\(.*\)",
90
+ r">\s*/",
91
+ r"<\s*/",
110
92
  ]
111
93
 
112
- # Environment variables that should never be passed through
113
94
  self.dangerous_env_vars = {
114
95
  "LD_PRELOAD",
115
96
  "DYLD_INSERT_LIBRARIES",
@@ -125,7 +106,6 @@ class SecureSubprocessExecutor:
125
106
  "BASHOPTS",
126
107
  }
127
108
 
128
- # Minimal safe environment variables
129
109
  self.safe_env_vars = {
130
110
  "HOME",
131
111
  "USER",
@@ -152,28 +132,6 @@ class SecureSubprocessExecutor:
152
132
  check: bool = False,
153
133
  **kwargs: t.Any,
154
134
  ) -> subprocess.CompletedProcess[str]:
155
- """
156
- Execute a subprocess with comprehensive security validation.
157
-
158
- Args:
159
- command: Command and arguments as list
160
- cwd: Working directory (validated for path traversal)
161
- env: Environment variables (will be sanitized)
162
- timeout: Maximum execution time
163
- input_data: Input to pass to subprocess
164
- capture_output: Whether to capture stdout/stderr
165
- text: Whether to use text mode
166
- check: Whether to raise on non-zero exit
167
- **kwargs: Additional subprocess.run arguments
168
-
169
- Returns:
170
- CompletedProcess result
171
-
172
- Raises:
173
- SecurityError: If security validation fails
174
- CommandValidationError: If command validation fails
175
- EnvironmentValidationError: If environment validation fails
176
- """
177
135
  start_time = time.time()
178
136
 
179
137
  try:
@@ -215,16 +173,12 @@ class SecureSubprocessExecutor:
215
173
  kwargs: dict[str, t.Any],
216
174
  start_time: float,
217
175
  ) -> subprocess.CompletedProcess[str]:
218
- """Execute subprocess with validation and logging."""
219
- # Validate and sanitize all inputs
220
176
  execution_params = self._prepare_execution_params(command, cwd, env, timeout)
221
177
 
222
- # Log and execute subprocess
223
178
  result = self._execute_subprocess(
224
179
  execution_params, input_data, capture_output, text, check, kwargs
225
180
  )
226
181
 
227
- # Log success
228
182
  self._log_successful_execution(execution_params, result, start_time)
229
183
  return result
230
184
 
@@ -235,7 +189,6 @@ class SecureSubprocessExecutor:
235
189
  env: dict[str, str] | None,
236
190
  timeout: float | None,
237
191
  ) -> dict[str, t.Any]:
238
- """Prepare and validate all execution parameters."""
239
192
  return {
240
193
  "command": self._validate_command(command),
241
194
  "cwd": self._validate_cwd(cwd),
@@ -252,7 +205,6 @@ class SecureSubprocessExecutor:
252
205
  check: bool,
253
206
  kwargs: dict[str, t.Any],
254
207
  ) -> subprocess.CompletedProcess[str]:
255
- """Execute subprocess with validated parameters."""
256
208
  if self.config.enable_command_logging:
257
209
  self.security_logger.log_subprocess_execution(
258
210
  command=params["command"],
@@ -279,13 +231,12 @@ class SecureSubprocessExecutor:
279
231
  result: subprocess.CompletedProcess[str],
280
232
  start_time: float,
281
233
  ) -> None:
282
- """Log successful subprocess execution."""
283
234
  execution_time = time.time() - start_time
284
235
  if self.config.enable_command_logging:
285
236
  self.security_logger.log_security_event(
286
237
  SecurityEventType.SUBPROCESS_EXECUTION,
287
238
  SecurityEventLevel.LOW,
288
- f"Subprocess completed successfully in {execution_time:.2f}s",
239
+ f"Subprocess completed successfully in {execution_time: .2f}s",
289
240
  command_preview=params["command"][:3],
290
241
  execution_time=execution_time,
291
242
  exit_code=result.returncode,
@@ -294,7 +245,6 @@ class SecureSubprocessExecutor:
294
245
  def _handle_timeout_error(
295
246
  self, command: list[str], timeout: float | None, start_time: float
296
247
  ) -> None:
297
- """Handle subprocess timeout errors."""
298
248
  execution_time = time.time() - start_time
299
249
  self.security_logger.log_subprocess_timeout(
300
250
  command=command,
@@ -305,7 +255,6 @@ class SecureSubprocessExecutor:
305
255
  def _handle_process_error(
306
256
  self, command: list[str], error: subprocess.CalledProcessError
307
257
  ) -> None:
308
- """Handle subprocess called process errors."""
309
258
  self.security_logger.log_subprocess_failure(
310
259
  command=command,
311
260
  exit_code=error.returncode,
@@ -313,7 +262,6 @@ class SecureSubprocessExecutor:
313
262
  )
314
263
 
315
264
  def _handle_unexpected_error(self, command: list[str], error: Exception) -> None:
316
- """Handle unexpected subprocess errors."""
317
265
  self.security_logger.log_security_event(
318
266
  SecurityEventType.SUBPROCESS_FAILURE,
319
267
  SecurityEventLevel.HIGH,
@@ -324,7 +272,6 @@ class SecureSubprocessExecutor:
324
272
  )
325
273
 
326
274
  def _validate_command(self, command: list[str]) -> list[str]:
327
- """Validate command arguments for security issues."""
328
275
  self._validate_command_structure(command)
329
276
 
330
277
  validated_command, issues = self._validate_command_arguments(command)
@@ -334,11 +281,9 @@ class SecureSubprocessExecutor:
334
281
  return validated_command
335
282
 
336
283
  def _validate_command_structure(self, command: list[str]) -> None:
337
- """Validate basic command structure."""
338
284
  if not command:
339
285
  raise CommandValidationError("Command cannot be empty")
340
286
 
341
- # Check overall command length
342
287
  total_length = sum(len(arg) for arg in command)
343
288
  if total_length > self.config.max_command_length:
344
289
  raise CommandValidationError(
@@ -348,19 +293,16 @@ class SecureSubprocessExecutor:
348
293
  def _validate_command_arguments(
349
294
  self, command: list[str]
350
295
  ) -> tuple[list[str], list[str]]:
351
- """Validate individual command arguments."""
352
296
  validated_command = []
353
297
  issues = []
354
298
 
355
299
  for i, arg in enumerate(command):
356
- # Check argument length
357
300
  if len(arg) > self.config.max_arg_length:
358
301
  issues.append(
359
302
  f"Argument {i} too long: {len(arg)} > {self.config.max_arg_length}"
360
303
  )
361
304
  continue
362
305
 
363
- # Check for injection patterns
364
306
  if self._has_dangerous_patterns(arg, i, issues):
365
307
  continue
366
308
 
@@ -369,7 +311,6 @@ class SecureSubprocessExecutor:
369
311
  return validated_command, issues
370
312
 
371
313
  def _has_dangerous_patterns(self, arg: str, index: int, issues: list[str]) -> bool:
372
- """Check if argument has dangerous patterns."""
373
314
  for pattern in self.dangerous_patterns:
374
315
  if re.search(pattern, arg):
375
316
  issues.append(
@@ -381,7 +322,6 @@ class SecureSubprocessExecutor:
381
322
  def _validate_executable_permissions(
382
323
  self, validated_command: list[str], issues: list[str]
383
324
  ) -> None:
384
- """Validate executable allowlist/blocklist."""
385
325
  if not validated_command:
386
326
  return
387
327
 
@@ -397,7 +337,6 @@ class SecureSubprocessExecutor:
397
337
  issues.append(f"Executable '{executable}' is blocked")
398
338
 
399
339
  def _handle_validation_results(self, command: list[str], issues: list[str]) -> None:
400
- """Handle validation results and logging."""
401
340
  validation_passed = len(issues) == 0
402
341
  if self.config.enable_command_logging:
403
342
  self.security_logger.log_subprocess_command_validation(
@@ -407,7 +346,6 @@ class SecureSubprocessExecutor:
407
346
  )
408
347
 
409
348
  if issues:
410
- # Block dangerous commands
411
349
  self.security_logger.log_dangerous_command_blocked(
412
350
  command=command,
413
351
  reason="Command validation failed",
@@ -418,7 +356,6 @@ class SecureSubprocessExecutor:
418
356
  )
419
357
 
420
358
  def _validate_cwd(self, cwd: Path | str | None) -> Path | None:
421
- """Validate working directory for path traversal."""
422
359
  if cwd is None:
423
360
  return None
424
361
 
@@ -428,10 +365,8 @@ class SecureSubprocessExecutor:
428
365
  cwd_path = Path(cwd) if isinstance(cwd, str) else cwd
429
366
 
430
367
  try:
431
- # Resolve to absolute path and check for traversal
432
368
  resolved_path = cwd_path.resolve()
433
369
 
434
- # Check for dangerous path components
435
370
  path_str = str(resolved_path)
436
371
  if ".." in path_str or path_str.startswith(
437
372
  ("/etc", "/usr/bin", "/bin", "/sbin")
@@ -448,13 +383,12 @@ class SecureSubprocessExecutor:
448
383
  raise CommandValidationError(f"Invalid working directory '{cwd}': {e}")
449
384
 
450
385
  def _sanitize_environment(self, env: dict[str, str] | None) -> dict[str, str]:
451
- """Sanitize environment variables."""
452
386
  if env is None:
453
387
  env = os.environ.copy()
454
388
 
455
389
  self._validate_environment_size(env)
456
390
 
457
- filtered_vars = []
391
+ filtered_vars: list[str] = []
458
392
  sanitized_env = self._filter_environment_variables(env, filtered_vars)
459
393
 
460
394
  self._add_safe_environment_variables(sanitized_env)
@@ -463,7 +397,6 @@ class SecureSubprocessExecutor:
463
397
  return sanitized_env
464
398
 
465
399
  def _validate_environment_size(self, env: dict[str, str]) -> None:
466
- """Validate environment variable count limits."""
467
400
  if len(env) > self.config.max_env_vars:
468
401
  self.security_logger.log_security_event(
469
402
  SecurityEventType.INPUT_SIZE_EXCEEDED,
@@ -479,7 +412,6 @@ class SecureSubprocessExecutor:
479
412
  def _filter_environment_variables(
480
413
  self, env: dict[str, str], filtered_vars: list[str]
481
414
  ) -> dict[str, str]:
482
- """Filter environment variables for security."""
483
415
  sanitized_env = {}
484
416
 
485
417
  for key, value in env.items():
@@ -499,7 +431,6 @@ class SecureSubprocessExecutor:
499
431
  def _is_dangerous_environment_key(
500
432
  self, key: str, value: str, filtered_vars: list[str]
501
433
  ) -> bool:
502
- """Check if environment key is dangerous."""
503
434
  if key in self.dangerous_env_vars:
504
435
  filtered_vars.append(key)
505
436
  self.security_logger.log_environment_variable_filtered(
@@ -513,7 +444,6 @@ class SecureSubprocessExecutor:
513
444
  def _is_environment_value_too_long(
514
445
  self, key: str, value: str, filtered_vars: list[str]
515
446
  ) -> bool:
516
- """Check if environment value exceeds length limits."""
517
447
  if len(value) > self.config.max_env_var_length:
518
448
  filtered_vars.append(key)
519
449
  self.security_logger.log_environment_variable_filtered(
@@ -527,8 +457,7 @@ class SecureSubprocessExecutor:
527
457
  def _has_environment_injection(
528
458
  self, key: str, value: str, filtered_vars: list[str]
529
459
  ) -> bool:
530
- """Check if environment value has injection patterns."""
531
- for pattern in self.dangerous_patterns[:3]: # Check first 3 most dangerous
460
+ for pattern in self.dangerous_patterns[:3]:
532
461
  if re.search(pattern, value):
533
462
  filtered_vars.append(key)
534
463
  self.security_logger.log_environment_variable_filtered(
@@ -540,7 +469,6 @@ class SecureSubprocessExecutor:
540
469
  return False
541
470
 
542
471
  def _add_safe_environment_variables(self, sanitized_env: dict[str, str]) -> None:
543
- """Add essential safe environment variables."""
544
472
  for safe_var in self.safe_env_vars:
545
473
  if safe_var not in sanitized_env and safe_var in os.environ:
546
474
  sanitized_env[safe_var] = os.environ[safe_var]
@@ -548,7 +476,6 @@ class SecureSubprocessExecutor:
548
476
  def _log_environment_sanitization(
549
477
  self, original_count: int, sanitized_count: int, filtered_vars: list[str]
550
478
  ) -> None:
551
- """Log environment sanitization results."""
552
479
  if self.config.enable_command_logging:
553
480
  self.security_logger.log_subprocess_environment_sanitized(
554
481
  original_count=original_count,
@@ -557,7 +484,6 @@ class SecureSubprocessExecutor:
557
484
  )
558
485
 
559
486
  def _validate_timeout(self, timeout: float | None) -> float | None:
560
- """Validate timeout value."""
561
487
  if timeout is None:
562
488
  return None
563
489
 
@@ -579,14 +505,12 @@ class SecureSubprocessExecutor:
579
505
  return timeout
580
506
 
581
507
 
582
- # Global secure executor instance
583
508
  _global_executor: SecureSubprocessExecutor | None = None
584
509
 
585
510
 
586
511
  def get_secure_executor(
587
512
  config: SubprocessSecurityConfig | None = None,
588
513
  ) -> SecureSubprocessExecutor:
589
- """Get the global secure subprocess executor."""
590
514
  global _global_executor
591
515
  if _global_executor is None:
592
516
  _global_executor = SecureSubprocessExecutor(config)
@@ -597,9 +521,4 @@ def execute_secure_subprocess(
597
521
  command: list[str],
598
522
  **kwargs: t.Any,
599
523
  ) -> subprocess.CompletedProcess[str]:
600
- """
601
- Convenience function for secure subprocess execution.
602
-
603
- This is the recommended way to execute subprocesses in Crackerjack.
604
- """
605
524
  return get_secure_executor().execute_secure(command, **kwargs)
@@ -9,7 +9,6 @@ from crackerjack.services.regex_patterns import SAFE_PATTERNS
9
9
 
10
10
 
11
11
  class SecurityService:
12
- # Security token masking patterns - now using validated patterns from regex_patterns.py
13
12
  TOKEN_PATTERN_NAMES = [
14
13
  "mask_pypi_token",
15
14
  "mask_github_token",
@@ -29,31 +28,16 @@ class SecurityService:
29
28
  }
30
29
 
31
30
  def mask_tokens(self, text: str) -> str:
32
- """
33
- Mask sensitive tokens in text using validated regex patterns.
34
-
35
- This method applies security token masking patterns to hide:
36
- - PyPI authentication tokens (pypi-*)
37
- - GitHub personal access tokens (ghp_*)
38
- - Generic long tokens (32+ characters)
39
- - Token assignments (token="value")
40
- - Password assignments (password="value")
41
- - Environment variable values
42
-
43
- Returns masked text with sensitive data replaced by "**** or similar.
44
- """
45
31
  if not text:
46
32
  return text
47
33
 
48
34
  masked_text = text
49
35
 
50
- # Apply validated token masking patterns
51
36
  for pattern_name in self.TOKEN_PATTERN_NAMES:
52
37
  if pattern_name in SAFE_PATTERNS:
53
38
  pattern = SAFE_PATTERNS[pattern_name]
54
39
  masked_text = pattern.apply(masked_text)
55
40
 
56
- # Also mask sensitive environment variable values
57
41
  for env_var in self.SENSITIVE_ENV_VARS:
58
42
  value = os.getenv(env_var)
59
43
  if value and len(value) > 8:
@@ -97,7 +81,7 @@ class SecurityService:
97
81
  with suppress(OSError):
98
82
  temp_file.unlink()
99
83
  raise FileError(
100
- message="Failed to set secure file permissions",
84
+ message="Failed to set[t.Any] secure file permissions",
101
85
  details=str(e),
102
86
  recovery="Check file system permissions and try again",
103
87
  ) from e
@@ -148,30 +132,17 @@ class SecurityService:
148
132
  return env_summary
149
133
 
150
134
  def validate_token_format(self, token: str, token_type: str | None = None) -> bool:
151
- """
152
- Validate token format for known token types.
153
-
154
- Args:
155
- token: The token string to validate
156
- token_type: Optional token type ("pypi", "github", or None)
157
-
158
- Returns:
159
- True if the token appears to be valid for the specified type
160
- """
161
135
  if not token:
162
136
  return False
163
137
  if len(token) < 8:
164
138
  return False
165
139
 
166
140
  if token_type and token_type.lower() == "pypi":
167
- # PyPI tokens start with "pypi-" (not "pypi -" which was a typo)
168
141
  return token.startswith("pypi-") and len(token) >= 16
169
142
 
170
143
  if token_type and token_type.lower() == "github":
171
- # GitHub personal access tokens: ghp_ + 36 chars = 40 total
172
144
  return token.startswith("ghp_") and len(token) == 40
173
145
 
174
- # Generic validation for unknown token types
175
146
  return len(token) >= 16 and not token.isspace()
176
147
 
177
148
  def create_secure_command_env(
@@ -201,13 +172,12 @@ class SecurityService:
201
172
  return secure_env
202
173
 
203
174
  def validate_file_safety(self, path: str | Path) -> bool:
204
- """Protocol method: Validate file safety."""
205
175
  try:
206
176
  file_path = Path(path)
207
- # Check if path exists and is safe
177
+
208
178
  if not file_path.exists():
209
179
  return False
210
- # Basic safety checks
180
+
211
181
  if file_path.is_symlink():
212
182
  return False
213
183
  return True
@@ -215,13 +185,12 @@ class SecurityService:
215
185
  return False
216
186
 
217
187
  def check_hardcoded_secrets(self, content: str) -> list[dict[str, t.Any]]:
218
- """Protocol method: Check for hardcoded secrets."""
219
188
  secrets = []
220
- # Basic patterns for common secrets
189
+
221
190
  patterns = {
222
- "api_key": r'api[_-]?key["\s]*[:=]["\s]*([a-zA-Z0-9_-]{20,})',
223
- "password": r'password["\s]*[:=]["\s]*([^\s"]{8,})',
224
- "token": r'token["\s]*[:=]["\s]*([a-zA-Z0-9_-]{20,})',
191
+ "api_key": r'api[_-]?key["\s]*[: =]["\s]*([a-zA-Z0-9_-]{20, })',
192
+ "password": r'password["\s]*[: =]["\s]*([^\s"]{8, })',
193
+ "token": r'token["\s]*[: =]["\s]*([a-zA-Z0-9_-]{20, })',
225
194
  }
226
195
 
227
196
  import re
@@ -232,14 +201,13 @@ class SecurityService:
232
201
  secrets.append(
233
202
  {
234
203
  "type": secret_type,
235
- "value": match.group(1)[:10] + "...", # Truncated for safety
204
+ "value": match.group(1)[:10] + "...",
236
205
  "line": content[: match.start()].count("\n") + 1,
237
206
  }
238
207
  )
239
208
  return secrets
240
209
 
241
210
  def is_safe_subprocess_call(self, cmd: list[str]) -> bool:
242
- """Protocol method: Check if subprocess call is safe."""
243
211
  if not cmd:
244
212
  return False
245
213
 
@@ -259,5 +227,5 @@ class SecurityService:
259
227
  "netcat",
260
228
  }
261
229
 
262
- command = cmd[0].split("/")[-1] # Get base command name
230
+ command = cmd[0].split("/")[-1]
263
231
  return command not in dangerous_commands
@@ -37,7 +37,7 @@ class SecurityEventType(str, Enum):
37
37
  STATUS_ACCESS_ATTEMPT = "status_access_attempt"
38
38
  SENSITIVE_DATA_SANITIZED = "sensitive_data_sanitized"
39
39
  STATUS_INFORMATION_DISCLOSURE = "status_information_disclosure"
40
- # Authentication and authorization events
40
+
41
41
  ACCESS_DENIED = "access_denied"
42
42
  API_KEY_CREATED = "api_key_created"
43
43
  API_KEY_REVOKED = "api_key_revoked"
@@ -46,40 +46,40 @@ class SecurityEventType(str, Enum):
46
46
  AUTH_SUCCESS = "auth_success"
47
47
  LOCAL_ACCESS_GRANTED = "local_access_granted"
48
48
  INSUFFICIENT_PRIVILEGES = "insufficient_privileges"
49
- # Circuit breaker and resilience events
49
+
50
50
  CIRCUIT_BREAKER_CLOSED = "circuit_breaker_closed"
51
51
  CIRCUIT_BREAKER_HALF_OPEN = "circuit_breaker_half_open"
52
52
  CIRCUIT_BREAKER_OPEN = "circuit_breaker_open"
53
53
  CIRCUIT_BREAKER_RESET = "circuit_breaker_reset"
54
- # Collection and data processing events
54
+
55
55
  COLLECTION_END = "collection_end"
56
56
  COLLECTION_ERROR = "collection_error"
57
57
  COLLECTION_START = "collection_start"
58
58
  STATUS_COLLECTED = "status_collected"
59
59
  FILE_READ_ERROR = "file_read_error"
60
- # Connection and network events
60
+
61
61
  CONNECTION_CLOSED = "connection_closed"
62
62
  CONNECTION_ESTABLISHED = "connection_established"
63
63
  CONNECTION_IDLE = "connection_idle"
64
64
  CONNECTION_TIMEOUT = "connection_timeout"
65
- # Request lifecycle events
65
+
66
66
  REQUEST_END = "request_end"
67
67
  REQUEST_START = "request_start"
68
68
  REQUEST_TIMEOUT = "request_timeout"
69
- # Resource and operation management events
69
+
70
70
  RESOURCE_CLEANUP = "resource_cleanup"
71
71
  RESOURCE_EXHAUSTED = "resource_exhausted"
72
72
  RESOURCE_LIMIT_EXCEEDED = "resource_limit_exceeded"
73
73
  SERVICE_CLEANUP = "service_cleanup"
74
74
  SERVICE_START = "service_start"
75
75
  SERVICE_STOP = "service_stop"
76
- # Operation monitoring events
76
+
77
77
  MONITORING_ERROR = "monitoring_error"
78
78
  OPERATION_DURATION_EXCEEDED = "operation_duration_exceeded"
79
79
  OPERATION_FAILURE = "operation_failure"
80
80
  OPERATION_SUCCESS = "operation_success"
81
81
  OPERATION_TIMEOUT = "operation_timeout"
82
- # Input validation events
82
+
83
83
  INVALID_INPUT = "invalid_input"
84
84
 
85
85
 
@@ -127,13 +127,11 @@ class SecurityLogger:
127
127
  if not self.logger.handlers:
128
128
  console_handler = logging.StreamHandler()
129
129
 
130
- # Only show security logs in debug modes
131
130
  debug_enabled = os.environ.get("CRACKERJACK_DEBUG", "0") == "1"
132
131
  if debug_enabled:
133
132
  console_handler.setLevel(logging.WARNING)
134
133
  else:
135
- # In non-debug mode, suppress detailed security logs
136
- console_handler.setLevel(logging.CRITICAL + 1) # Effectively disable
134
+ console_handler.setLevel(logging.CRITICAL + 1)
137
135
 
138
136
  formatter = logging.Formatter(
139
137
  "%(asctime)s - SECURITY - %(levelname)s-%(message)s"
@@ -304,12 +302,11 @@ class SecurityLogger:
304
302
  env_vars_count: int = 0,
305
303
  **kwargs: t.Any,
306
304
  ) -> None:
307
- """Log secure subprocess execution."""
308
305
  self.log_security_event(
309
306
  SecurityEventType.SUBPROCESS_EXECUTION,
310
307
  SecurityEventLevel.LOW,
311
308
  f"Subprocess executed: {' '.join(command[:3])}{'...' if len(command) > 3 else ''}",
312
- command=command[:10], # Log first 10 args only
309
+ command=command[:10],
313
310
  cwd=cwd,
314
311
  env_vars_count=env_vars_count,
315
312
  **kwargs,
@@ -322,14 +319,13 @@ class SecurityLogger:
322
319
  filtered_vars: list[str],
323
320
  **kwargs: t.Any,
324
321
  ) -> None:
325
- """Log environment sanitization for subprocess."""
326
322
  self.log_security_event(
327
323
  SecurityEventType.SUBPROCESS_ENVIRONMENT_SANITIZED,
328
324
  SecurityEventLevel.LOW,
329
325
  f"Environment sanitized: {original_count} -> {sanitized_count} vars",
330
326
  original_count=original_count,
331
327
  sanitized_count=sanitized_count,
332
- filtered_vars=filtered_vars[:20], # Log first 20 filtered vars
328
+ filtered_vars=filtered_vars[:20],
333
329
  **kwargs,
334
330
  )
335
331
 
@@ -340,7 +336,6 @@ class SecurityLogger:
340
336
  issues: list[str] | None = None,
341
337
  **kwargs: t.Any,
342
338
  ) -> None:
343
- """Log subprocess command validation results."""
344
339
  level = SecurityEventLevel.LOW if validation_result else SecurityEventLevel.HIGH
345
340
  status = "passed" if validation_result else "failed"
346
341
 
@@ -361,7 +356,6 @@ class SecurityLogger:
361
356
  actual_duration: float,
362
357
  **kwargs: t.Any,
363
358
  ) -> None:
364
- """Log subprocess timeout events."""
365
359
  self.log_security_event(
366
360
  SecurityEventType.SUBPROCESS_TIMEOUT,
367
361
  SecurityEventLevel.MEDIUM,
@@ -375,14 +369,13 @@ class SecurityLogger:
375
369
  def log_subprocess_failure(
376
370
  self, command: list[str], exit_code: int, error_output: str, **kwargs: t.Any
377
371
  ) -> None:
378
- """Log subprocess execution failures."""
379
372
  self.log_security_event(
380
373
  SecurityEventType.SUBPROCESS_FAILURE,
381
374
  SecurityEventLevel.MEDIUM,
382
375
  f"Subprocess failed (exit code {exit_code}): {' '.join(command[:2])}{'...' if len(command) > 2 else ''}",
383
376
  command_preview=command[:3],
384
377
  exit_code=exit_code,
385
- error_preview=error_output[:200], # Log first 200 chars of error
378
+ error_preview=error_output[:200],
386
379
  **kwargs,
387
380
  )
388
381
 
@@ -393,7 +386,6 @@ class SecurityLogger:
393
386
  dangerous_patterns: list[str],
394
387
  **kwargs: t.Any,
395
388
  ) -> None:
396
- """Log blocking of dangerous commands."""
397
389
  self.log_security_event(
398
390
  SecurityEventType.DANGEROUS_COMMAND_BLOCKED,
399
391
  SecurityEventLevel.CRITICAL,
@@ -411,7 +403,6 @@ class SecurityLogger:
411
403
  value_preview: str | None = None,
412
404
  **kwargs: t.Any,
413
405
  ) -> None:
414
- """Log filtering of environment variables."""
415
406
  self.log_security_event(
416
407
  SecurityEventType.ENVIRONMENT_VARIABLE_FILTERED,
417
408
  SecurityEventLevel.LOW,
@@ -430,7 +421,6 @@ class SecurityLogger:
430
421
  data_keys: list[str] | None = None,
431
422
  **kwargs: t.Any,
432
423
  ) -> None:
433
- """Log status endpoint access attempts."""
434
424
  self.log_security_event(
435
425
  SecurityEventType.STATUS_ACCESS_ATTEMPT,
436
426
  SecurityEventLevel.LOW,
@@ -450,7 +440,6 @@ class SecurityLogger:
450
440
  patterns_matched: list[str] | None = None,
451
441
  **kwargs: t.Any,
452
442
  ) -> None:
453
- """Log sensitive data sanitization events."""
454
443
  self.log_security_event(
455
444
  SecurityEventType.SENSITIVE_DATA_SANITIZED,
456
445
  SecurityEventLevel.LOW,
@@ -470,7 +459,6 @@ class SecurityLogger:
470
459
  severity: str = "medium",
471
460
  **kwargs: t.Any,
472
461
  ) -> None:
473
- """Log potential information disclosure in status responses."""
474
462
  level_map = {
475
463
  "low": SecurityEventLevel.LOW,
476
464
  "medium": SecurityEventLevel.MEDIUM,