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
@@ -28,31 +28,27 @@ class InitializationService:
28
28
  self.filesystem = filesystem
29
29
  self.git_service = git_service
30
30
  self.pkg_path = pkg_path
31
- # Use dependency injection with default implementation
31
+
32
32
  self.config_merge_service = config_merge_service or ConfigMergeService(
33
33
  console, filesystem, git_service
34
34
  )
35
35
 
36
36
  def initialize_project(self, project_path: str | Path) -> bool:
37
- """Protocol method: Initialize project at given path."""
38
37
  try:
39
38
  result = self.initialize_project_full(Path(project_path))
40
- return result.get("success", False)
39
+ success = result.get("success", False)
40
+ return bool(success)
41
41
  except Exception:
42
42
  return False
43
43
 
44
44
  def setup_git_hooks(self) -> bool:
45
- """Protocol method: Setup git hooks."""
46
45
  try:
47
- # Basic git hooks setup implementation
48
46
  return True
49
47
  except Exception:
50
48
  return False
51
49
 
52
50
  def validate_project_structure(self) -> bool:
53
- """Protocol method: Validate project structure."""
54
51
  try:
55
- # Basic project structure validation
56
52
  return True
57
53
  except Exception:
58
54
  return False
@@ -65,7 +61,6 @@ class InitializationService:
65
61
  if target_path is None:
66
62
  target_path = Path.cwd()
67
63
 
68
- # Validate target path for security
69
64
  try:
70
65
  target_path = validate_and_sanitize_path(target_path, allow_absolute=True)
71
66
  except Exception as e:
@@ -83,7 +78,6 @@ class InitializationService:
83
78
  config_files = self._get_config_files()
84
79
  project_name = target_path.name
85
80
 
86
- # Validate project name
87
81
  validator = get_input_validator()
88
82
  name_result = validator.validate_project_name(project_name)
89
83
  if not name_result.valid:
@@ -93,7 +87,6 @@ class InitializationService:
93
87
  results["success"] = False
94
88
  return results
95
89
 
96
- # Use sanitized project name
97
90
  sanitized_project_name = name_result.sanitized_value
98
91
 
99
92
  for file_name, merge_strategy in config_files.items():
@@ -501,7 +494,6 @@ python -m crackerjack - a patch
501
494
  results: dict[str, t.Any],
502
495
  ) -> None:
503
496
  try:
504
- # Generate appropriate source content
505
497
  if file_name == "CLAUDE.md" and project_name != "crackerjack":
506
498
  source_content = self._generate_project_claude_content(project_name)
507
499
  else:
@@ -509,11 +501,9 @@ python -m crackerjack - a patch
509
501
  source_file, True, project_name
510
502
  )
511
503
 
512
- # Define markers for this file type
513
504
  crackerjack_start_marker = "<!-- CRACKERJACK INTEGRATION START -->"
514
505
  crackerjack_end_marker = "<!-- CRACKERJACK INTEGRATION END -->"
515
506
 
516
- # Delegate to ConfigMergeService for smart append logic
517
507
  merged_content = self.config_merge_service.smart_append_file(
518
508
  source_content,
519
509
  target_file,
@@ -522,7 +512,6 @@ python -m crackerjack - a patch
522
512
  force,
523
513
  )
524
514
 
525
- # Check if content was actually changed
526
515
  if target_file.exists():
527
516
  existing_content = target_file.read_text()
528
517
  if crackerjack_start_marker in existing_content and not force:
@@ -531,7 +520,6 @@ python -m crackerjack - a patch
531
520
  )
532
521
  return
533
522
 
534
- # Write the merged content
535
523
  target_file.write_text(merged_content)
536
524
  t.cast("list[str]", results["files_copied"]).append(
537
525
  f"{file_name} (appended)"
@@ -556,8 +544,6 @@ python -m crackerjack - a patch
556
544
  force: bool,
557
545
  results: dict[str, t.Any],
558
546
  ) -> None:
559
- """Smart merge .gitignore patterns using ConfigMergeService."""
560
- # Define crackerjack .gitignore patterns
561
547
  gitignore_patterns = [
562
548
  "# Build/Distribution",
563
549
  "/build/",
@@ -652,12 +638,10 @@ python -m crackerjack - a patch
652
638
  with source_file.open("rb") as f:
653
639
  source_config = tomli.load(f)
654
640
 
655
- # Delegate to ConfigMergeService for smart merge logic
656
641
  merged_config = self.config_merge_service.smart_merge_pyproject(
657
642
  source_config, target_file, project_name
658
643
  )
659
644
 
660
- # Write the merged configuration
661
645
  self.config_merge_service.write_pyproject_config(merged_config, target_file)
662
646
 
663
647
  t.cast("list[str]", results["files_copied"]).append(
@@ -704,11 +688,12 @@ python -m crackerjack - a patch
704
688
  self._handle_file_processing_error(".pre-commit-config.yaml", e, results)
705
689
 
706
690
  def _load_source_config(self, source_file: Path) -> dict[str, t.Any] | None:
707
- """Load and validate source configuration file."""
708
691
  with source_file.open() as f:
709
- source_config = yaml.safe_load(f) or {}
692
+ loaded_config = yaml.safe_load(f)
693
+ source_config: dict[str, t.Any] = (
694
+ loaded_config if isinstance(loaded_config, dict) else {}
695
+ )
710
696
 
711
- # Ensure source_config is a dict
712
697
  if not isinstance(source_config, dict):
713
698
  self.console.print(
714
699
  "[yellow]⚠️[/yellow] Source .pre-commit-config.yaml is not a dictionary, skipping merge"
@@ -720,7 +705,6 @@ python -m crackerjack - a patch
720
705
  def _perform_config_merge(
721
706
  self, source_config: dict[str, t.Any], target_file: Path, project_name: str
722
707
  ) -> dict[str, t.Any]:
723
- """Perform the configuration merge using ConfigMergeService."""
724
708
  return self.config_merge_service.smart_merge_pre_commit_config(
725
709
  source_config, target_file, project_name
726
710
  )
@@ -731,14 +715,15 @@ python -m crackerjack - a patch
731
715
  merged_config: dict[str, t.Any],
732
716
  results: dict[str, t.Any],
733
717
  ) -> bool:
734
- """Check if merge should be skipped due to no changes."""
735
718
  if not target_file.exists():
736
719
  return False
737
720
 
738
721
  with target_file.open() as f:
739
- old_config = yaml.safe_load(f) or {}
722
+ loaded_config = yaml.safe_load(f)
723
+ old_config: dict[str, t.Any] = (
724
+ loaded_config if isinstance(loaded_config, dict) else {}
725
+ )
740
726
 
741
- # Ensure old_config is a dict
742
727
  if not isinstance(old_config, dict):
743
728
  old_config = {}
744
729
 
@@ -758,8 +743,6 @@ python -m crackerjack - a patch
758
743
  source_config: dict[str, t.Any],
759
744
  results: dict[str, t.Any],
760
745
  ) -> None:
761
- """Write merged config and finalize the process."""
762
- # Write the merged configuration
763
746
  self.config_merge_service.write_pre_commit_config(merged_config, target_file)
764
747
 
765
748
  t.cast("list[str]", results["files_copied"]).append(
@@ -770,7 +753,6 @@ python -m crackerjack - a patch
770
753
  self._display_merge_success(source_config)
771
754
 
772
755
  def _git_add_config_file(self, target_file: Path) -> None:
773
- """Add config file to git with error handling."""
774
756
  try:
775
757
  self.git_service.add_files([str(target_file)])
776
758
  except Exception as e:
@@ -779,7 +761,6 @@ python -m crackerjack - a patch
779
761
  )
780
762
 
781
763
  def _display_merge_success(self, source_config: dict[str, t.Any]) -> None:
782
- """Display success message with repo count."""
783
764
  source_repo_count = len(source_config.get("repos", []))
784
765
  self.console.print(
785
766
  f"[green]✅[/ green] Merged .pre-commit-config.yaml ({source_repo_count} repos processed)"
@@ -1,15 +1,3 @@
1
- """
2
- Comprehensive input validation framework for security hardening.
3
-
4
- This module provides defense-in-depth input validation to prevent:
5
- - Command injection attacks (CWE-77)
6
- - Path traversal attacks (CWE-22)
7
- - SQL injection (CWE-89)
8
- - JSON injection (CWE-91)
9
- - DoS via malformed input (CWE-400)
10
- - Code injection (CWE-94)
11
- """
12
-
13
1
  import json
14
2
  import typing as t
15
3
  from functools import wraps
@@ -26,29 +14,21 @@ from .security_logger import (
26
14
 
27
15
 
28
16
  class ValidationConfig(BaseModel):
29
- """Configuration for input validation rules."""
30
-
31
- # String limits
32
17
  MAX_STRING_LENGTH: int = Field(default=10000, ge=1)
33
18
  MAX_PROJECT_NAME_LENGTH: int = Field(default=255, ge=1)
34
19
  MAX_JOB_ID_LENGTH: int = Field(default=128, ge=1)
35
20
  MAX_COMMAND_LENGTH: int = Field(default=1000, ge=1)
36
21
 
37
- # JSON limits
38
- MAX_JSON_SIZE: int = Field(default=1024 * 1024, ge=1) # 1MB
22
+ MAX_JSON_SIZE: int = Field(default=1024 * 1024, ge=1)
39
23
  MAX_JSON_DEPTH: int = Field(default=10, ge=1)
40
24
 
41
- # Rate limiting
42
25
  MAX_VALIDATION_FAILURES_PER_MINUTE: int = Field(default=10, ge=1)
43
26
 
44
- # Pattern validation
45
27
  ALLOW_SHELL_METACHARACTERS: bool = Field(default=False)
46
28
  STRICT_ALPHANUMERIC_MODE: bool = Field(default=False)
47
29
 
48
30
 
49
31
  class ValidationResult(BaseModel):
50
- """Result of input validation."""
51
-
52
32
  valid: bool
53
33
  sanitized_value: t.Any = None
54
34
  error_message: str = ""
@@ -57,9 +37,6 @@ class ValidationResult(BaseModel):
57
37
 
58
38
 
59
39
  class InputSanitizer:
60
- """Provides secure input sanitization utilities."""
61
-
62
- # Shell metacharacters that could enable command injection
63
40
  SHELL_METACHARACTERS = {
64
41
  ";",
65
42
  "&",
@@ -85,7 +62,6 @@ class InputSanitizer:
85
62
  "^",
86
63
  }
87
64
 
88
- # Dangerous path components
89
65
  DANGEROUS_PATH_COMPONENTS = {
90
66
  "..",
91
67
  ".",
@@ -121,9 +97,6 @@ class InputSanitizer:
121
97
  "LPT9",
122
98
  }
123
99
 
124
- # NOTE: SQL and Code injection patterns now use centralized SAFE_PATTERNS
125
- # from regex_patterns.py for security consistency and testing
126
-
127
100
  @classmethod
128
101
  def sanitize_string(
129
102
  cls,
@@ -132,29 +105,22 @@ class InputSanitizer:
132
105
  allow_shell_chars: bool = False,
133
106
  strict_alphanumeric: bool = False,
134
107
  ) -> ValidationResult:
135
- """Sanitize string input with configurable restrictions."""
136
-
137
- # Type validation
138
108
  type_result = cls._validate_string_type(value)
139
109
  if not type_result.valid:
140
110
  return type_result
141
111
 
142
- # Length validation
143
112
  length_result = cls._validate_string_length(value, max_length)
144
113
  if not length_result.valid:
145
114
  return length_result
146
115
 
147
- # Security validations
148
116
  security_result = cls._validate_string_security(value, allow_shell_chars)
149
117
  if not security_result.valid:
150
118
  return security_result
151
119
 
152
- # Pattern validations
153
120
  pattern_result = cls._validate_string_patterns(value)
154
121
  if not pattern_result.valid:
155
122
  return pattern_result
156
123
 
157
- # Strict alphanumeric mode
158
124
  if strict_alphanumeric and not cls._is_strictly_alphanumeric(value):
159
125
  return ValidationResult(
160
126
  valid=False,
@@ -163,7 +129,6 @@ class InputSanitizer:
163
129
  validation_type="alphanumeric_only",
164
130
  )
165
131
 
166
- # Basic sanitization (remove leading/trailing whitespace)
167
132
  sanitized = value.strip()
168
133
 
169
134
  return ValidationResult(
@@ -172,7 +137,6 @@ class InputSanitizer:
172
137
 
173
138
  @classmethod
174
139
  def _validate_string_type(cls, value: t.Any) -> ValidationResult:
175
- """Validate that the input is a string."""
176
140
  if not isinstance(value, str):
177
141
  return ValidationResult(
178
142
  valid=False,
@@ -184,7 +148,6 @@ class InputSanitizer:
184
148
 
185
149
  @classmethod
186
150
  def _validate_string_length(cls, value: str, max_length: int) -> ValidationResult:
187
- """Validate string length."""
188
151
  if len(value) > max_length:
189
152
  return ValidationResult(
190
153
  valid=False,
@@ -198,8 +161,6 @@ class InputSanitizer:
198
161
  def _validate_string_security(
199
162
  cls, value: str, allow_shell_chars: bool
200
163
  ) -> ValidationResult:
201
- """Validate string security constraints."""
202
- # Null byte injection check
203
164
  if "\x00" in value:
204
165
  return ValidationResult(
205
166
  valid=False,
@@ -208,7 +169,6 @@ class InputSanitizer:
208
169
  validation_type="null_byte_injection",
209
170
  )
210
171
 
211
- # Control character check
212
172
  if any(ord(c) < 32 and c not in "\t\n\r" for c in value):
213
173
  return ValidationResult(
214
174
  valid=False,
@@ -217,7 +177,6 @@ class InputSanitizer:
217
177
  validation_type="control_chars",
218
178
  )
219
179
 
220
- # Shell metacharacter check
221
180
  if not allow_shell_chars:
222
181
  found_chars = [c for c in value if c in cls.SHELL_METACHARACTERS]
223
182
  if found_chars:
@@ -232,8 +191,6 @@ class InputSanitizer:
232
191
 
233
192
  @classmethod
234
193
  def _validate_string_patterns(cls, value: str) -> ValidationResult:
235
- """Validate string against security patterns."""
236
- # SQL injection pattern check using SAFE_PATTERNS
237
194
  sql_patterns = [
238
195
  "validate_sql_injection_patterns",
239
196
  "validate_sql_comment_patterns",
@@ -250,7 +207,6 @@ class InputSanitizer:
250
207
  validation_type="sql_injection",
251
208
  )
252
209
 
253
- # Code injection pattern check using SAFE_PATTERNS
254
210
  code_patterns = [
255
211
  "validate_code_eval_injection",
256
212
  "validate_code_dynamic_access",
@@ -271,15 +227,12 @@ class InputSanitizer:
271
227
 
272
228
  @classmethod
273
229
  def _is_strictly_alphanumeric(cls, value: str) -> bool:
274
- """Check if string is strictly alphanumeric with allowed characters."""
275
230
  return value.replace("-", "").replace("_", "").isalnum()
276
231
 
277
232
  @classmethod
278
233
  def sanitize_json(
279
234
  cls, value: str, max_size: int = 1024 * 1024, max_depth: int = 10
280
235
  ) -> ValidationResult:
281
- """Sanitize JSON input with size and depth limits."""
282
-
283
236
  if len(value) > max_size:
284
237
  return ValidationResult(
285
238
  valid=False,
@@ -289,10 +242,8 @@ class InputSanitizer:
289
242
  )
290
243
 
291
244
  try:
292
- # Parse JSON to validate structure
293
245
  parsed = json.loads(value)
294
246
 
295
- # Check nesting depth
296
247
  def check_depth(obj: t.Any, current_depth: int = 0) -> int:
297
248
  if current_depth > max_depth:
298
249
  return current_depth
@@ -339,17 +290,13 @@ class InputSanitizer:
339
290
  base_directory: Path | None = None,
340
291
  allow_absolute: bool = False,
341
292
  ) -> ValidationResult:
342
- """Sanitize file path with traversal protection."""
343
-
344
293
  try:
345
294
  path = Path(value)
346
295
 
347
- # Check for dangerous components in the original path before resolving
348
296
  danger_result = cls._check_dangerous_components(path)
349
297
  if not danger_result.valid:
350
298
  return danger_result
351
299
 
352
- # Handle base directory constraints
353
300
  if base_directory:
354
301
  base_result = cls._validate_base_directory(
355
302
  path, base_directory, allow_absolute
@@ -358,10 +305,8 @@ class InputSanitizer:
358
305
  return base_result
359
306
  resolved = base_result.sanitized_value
360
307
  else:
361
- # Resolve to absolute path to eliminate .. components if no base directory
362
308
  resolved = path.resolve()
363
309
 
364
- # Check absolute path restrictions
365
310
  absolute_result = cls._validate_absolute_path(
366
311
  resolved, allow_absolute, base_directory
367
312
  )
@@ -384,7 +329,6 @@ class InputSanitizer:
384
329
 
385
330
  @classmethod
386
331
  def _check_dangerous_components(cls, path: Path) -> ValidationResult:
387
- """Check for dangerous components in the path."""
388
332
  for part in path.parts:
389
333
  if part.upper() in cls.DANGEROUS_PATH_COMPONENTS:
390
334
  return ValidationResult(
@@ -399,10 +343,8 @@ class InputSanitizer:
399
343
  def _validate_base_directory(
400
344
  cls, path: Path, base_directory: Path, allow_absolute: bool
401
345
  ) -> ValidationResult:
402
- """Validate path against base directory constraints."""
403
346
  base_resolved = base_directory.resolve()
404
347
 
405
- # If the path is absolute and doesn't start with base directory, it's invalid
406
348
  if path.is_absolute() and not str(path).startswith(str(base_resolved)):
407
349
  return ValidationResult(
408
350
  valid=False,
@@ -411,7 +353,6 @@ class InputSanitizer:
411
353
  validation_type="directory_escape",
412
354
  )
413
355
 
414
- # If the path is relative, resolve it relative to base directory
415
356
  if not path.is_absolute():
416
357
  resolved = (base_resolved / path).resolve()
417
358
  try:
@@ -424,7 +365,6 @@ class InputSanitizer:
424
365
  validation_type="directory_escape",
425
366
  )
426
367
  else:
427
- # For absolute paths that start with base directory, resolve normally
428
368
  resolved = path.resolve()
429
369
  try:
430
370
  resolved.relative_to(base_resolved)
@@ -444,7 +384,6 @@ class InputSanitizer:
444
384
  def _validate_absolute_path(
445
385
  cls, resolved: Path, allow_absolute: bool, base_directory: Path | None
446
386
  ) -> ValidationResult:
447
- """Validate absolute path restrictions."""
448
387
  if not allow_absolute and resolved.is_absolute() and base_directory:
449
388
  return ValidationResult(
450
389
  valid=False,
@@ -456,8 +395,6 @@ class InputSanitizer:
456
395
 
457
396
 
458
397
  class SecureInputValidator:
459
- """Main input validation class with security logging."""
460
-
461
398
  def __init__(self, config: ValidationConfig | None = None):
462
399
  self.config = config or ValidationConfig()
463
400
  self.logger = get_security_logger()
@@ -465,8 +402,6 @@ class SecureInputValidator:
465
402
  self._failure_counts: dict[str, int] = {}
466
403
 
467
404
  def validate_project_name(self, name: str) -> ValidationResult:
468
- """Validate project name with security constraints."""
469
-
470
405
  result = self.sanitizer.sanitize_string(
471
406
  name,
472
407
  max_length=self.config.MAX_PROJECT_NAME_LENGTH,
@@ -482,9 +417,6 @@ class SecureInputValidator:
482
417
  return result
483
418
 
484
419
  def validate_job_id(self, job_id: str) -> ValidationResult:
485
- """Validate job ID with strict alphanumeric constraints."""
486
-
487
- # Job IDs must be alphanumeric with hyphens only using SAFE_PATTERNS
488
420
  job_id_pattern = SAFE_PATTERNS["validate_job_id_format"]
489
421
  if not job_id_pattern.test(job_id):
490
422
  result = ValidationResult(
@@ -513,8 +445,6 @@ class SecureInputValidator:
513
445
  return result
514
446
 
515
447
  def validate_command_args(self, args: t.Any) -> ValidationResult:
516
- """Validate command arguments to prevent injection."""
517
-
518
448
  if isinstance(args, str):
519
449
  result = self.sanitizer.sanitize_string(
520
450
  args,
@@ -522,7 +452,6 @@ class SecureInputValidator:
522
452
  allow_shell_chars=self.config.ALLOW_SHELL_METACHARACTERS,
523
453
  )
524
454
  elif isinstance(args, list):
525
- # Validate each argument in the list
526
455
  sanitized_args = []
527
456
  for arg in args:
528
457
  if not isinstance(arg, str):
@@ -554,7 +483,7 @@ class SecureInputValidator:
554
483
  else:
555
484
  result = ValidationResult(
556
485
  valid=False,
557
- error_message=f"Command args must be string or list, got {type(args).__name__}",
486
+ error_message=f"Command args must be string or list[t.Any], got {type(args).__name__}",
558
487
  security_level=SecurityEventLevel.HIGH,
559
488
  validation_type="command_args_type",
560
489
  )
@@ -567,8 +496,6 @@ class SecureInputValidator:
567
496
  return result
568
497
 
569
498
  def validate_json_payload(self, payload: str) -> ValidationResult:
570
- """Validate JSON payload with size and structure limits."""
571
-
572
499
  result = self.sanitizer.sanitize_json(
573
500
  payload,
574
501
  max_size=self.config.MAX_JSON_SIZE,
@@ -591,8 +518,6 @@ class SecureInputValidator:
591
518
  base_directory: Path | None = None,
592
519
  allow_absolute: bool = False,
593
520
  ) -> ValidationResult:
594
- """Validate file path with traversal protection."""
595
-
596
521
  result = self.sanitizer.sanitize_path(path, base_directory, allow_absolute)
597
522
 
598
523
  if not result.valid:
@@ -603,9 +528,6 @@ class SecureInputValidator:
603
528
  return result
604
529
 
605
530
  def validate_environment_var(self, name: str, value: str) -> ValidationResult:
606
- """Validate environment variable name and value."""
607
-
608
- # Environment variable names must be valid identifiers using SAFE_PATTERNS
609
531
  env_var_pattern = SAFE_PATTERNS["validate_env_var_name_format"]
610
532
  if not env_var_pattern.test(name):
611
533
  result = ValidationResult(
@@ -619,7 +541,6 @@ class SecureInputValidator:
619
541
  )
620
542
  return result
621
543
 
622
- # Validate environment variable value
623
544
  result = self.sanitizer.sanitize_string(
624
545
  value, max_length=self.config.MAX_STRING_LENGTH, allow_shell_chars=False
625
546
  )
@@ -641,15 +562,12 @@ class SecureInputValidator:
641
562
  reason: str,
642
563
  level: SecurityEventLevel,
643
564
  ) -> None:
644
- """Log validation failure with rate limiting."""
645
-
646
565
  self.logger.log_validation_failed(
647
566
  validation_type=validation_type,
648
- file_path=input_value, # Reusing file_path field for input value
567
+ file_path=input_value,
649
568
  reason=reason,
650
569
  )
651
570
 
652
- # Track failure counts for rate limiting
653
571
  self._failure_counts[validation_type] = (
654
572
  self._failure_counts.get(validation_type, 0) + 1
655
573
  )
@@ -660,9 +578,7 @@ def validation_required(
660
578
  validate_args: bool = True,
661
579
  validate_kwargs: bool = True,
662
580
  config: ValidationConfig | None = None,
663
- ):
664
- """Decorator to add automatic input validation to functions."""
665
-
581
+ ) -> t.Callable[[t.Callable[..., t.Any]], t.Callable[..., t.Any]]:
666
582
  def decorator(func: t.Callable[..., t.Any]) -> t.Callable[..., t.Any]:
667
583
  @wraps(func)
668
584
  def wrapper(*args: t.Any, **kwargs: t.Any) -> t.Any:
@@ -684,7 +600,6 @@ def validation_required(
684
600
  def _validate_function_args(
685
601
  validator: SecureInputValidator, args: tuple[t.Any, ...]
686
602
  ) -> None:
687
- """Validate string arguments in function args."""
688
603
  for i, arg in enumerate(args):
689
604
  if isinstance(arg, str):
690
605
  result = validator.sanitizer.sanitize_string(arg)
@@ -698,7 +613,6 @@ def _validate_function_args(
698
613
  def _validate_function_kwargs(
699
614
  validator: SecureInputValidator, kwargs: dict[str, t.Any]
700
615
  ) -> None:
701
- """Validate string values in function kwargs."""
702
616
  for key, value in kwargs.items():
703
617
  if isinstance(value, str):
704
618
  result = validator.sanitizer.sanitize_string(value)
@@ -712,13 +626,10 @@ def _validate_function_kwargs(
712
626
  def get_input_validator(
713
627
  config: ValidationConfig | None = None,
714
628
  ) -> SecureInputValidator:
715
- """Get configured input validator instance."""
716
629
  return SecureInputValidator(config)
717
630
 
718
631
 
719
- # Convenience validation functions
720
632
  def validate_and_sanitize_string(value: str, **kwargs: t.Any) -> str:
721
- """Validate and return sanitized string, raising on failure."""
722
633
  validator = SecureInputValidator()
723
634
  result = validator.sanitizer.sanitize_string(value, **kwargs)
724
635
 
@@ -728,14 +639,12 @@ def validate_and_sanitize_string(value: str, **kwargs: t.Any) -> str:
728
639
  error_code=ErrorCode.VALIDATION_ERROR,
729
640
  )
730
641
 
731
- return result.sanitized_value
642
+ return result.sanitized_value # type: ignore[no-any-return]
732
643
 
733
644
 
734
645
  def validate_and_sanitize_path(value: str | Path, **kwargs: t.Any) -> Path:
735
- """Validate and return sanitized path, raising on failure."""
736
646
  validator = SecureInputValidator()
737
647
  result = validator.sanitizer.sanitize_string(str(value), **kwargs)
738
- # Convert back to Path if validation passes
739
648
 
740
649
  if not result.valid:
741
650
  raise ExecutionError(
@@ -747,7 +656,6 @@ def validate_and_sanitize_path(value: str | Path, **kwargs: t.Any) -> Path:
747
656
 
748
657
 
749
658
  def validate_and_parse_json(value: str, **kwargs: t.Any) -> t.Any:
750
- """Validate and return parsed JSON, raising on failure."""
751
659
  validator = SecureInputValidator()
752
660
  result = validator.sanitizer.sanitize_json(value, **kwargs)
753
661