crackerjack 0.18.2__py3-none-any.whl → 0.45.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.
- crackerjack/README.md +19 -0
- crackerjack/__init__.py +96 -2
- crackerjack/__main__.py +637 -138
- crackerjack/adapters/README.md +18 -0
- crackerjack/adapters/__init__.py +39 -0
- crackerjack/adapters/_output_paths.py +167 -0
- crackerjack/adapters/_qa_adapter_base.py +309 -0
- crackerjack/adapters/_tool_adapter_base.py +706 -0
- crackerjack/adapters/ai/README.md +65 -0
- crackerjack/adapters/ai/__init__.py +5 -0
- crackerjack/adapters/ai/claude.py +853 -0
- crackerjack/adapters/complexity/README.md +53 -0
- crackerjack/adapters/complexity/__init__.py +10 -0
- crackerjack/adapters/complexity/complexipy.py +641 -0
- crackerjack/adapters/dependency/__init__.py +22 -0
- crackerjack/adapters/dependency/pip_audit.py +418 -0
- crackerjack/adapters/format/README.md +72 -0
- crackerjack/adapters/format/__init__.py +11 -0
- crackerjack/adapters/format/mdformat.py +313 -0
- crackerjack/adapters/format/ruff.py +516 -0
- crackerjack/adapters/lint/README.md +47 -0
- crackerjack/adapters/lint/__init__.py +11 -0
- crackerjack/adapters/lint/codespell.py +273 -0
- crackerjack/adapters/lsp/README.md +49 -0
- crackerjack/adapters/lsp/__init__.py +27 -0
- crackerjack/adapters/lsp/_base.py +194 -0
- crackerjack/adapters/lsp/_client.py +358 -0
- crackerjack/adapters/lsp/_manager.py +193 -0
- crackerjack/adapters/lsp/skylos.py +283 -0
- crackerjack/adapters/lsp/zuban.py +557 -0
- crackerjack/adapters/refactor/README.md +59 -0
- crackerjack/adapters/refactor/__init__.py +12 -0
- crackerjack/adapters/refactor/creosote.py +318 -0
- crackerjack/adapters/refactor/refurb.py +406 -0
- crackerjack/adapters/refactor/skylos.py +494 -0
- crackerjack/adapters/sast/README.md +132 -0
- crackerjack/adapters/sast/__init__.py +32 -0
- crackerjack/adapters/sast/_base.py +201 -0
- crackerjack/adapters/sast/bandit.py +423 -0
- crackerjack/adapters/sast/pyscn.py +405 -0
- crackerjack/adapters/sast/semgrep.py +241 -0
- crackerjack/adapters/security/README.md +111 -0
- crackerjack/adapters/security/__init__.py +17 -0
- crackerjack/adapters/security/gitleaks.py +339 -0
- crackerjack/adapters/type/README.md +52 -0
- crackerjack/adapters/type/__init__.py +12 -0
- crackerjack/adapters/type/pyrefly.py +402 -0
- crackerjack/adapters/type/ty.py +402 -0
- crackerjack/adapters/type/zuban.py +522 -0
- crackerjack/adapters/utility/README.md +51 -0
- crackerjack/adapters/utility/__init__.py +10 -0
- crackerjack/adapters/utility/checks.py +884 -0
- crackerjack/agents/README.md +264 -0
- crackerjack/agents/__init__.py +66 -0
- crackerjack/agents/architect_agent.py +238 -0
- crackerjack/agents/base.py +167 -0
- crackerjack/agents/claude_code_bridge.py +641 -0
- crackerjack/agents/coordinator.py +600 -0
- crackerjack/agents/documentation_agent.py +520 -0
- crackerjack/agents/dry_agent.py +585 -0
- crackerjack/agents/enhanced_coordinator.py +279 -0
- crackerjack/agents/enhanced_proactive_agent.py +185 -0
- crackerjack/agents/error_middleware.py +53 -0
- crackerjack/agents/formatting_agent.py +230 -0
- crackerjack/agents/helpers/__init__.py +9 -0
- crackerjack/agents/helpers/performance/__init__.py +22 -0
- crackerjack/agents/helpers/performance/performance_ast_analyzer.py +357 -0
- crackerjack/agents/helpers/performance/performance_pattern_detector.py +909 -0
- crackerjack/agents/helpers/performance/performance_recommender.py +572 -0
- crackerjack/agents/helpers/refactoring/__init__.py +22 -0
- crackerjack/agents/helpers/refactoring/code_transformer.py +536 -0
- crackerjack/agents/helpers/refactoring/complexity_analyzer.py +344 -0
- crackerjack/agents/helpers/refactoring/dead_code_detector.py +437 -0
- crackerjack/agents/helpers/test_creation/__init__.py +19 -0
- crackerjack/agents/helpers/test_creation/test_ast_analyzer.py +216 -0
- crackerjack/agents/helpers/test_creation/test_coverage_analyzer.py +643 -0
- crackerjack/agents/helpers/test_creation/test_template_generator.py +1031 -0
- crackerjack/agents/import_optimization_agent.py +1181 -0
- crackerjack/agents/performance_agent.py +325 -0
- crackerjack/agents/performance_helpers.py +205 -0
- crackerjack/agents/proactive_agent.py +55 -0
- crackerjack/agents/refactoring_agent.py +511 -0
- crackerjack/agents/refactoring_helpers.py +247 -0
- crackerjack/agents/security_agent.py +793 -0
- crackerjack/agents/semantic_agent.py +479 -0
- crackerjack/agents/semantic_helpers.py +356 -0
- crackerjack/agents/test_creation_agent.py +570 -0
- crackerjack/agents/test_specialist_agent.py +526 -0
- crackerjack/agents/tracker.py +110 -0
- crackerjack/api.py +647 -0
- crackerjack/cli/README.md +394 -0
- crackerjack/cli/__init__.py +24 -0
- crackerjack/cli/cache_handlers.py +209 -0
- crackerjack/cli/cache_handlers_enhanced.py +680 -0
- crackerjack/cli/facade.py +162 -0
- crackerjack/cli/formatting.py +13 -0
- crackerjack/cli/handlers/__init__.py +85 -0
- crackerjack/cli/handlers/advanced.py +103 -0
- crackerjack/cli/handlers/ai_features.py +62 -0
- crackerjack/cli/handlers/analytics.py +479 -0
- crackerjack/cli/handlers/changelog.py +271 -0
- crackerjack/cli/handlers/config_handlers.py +16 -0
- crackerjack/cli/handlers/coverage.py +84 -0
- crackerjack/cli/handlers/documentation.py +280 -0
- crackerjack/cli/handlers/main_handlers.py +497 -0
- crackerjack/cli/handlers/monitoring.py +371 -0
- crackerjack/cli/handlers.py +700 -0
- crackerjack/cli/interactive.py +488 -0
- crackerjack/cli/options.py +1216 -0
- crackerjack/cli/semantic_handlers.py +292 -0
- crackerjack/cli/utils.py +19 -0
- crackerjack/cli/version.py +19 -0
- crackerjack/code_cleaner.py +1307 -0
- crackerjack/config/README.md +472 -0
- crackerjack/config/__init__.py +275 -0
- crackerjack/config/global_lock_config.py +207 -0
- crackerjack/config/hooks.py +390 -0
- crackerjack/config/loader.py +239 -0
- crackerjack/config/settings.py +141 -0
- crackerjack/config/tool_commands.py +331 -0
- crackerjack/core/README.md +393 -0
- crackerjack/core/__init__.py +0 -0
- crackerjack/core/async_workflow_orchestrator.py +738 -0
- crackerjack/core/autofix_coordinator.py +282 -0
- crackerjack/core/container.py +105 -0
- crackerjack/core/enhanced_container.py +583 -0
- crackerjack/core/file_lifecycle.py +472 -0
- crackerjack/core/performance.py +244 -0
- crackerjack/core/performance_monitor.py +357 -0
- crackerjack/core/phase_coordinator.py +1227 -0
- crackerjack/core/proactive_workflow.py +267 -0
- crackerjack/core/resource_manager.py +425 -0
- crackerjack/core/retry.py +275 -0
- crackerjack/core/service_watchdog.py +601 -0
- crackerjack/core/session_coordinator.py +239 -0
- crackerjack/core/timeout_manager.py +563 -0
- crackerjack/core/websocket_lifecycle.py +410 -0
- crackerjack/core/workflow/__init__.py +21 -0
- crackerjack/core/workflow/workflow_ai_coordinator.py +863 -0
- crackerjack/core/workflow/workflow_event_orchestrator.py +1107 -0
- crackerjack/core/workflow/workflow_issue_parser.py +714 -0
- crackerjack/core/workflow/workflow_phase_executor.py +1158 -0
- crackerjack/core/workflow/workflow_security_gates.py +400 -0
- crackerjack/core/workflow_orchestrator.py +2243 -0
- crackerjack/data/README.md +11 -0
- crackerjack/data/__init__.py +8 -0
- crackerjack/data/models.py +79 -0
- crackerjack/data/repository.py +210 -0
- crackerjack/decorators/README.md +180 -0
- crackerjack/decorators/__init__.py +35 -0
- crackerjack/decorators/error_handling.py +649 -0
- crackerjack/decorators/error_handling_decorators.py +334 -0
- crackerjack/decorators/helpers.py +58 -0
- crackerjack/decorators/patterns.py +281 -0
- crackerjack/decorators/utils.py +58 -0
- crackerjack/docs/INDEX.md +11 -0
- crackerjack/docs/README.md +11 -0
- crackerjack/docs/generated/api/API_REFERENCE.md +10895 -0
- crackerjack/docs/generated/api/CLI_REFERENCE.md +109 -0
- crackerjack/docs/generated/api/CROSS_REFERENCES.md +1755 -0
- crackerjack/docs/generated/api/PROTOCOLS.md +3 -0
- crackerjack/docs/generated/api/SERVICES.md +1252 -0
- crackerjack/documentation/README.md +11 -0
- crackerjack/documentation/__init__.py +31 -0
- crackerjack/documentation/ai_templates.py +756 -0
- crackerjack/documentation/dual_output_generator.py +767 -0
- crackerjack/documentation/mkdocs_integration.py +518 -0
- crackerjack/documentation/reference_generator.py +1065 -0
- crackerjack/dynamic_config.py +678 -0
- crackerjack/errors.py +378 -0
- crackerjack/events/README.md +11 -0
- crackerjack/events/__init__.py +16 -0
- crackerjack/events/telemetry.py +175 -0
- crackerjack/events/workflow_bus.py +346 -0
- crackerjack/exceptions/README.md +301 -0
- crackerjack/exceptions/__init__.py +5 -0
- crackerjack/exceptions/config.py +4 -0
- crackerjack/exceptions/tool_execution_error.py +245 -0
- crackerjack/executors/README.md +591 -0
- crackerjack/executors/__init__.py +13 -0
- crackerjack/executors/async_hook_executor.py +938 -0
- crackerjack/executors/cached_hook_executor.py +316 -0
- crackerjack/executors/hook_executor.py +1295 -0
- crackerjack/executors/hook_lock_manager.py +708 -0
- crackerjack/executors/individual_hook_executor.py +739 -0
- crackerjack/executors/lsp_aware_hook_executor.py +349 -0
- crackerjack/executors/progress_hook_executor.py +282 -0
- crackerjack/executors/tool_proxy.py +433 -0
- crackerjack/hooks/README.md +485 -0
- crackerjack/hooks/lsp_hook.py +93 -0
- crackerjack/intelligence/README.md +557 -0
- crackerjack/intelligence/__init__.py +37 -0
- crackerjack/intelligence/adaptive_learning.py +693 -0
- crackerjack/intelligence/agent_orchestrator.py +485 -0
- crackerjack/intelligence/agent_registry.py +377 -0
- crackerjack/intelligence/agent_selector.py +439 -0
- crackerjack/intelligence/integration.py +250 -0
- crackerjack/interactive.py +719 -0
- crackerjack/managers/README.md +369 -0
- crackerjack/managers/__init__.py +11 -0
- crackerjack/managers/async_hook_manager.py +135 -0
- crackerjack/managers/hook_manager.py +585 -0
- crackerjack/managers/publish_manager.py +631 -0
- crackerjack/managers/test_command_builder.py +391 -0
- crackerjack/managers/test_executor.py +474 -0
- crackerjack/managers/test_manager.py +1357 -0
- crackerjack/managers/test_progress.py +187 -0
- crackerjack/mcp/README.md +374 -0
- crackerjack/mcp/__init__.py +0 -0
- crackerjack/mcp/cache.py +352 -0
- crackerjack/mcp/client_runner.py +121 -0
- crackerjack/mcp/context.py +802 -0
- crackerjack/mcp/dashboard.py +657 -0
- crackerjack/mcp/enhanced_progress_monitor.py +493 -0
- crackerjack/mcp/file_monitor.py +394 -0
- crackerjack/mcp/progress_components.py +607 -0
- crackerjack/mcp/progress_monitor.py +1016 -0
- crackerjack/mcp/rate_limiter.py +336 -0
- crackerjack/mcp/server.py +24 -0
- crackerjack/mcp/server_core.py +526 -0
- crackerjack/mcp/service_watchdog.py +505 -0
- crackerjack/mcp/state.py +407 -0
- crackerjack/mcp/task_manager.py +259 -0
- crackerjack/mcp/tools/README.md +27 -0
- crackerjack/mcp/tools/__init__.py +19 -0
- crackerjack/mcp/tools/core_tools.py +469 -0
- crackerjack/mcp/tools/error_analyzer.py +283 -0
- crackerjack/mcp/tools/execution_tools.py +384 -0
- crackerjack/mcp/tools/intelligence_tool_registry.py +46 -0
- crackerjack/mcp/tools/intelligence_tools.py +264 -0
- crackerjack/mcp/tools/monitoring_tools.py +628 -0
- crackerjack/mcp/tools/proactive_tools.py +367 -0
- crackerjack/mcp/tools/progress_tools.py +222 -0
- crackerjack/mcp/tools/semantic_tools.py +584 -0
- crackerjack/mcp/tools/utility_tools.py +358 -0
- crackerjack/mcp/tools/workflow_executor.py +699 -0
- crackerjack/mcp/websocket/README.md +31 -0
- crackerjack/mcp/websocket/__init__.py +14 -0
- crackerjack/mcp/websocket/app.py +54 -0
- crackerjack/mcp/websocket/endpoints.py +492 -0
- crackerjack/mcp/websocket/event_bridge.py +188 -0
- crackerjack/mcp/websocket/jobs.py +406 -0
- crackerjack/mcp/websocket/monitoring/__init__.py +25 -0
- crackerjack/mcp/websocket/monitoring/api/__init__.py +19 -0
- crackerjack/mcp/websocket/monitoring/api/dependencies.py +141 -0
- crackerjack/mcp/websocket/monitoring/api/heatmap.py +154 -0
- crackerjack/mcp/websocket/monitoring/api/intelligence.py +199 -0
- crackerjack/mcp/websocket/monitoring/api/metrics.py +203 -0
- crackerjack/mcp/websocket/monitoring/api/telemetry.py +101 -0
- crackerjack/mcp/websocket/monitoring/dashboard.py +18 -0
- crackerjack/mcp/websocket/monitoring/factory.py +109 -0
- crackerjack/mcp/websocket/monitoring/filters.py +10 -0
- crackerjack/mcp/websocket/monitoring/metrics.py +64 -0
- crackerjack/mcp/websocket/monitoring/models.py +90 -0
- crackerjack/mcp/websocket/monitoring/utils.py +171 -0
- crackerjack/mcp/websocket/monitoring/websocket_manager.py +78 -0
- crackerjack/mcp/websocket/monitoring/websockets/__init__.py +17 -0
- crackerjack/mcp/websocket/monitoring/websockets/dependencies.py +126 -0
- crackerjack/mcp/websocket/monitoring/websockets/heatmap.py +176 -0
- crackerjack/mcp/websocket/monitoring/websockets/intelligence.py +291 -0
- crackerjack/mcp/websocket/monitoring/websockets/metrics.py +291 -0
- crackerjack/mcp/websocket/monitoring_endpoints.py +21 -0
- crackerjack/mcp/websocket/server.py +174 -0
- crackerjack/mcp/websocket/websocket_handler.py +276 -0
- crackerjack/mcp/websocket_server.py +10 -0
- crackerjack/models/README.md +308 -0
- crackerjack/models/__init__.py +40 -0
- crackerjack/models/config.py +730 -0
- crackerjack/models/config_adapter.py +265 -0
- crackerjack/models/protocols.py +1535 -0
- crackerjack/models/pydantic_models.py +320 -0
- crackerjack/models/qa_config.py +145 -0
- crackerjack/models/qa_results.py +134 -0
- crackerjack/models/resource_protocols.py +299 -0
- crackerjack/models/results.py +35 -0
- crackerjack/models/semantic_models.py +258 -0
- crackerjack/models/task.py +173 -0
- crackerjack/models/test_models.py +60 -0
- crackerjack/monitoring/README.md +11 -0
- crackerjack/monitoring/__init__.py +0 -0
- crackerjack/monitoring/ai_agent_watchdog.py +405 -0
- crackerjack/monitoring/metrics_collector.py +427 -0
- crackerjack/monitoring/regression_prevention.py +580 -0
- crackerjack/monitoring/websocket_server.py +406 -0
- crackerjack/orchestration/README.md +340 -0
- crackerjack/orchestration/__init__.py +43 -0
- crackerjack/orchestration/advanced_orchestrator.py +894 -0
- crackerjack/orchestration/cache/README.md +312 -0
- crackerjack/orchestration/cache/__init__.py +37 -0
- crackerjack/orchestration/cache/memory_cache.py +338 -0
- crackerjack/orchestration/cache/tool_proxy_cache.py +340 -0
- crackerjack/orchestration/config.py +297 -0
- crackerjack/orchestration/coverage_improvement.py +180 -0
- crackerjack/orchestration/execution_strategies.py +361 -0
- crackerjack/orchestration/hook_orchestrator.py +1398 -0
- crackerjack/orchestration/strategies/README.md +401 -0
- crackerjack/orchestration/strategies/__init__.py +39 -0
- crackerjack/orchestration/strategies/adaptive_strategy.py +630 -0
- crackerjack/orchestration/strategies/parallel_strategy.py +237 -0
- crackerjack/orchestration/strategies/sequential_strategy.py +299 -0
- crackerjack/orchestration/test_progress_streamer.py +647 -0
- crackerjack/plugins/README.md +11 -0
- crackerjack/plugins/__init__.py +15 -0
- crackerjack/plugins/base.py +200 -0
- crackerjack/plugins/hooks.py +254 -0
- crackerjack/plugins/loader.py +335 -0
- crackerjack/plugins/managers.py +264 -0
- crackerjack/py313.py +191 -0
- crackerjack/security/README.md +11 -0
- crackerjack/security/__init__.py +0 -0
- crackerjack/security/audit.py +197 -0
- crackerjack/services/README.md +374 -0
- crackerjack/services/__init__.py +9 -0
- crackerjack/services/ai/README.md +295 -0
- crackerjack/services/ai/__init__.py +7 -0
- crackerjack/services/ai/advanced_optimizer.py +878 -0
- crackerjack/services/ai/contextual_ai_assistant.py +542 -0
- crackerjack/services/ai/embeddings.py +444 -0
- crackerjack/services/ai/intelligent_commit.py +328 -0
- crackerjack/services/ai/predictive_analytics.py +510 -0
- crackerjack/services/anomaly_detector.py +392 -0
- crackerjack/services/api_extractor.py +617 -0
- crackerjack/services/backup_service.py +467 -0
- crackerjack/services/bounded_status_operations.py +530 -0
- crackerjack/services/cache.py +369 -0
- crackerjack/services/changelog_automation.py +399 -0
- crackerjack/services/command_execution_service.py +305 -0
- crackerjack/services/config_integrity.py +132 -0
- crackerjack/services/config_merge.py +546 -0
- crackerjack/services/config_service.py +198 -0
- crackerjack/services/config_template.py +493 -0
- crackerjack/services/coverage_badge_service.py +173 -0
- crackerjack/services/coverage_ratchet.py +381 -0
- crackerjack/services/debug.py +733 -0
- crackerjack/services/dependency_analyzer.py +460 -0
- crackerjack/services/dependency_monitor.py +622 -0
- crackerjack/services/documentation_generator.py +493 -0
- crackerjack/services/documentation_service.py +704 -0
- crackerjack/services/enhanced_filesystem.py +497 -0
- crackerjack/services/enterprise_optimizer.py +865 -0
- crackerjack/services/error_pattern_analyzer.py +676 -0
- crackerjack/services/file_filter.py +221 -0
- crackerjack/services/file_hasher.py +149 -0
- crackerjack/services/file_io_service.py +361 -0
- crackerjack/services/file_modifier.py +615 -0
- crackerjack/services/filesystem.py +381 -0
- crackerjack/services/git.py +422 -0
- crackerjack/services/health_metrics.py +615 -0
- crackerjack/services/heatmap_generator.py +744 -0
- crackerjack/services/incremental_executor.py +380 -0
- crackerjack/services/initialization.py +823 -0
- crackerjack/services/input_validator.py +668 -0
- crackerjack/services/intelligent_commit.py +327 -0
- crackerjack/services/log_manager.py +289 -0
- crackerjack/services/logging.py +228 -0
- crackerjack/services/lsp_client.py +628 -0
- crackerjack/services/memory_optimizer.py +414 -0
- crackerjack/services/metrics.py +587 -0
- crackerjack/services/monitoring/README.md +30 -0
- crackerjack/services/monitoring/__init__.py +9 -0
- crackerjack/services/monitoring/dependency_monitor.py +678 -0
- crackerjack/services/monitoring/error_pattern_analyzer.py +676 -0
- crackerjack/services/monitoring/health_metrics.py +716 -0
- crackerjack/services/monitoring/metrics.py +587 -0
- crackerjack/services/monitoring/performance_benchmarks.py +410 -0
- crackerjack/services/monitoring/performance_cache.py +388 -0
- crackerjack/services/monitoring/performance_monitor.py +569 -0
- crackerjack/services/parallel_executor.py +527 -0
- crackerjack/services/pattern_cache.py +333 -0
- crackerjack/services/pattern_detector.py +478 -0
- crackerjack/services/patterns/__init__.py +142 -0
- crackerjack/services/patterns/agents.py +107 -0
- crackerjack/services/patterns/code/__init__.py +15 -0
- crackerjack/services/patterns/code/detection.py +118 -0
- crackerjack/services/patterns/code/imports.py +107 -0
- crackerjack/services/patterns/code/paths.py +159 -0
- crackerjack/services/patterns/code/performance.py +119 -0
- crackerjack/services/patterns/code/replacement.py +36 -0
- crackerjack/services/patterns/core.py +212 -0
- crackerjack/services/patterns/documentation/__init__.py +14 -0
- crackerjack/services/patterns/documentation/badges_markdown.py +96 -0
- crackerjack/services/patterns/documentation/comments_blocks.py +83 -0
- crackerjack/services/patterns/documentation/docstrings.py +89 -0
- crackerjack/services/patterns/formatting.py +226 -0
- crackerjack/services/patterns/operations.py +339 -0
- crackerjack/services/patterns/security/__init__.py +23 -0
- crackerjack/services/patterns/security/code_injection.py +122 -0
- crackerjack/services/patterns/security/credentials.py +190 -0
- crackerjack/services/patterns/security/path_traversal.py +221 -0
- crackerjack/services/patterns/security/unsafe_operations.py +216 -0
- crackerjack/services/patterns/templates.py +62 -0
- crackerjack/services/patterns/testing/__init__.py +18 -0
- crackerjack/services/patterns/testing/error_patterns.py +107 -0
- crackerjack/services/patterns/testing/pytest_output.py +126 -0
- crackerjack/services/patterns/tool_output/__init__.py +16 -0
- crackerjack/services/patterns/tool_output/bandit.py +72 -0
- crackerjack/services/patterns/tool_output/other.py +97 -0
- crackerjack/services/patterns/tool_output/pyright.py +67 -0
- crackerjack/services/patterns/tool_output/ruff.py +44 -0
- crackerjack/services/patterns/url_sanitization.py +114 -0
- crackerjack/services/patterns/utilities.py +42 -0
- crackerjack/services/patterns/utils.py +339 -0
- crackerjack/services/patterns/validation.py +46 -0
- crackerjack/services/patterns/versioning.py +62 -0
- crackerjack/services/predictive_analytics.py +523 -0
- crackerjack/services/profiler.py +280 -0
- crackerjack/services/quality/README.md +415 -0
- crackerjack/services/quality/__init__.py +11 -0
- crackerjack/services/quality/anomaly_detector.py +392 -0
- crackerjack/services/quality/pattern_cache.py +333 -0
- crackerjack/services/quality/pattern_detector.py +479 -0
- crackerjack/services/quality/qa_orchestrator.py +491 -0
- crackerjack/services/quality/quality_baseline.py +395 -0
- crackerjack/services/quality/quality_baseline_enhanced.py +649 -0
- crackerjack/services/quality/quality_intelligence.py +949 -0
- crackerjack/services/regex_patterns.py +58 -0
- crackerjack/services/regex_utils.py +483 -0
- crackerjack/services/secure_path_utils.py +524 -0
- crackerjack/services/secure_status_formatter.py +450 -0
- crackerjack/services/secure_subprocess.py +635 -0
- crackerjack/services/security.py +239 -0
- crackerjack/services/security_logger.py +495 -0
- crackerjack/services/server_manager.py +411 -0
- crackerjack/services/smart_scheduling.py +167 -0
- crackerjack/services/status_authentication.py +460 -0
- crackerjack/services/status_security_manager.py +315 -0
- crackerjack/services/terminal_utils.py +0 -0
- crackerjack/services/thread_safe_status_collector.py +441 -0
- crackerjack/services/tool_filter.py +368 -0
- crackerjack/services/tool_version_service.py +43 -0
- crackerjack/services/unified_config.py +115 -0
- crackerjack/services/validation_rate_limiter.py +220 -0
- crackerjack/services/vector_store.py +689 -0
- crackerjack/services/version_analyzer.py +461 -0
- crackerjack/services/version_checker.py +223 -0
- crackerjack/services/websocket_resource_limiter.py +438 -0
- crackerjack/services/zuban_lsp_service.py +391 -0
- crackerjack/slash_commands/README.md +11 -0
- crackerjack/slash_commands/__init__.py +59 -0
- crackerjack/slash_commands/init.md +112 -0
- crackerjack/slash_commands/run.md +197 -0
- crackerjack/slash_commands/status.md +127 -0
- crackerjack/tools/README.md +11 -0
- crackerjack/tools/__init__.py +30 -0
- crackerjack/tools/_git_utils.py +105 -0
- crackerjack/tools/check_added_large_files.py +139 -0
- crackerjack/tools/check_ast.py +105 -0
- crackerjack/tools/check_json.py +103 -0
- crackerjack/tools/check_jsonschema.py +297 -0
- crackerjack/tools/check_toml.py +103 -0
- crackerjack/tools/check_yaml.py +110 -0
- crackerjack/tools/codespell_wrapper.py +72 -0
- crackerjack/tools/end_of_file_fixer.py +202 -0
- crackerjack/tools/format_json.py +128 -0
- crackerjack/tools/mdformat_wrapper.py +114 -0
- crackerjack/tools/trailing_whitespace.py +198 -0
- crackerjack/tools/validate_input_validator_patterns.py +236 -0
- crackerjack/tools/validate_regex_patterns.py +188 -0
- crackerjack/ui/README.md +11 -0
- crackerjack/ui/__init__.py +1 -0
- crackerjack/ui/dashboard_renderer.py +28 -0
- crackerjack/ui/templates/README.md +11 -0
- crackerjack/utils/console_utils.py +13 -0
- crackerjack/utils/dependency_guard.py +230 -0
- crackerjack/utils/retry_utils.py +275 -0
- crackerjack/workflows/README.md +590 -0
- crackerjack/workflows/__init__.py +46 -0
- crackerjack/workflows/actions.py +811 -0
- crackerjack/workflows/auto_fix.py +444 -0
- crackerjack/workflows/container_builder.py +499 -0
- crackerjack/workflows/definitions.py +443 -0
- crackerjack/workflows/engine.py +177 -0
- crackerjack/workflows/event_bridge.py +242 -0
- crackerjack-0.45.2.dist-info/METADATA +1678 -0
- crackerjack-0.45.2.dist-info/RECORD +478 -0
- {crackerjack-0.18.2.dist-info → crackerjack-0.45.2.dist-info}/WHEEL +1 -1
- crackerjack-0.45.2.dist-info/entry_points.txt +2 -0
- crackerjack/.gitignore +0 -14
- crackerjack/.libcst.codemod.yaml +0 -18
- crackerjack/.pdm.toml +0 -1
- crackerjack/.pre-commit-config.yaml +0 -91
- crackerjack/.pytest_cache/.gitignore +0 -2
- crackerjack/.pytest_cache/CACHEDIR.TAG +0 -4
- crackerjack/.pytest_cache/README.md +0 -8
- crackerjack/.pytest_cache/v/cache/nodeids +0 -1
- crackerjack/.pytest_cache/v/cache/stepwise +0 -1
- crackerjack/.ruff_cache/.gitignore +0 -1
- crackerjack/.ruff_cache/0.1.11/3256171999636029978 +0 -0
- crackerjack/.ruff_cache/0.1.14/602324811142551221 +0 -0
- crackerjack/.ruff_cache/0.1.4/10355199064880463147 +0 -0
- crackerjack/.ruff_cache/0.1.6/15140459877605758699 +0 -0
- crackerjack/.ruff_cache/0.1.7/1790508110482614856 +0 -0
- crackerjack/.ruff_cache/0.1.9/17041001205004563469 +0 -0
- crackerjack/.ruff_cache/0.11.2/4070660268492669020 +0 -0
- crackerjack/.ruff_cache/0.11.3/9818742842212983150 +0 -0
- crackerjack/.ruff_cache/0.11.4/9818742842212983150 +0 -0
- crackerjack/.ruff_cache/0.11.6/3557596832929915217 +0 -0
- crackerjack/.ruff_cache/0.11.7/10386934055395314831 +0 -0
- crackerjack/.ruff_cache/0.11.7/3557596832929915217 +0 -0
- crackerjack/.ruff_cache/0.11.8/530407680854991027 +0 -0
- crackerjack/.ruff_cache/0.2.0/10047773857155985907 +0 -0
- crackerjack/.ruff_cache/0.2.1/8522267973936635051 +0 -0
- crackerjack/.ruff_cache/0.2.2/18053836298936336950 +0 -0
- crackerjack/.ruff_cache/0.3.0/12548816621480535786 +0 -0
- crackerjack/.ruff_cache/0.3.3/11081883392474770722 +0 -0
- crackerjack/.ruff_cache/0.3.4/676973378459347183 +0 -0
- crackerjack/.ruff_cache/0.3.5/16311176246009842383 +0 -0
- crackerjack/.ruff_cache/0.5.7/1493622539551733492 +0 -0
- crackerjack/.ruff_cache/0.5.7/6231957614044513175 +0 -0
- crackerjack/.ruff_cache/0.5.7/9932762556785938009 +0 -0
- crackerjack/.ruff_cache/0.6.0/11982804814124138945 +0 -0
- crackerjack/.ruff_cache/0.6.0/12055761203849489982 +0 -0
- crackerjack/.ruff_cache/0.6.2/1206147804896221174 +0 -0
- crackerjack/.ruff_cache/0.6.4/1206147804896221174 +0 -0
- crackerjack/.ruff_cache/0.6.5/1206147804896221174 +0 -0
- crackerjack/.ruff_cache/0.6.7/3657366982708166874 +0 -0
- crackerjack/.ruff_cache/0.6.9/285614542852677309 +0 -0
- crackerjack/.ruff_cache/0.7.1/1024065805990144819 +0 -0
- crackerjack/.ruff_cache/0.7.1/285614542852677309 +0 -0
- crackerjack/.ruff_cache/0.7.3/16061516852537040135 +0 -0
- crackerjack/.ruff_cache/0.8.4/16354268377385700367 +0 -0
- crackerjack/.ruff_cache/0.9.10/12813592349865671909 +0 -0
- crackerjack/.ruff_cache/0.9.10/923908772239632759 +0 -0
- crackerjack/.ruff_cache/0.9.3/13948373885254993391 +0 -0
- crackerjack/.ruff_cache/0.9.9/12813592349865671909 +0 -0
- crackerjack/.ruff_cache/0.9.9/8843823720003377982 +0 -0
- crackerjack/.ruff_cache/CACHEDIR.TAG +0 -1
- crackerjack/crackerjack.py +0 -855
- crackerjack/pyproject.toml +0 -214
- crackerjack-0.18.2.dist-info/METADATA +0 -420
- crackerjack-0.18.2.dist-info/RECORD +0 -59
- crackerjack-0.18.2.dist-info/entry_points.txt +0 -4
- {crackerjack-0.18.2.dist-info → crackerjack-0.45.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,793 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from ..services.regex_patterns import SAFE_PATTERNS
|
|
4
|
+
from .base import (
|
|
5
|
+
AgentContext,
|
|
6
|
+
FixResult,
|
|
7
|
+
Issue,
|
|
8
|
+
IssueType,
|
|
9
|
+
SubAgent,
|
|
10
|
+
agent_registry,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class SecurityAgent(SubAgent):
|
|
15
|
+
def __init__(self, context: AgentContext) -> None:
|
|
16
|
+
super().__init__(context)
|
|
17
|
+
|
|
18
|
+
def get_supported_types(self) -> set[IssueType]:
|
|
19
|
+
return {IssueType.SECURITY, IssueType.REGEX_VALIDATION}
|
|
20
|
+
|
|
21
|
+
async def can_handle(self, issue: Issue) -> float:
|
|
22
|
+
if issue.type not in self.get_supported_types():
|
|
23
|
+
return 0.0
|
|
24
|
+
|
|
25
|
+
message_lower = issue.message.lower()
|
|
26
|
+
|
|
27
|
+
if issue.type == IssueType.REGEX_VALIDATION:
|
|
28
|
+
return 0.95
|
|
29
|
+
|
|
30
|
+
if any(
|
|
31
|
+
keyword in message_lower
|
|
32
|
+
for keyword in (
|
|
33
|
+
"validate-regex-patterns",
|
|
34
|
+
"raw regex",
|
|
35
|
+
"regex pattern",
|
|
36
|
+
r"\g<",
|
|
37
|
+
"replacement",
|
|
38
|
+
"unsafe regex",
|
|
39
|
+
"regex vulnerability",
|
|
40
|
+
"redos",
|
|
41
|
+
)
|
|
42
|
+
):
|
|
43
|
+
return 0.95
|
|
44
|
+
|
|
45
|
+
if any(
|
|
46
|
+
keyword in message_lower
|
|
47
|
+
for keyword in (
|
|
48
|
+
"bandit",
|
|
49
|
+
"security",
|
|
50
|
+
"vulnerability",
|
|
51
|
+
"hardcoded",
|
|
52
|
+
"shell=true",
|
|
53
|
+
"b108",
|
|
54
|
+
"b602",
|
|
55
|
+
"b301",
|
|
56
|
+
"b506",
|
|
57
|
+
"unsafe",
|
|
58
|
+
"injection",
|
|
59
|
+
"pickle",
|
|
60
|
+
"yaml.load",
|
|
61
|
+
"md5",
|
|
62
|
+
"sha1",
|
|
63
|
+
"jwt_secret",
|
|
64
|
+
)
|
|
65
|
+
):
|
|
66
|
+
return 0.9
|
|
67
|
+
|
|
68
|
+
enhanced_patterns = [
|
|
69
|
+
"detect_security_keywords",
|
|
70
|
+
"detect_crypto_weak_algorithms",
|
|
71
|
+
"detect_hardcoded_credentials_advanced",
|
|
72
|
+
"detect_subprocess_shell_injection",
|
|
73
|
+
"detect_unsafe_pickle_usage",
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
for pattern_name in enhanced_patterns:
|
|
77
|
+
if SAFE_PATTERNS[pattern_name].test(issue.message):
|
|
78
|
+
return 0.9
|
|
79
|
+
|
|
80
|
+
if issue.file_path and any(
|
|
81
|
+
keyword in issue.file_path.lower()
|
|
82
|
+
for keyword in ("security", "auth", "crypto", "password", "token", "jwt")
|
|
83
|
+
):
|
|
84
|
+
return 0.7
|
|
85
|
+
|
|
86
|
+
if issue.type == IssueType.SECURITY:
|
|
87
|
+
return 0.6
|
|
88
|
+
|
|
89
|
+
return 0.0
|
|
90
|
+
|
|
91
|
+
async def analyze_and_fix(self, issue: Issue) -> FixResult:
|
|
92
|
+
self.log(f"Analyzing security issue: {issue.message}")
|
|
93
|
+
|
|
94
|
+
fixes_applied: list[str] = []
|
|
95
|
+
files_modified: list[str] = []
|
|
96
|
+
recommendations: list[str] = []
|
|
97
|
+
|
|
98
|
+
try:
|
|
99
|
+
vulnerability_type = self._identify_vulnerability_type(issue)
|
|
100
|
+
self.log(f"Identified vulnerability type: {vulnerability_type}")
|
|
101
|
+
|
|
102
|
+
fixes_applied, files_modified = await self._apply_vulnerability_fixes(
|
|
103
|
+
vulnerability_type,
|
|
104
|
+
issue,
|
|
105
|
+
fixes_applied,
|
|
106
|
+
files_modified,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
fixes_applied, files_modified = await self._apply_additional_fixes(
|
|
110
|
+
issue,
|
|
111
|
+
fixes_applied,
|
|
112
|
+
files_modified,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
success = len(fixes_applied) > 0
|
|
116
|
+
confidence = 0.95 if success else 0.4
|
|
117
|
+
|
|
118
|
+
if not success:
|
|
119
|
+
recommendations = self._get_security_recommendations()
|
|
120
|
+
|
|
121
|
+
return FixResult(
|
|
122
|
+
success=success,
|
|
123
|
+
confidence=confidence,
|
|
124
|
+
fixes_applied=fixes_applied,
|
|
125
|
+
files_modified=files_modified,
|
|
126
|
+
recommendations=recommendations,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
except Exception as e:
|
|
130
|
+
self.log(f"Error fixing security issue: {e}", "ERROR")
|
|
131
|
+
return self._create_error_fix_result(e)
|
|
132
|
+
|
|
133
|
+
async def _apply_vulnerability_fixes(
|
|
134
|
+
self,
|
|
135
|
+
vulnerability_type: str,
|
|
136
|
+
issue: Issue,
|
|
137
|
+
fixes_applied: list[str],
|
|
138
|
+
files_modified: list[str],
|
|
139
|
+
) -> tuple[list[str], list[str]]:
|
|
140
|
+
vulnerability_fix_map = {
|
|
141
|
+
"regex_validation": self._fix_regex_validation_issues,
|
|
142
|
+
"hardcoded_temp_paths": self._fix_hardcoded_temp_paths,
|
|
143
|
+
"shell_injection": self._fix_shell_injection,
|
|
144
|
+
"hardcoded_secrets": self._fix_hardcoded_secrets,
|
|
145
|
+
"unsafe_yaml": self._fix_unsafe_yaml,
|
|
146
|
+
"eval_usage": self._fix_eval_usage,
|
|
147
|
+
"weak_crypto": self._fix_weak_crypto,
|
|
148
|
+
"jwt_secrets": self._fix_jwt_secrets,
|
|
149
|
+
"pickle_usage": self._fix_pickle_usage,
|
|
150
|
+
"insecure_random": self._fix_insecure_random,
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (fix_method := vulnerability_fix_map.get(vulnerability_type)) is not None:
|
|
154
|
+
fixes = await fix_method(issue)
|
|
155
|
+
fixes_applied.extend(fixes["fixes"])
|
|
156
|
+
files_modified.extend(fixes["files"])
|
|
157
|
+
|
|
158
|
+
return fixes_applied, files_modified
|
|
159
|
+
|
|
160
|
+
async def _apply_additional_fixes(
|
|
161
|
+
self,
|
|
162
|
+
issue: Issue,
|
|
163
|
+
fixes_applied: list[str],
|
|
164
|
+
files_modified: list[str],
|
|
165
|
+
) -> tuple[list[str], list[str]]:
|
|
166
|
+
if not fixes_applied:
|
|
167
|
+
bandit_fixes = await self._run_bandit_analysis()
|
|
168
|
+
fixes_applied.extend(bandit_fixes)
|
|
169
|
+
|
|
170
|
+
if issue.file_path:
|
|
171
|
+
file_fixes = await self._fix_file_security_issues(issue.file_path)
|
|
172
|
+
fixes_applied.extend(file_fixes["fixes"])
|
|
173
|
+
if file_fixes["fixes"]:
|
|
174
|
+
files_modified.append(issue.file_path)
|
|
175
|
+
|
|
176
|
+
return fixes_applied, files_modified
|
|
177
|
+
|
|
178
|
+
def _get_security_recommendations(self) -> list[str]:
|
|
179
|
+
return [
|
|
180
|
+
"Use centralized SAFE_PATTERNS for regex operations to prevent ReDoS attacks",
|
|
181
|
+
"Avoid raw regex patterns with vulnerable replacement syntax like \\g<1>",
|
|
182
|
+
"Use tempfile module for temporary file creation instead of hardcoded paths",
|
|
183
|
+
"Avoid shell=True in subprocess calls to prevent command injection",
|
|
184
|
+
"Store secrets in environment variables using os.getenv(), never hardcode them",
|
|
185
|
+
"Replace weak cryptographic algorithms (MD5, SHA1, DES, RC4) with stronger alternatives",
|
|
186
|
+
"Use secrets module instead of random for cryptographically secure operations",
|
|
187
|
+
"Replace unsafe yaml.load() with yaml.safe_load() to prevent code execution",
|
|
188
|
+
"Avoid pickle.load() with untrusted data as it can execute arbitrary code",
|
|
189
|
+
"Use JWT secrets from environment variables, never hardcode them",
|
|
190
|
+
"Implement proper input validation and sanitization for all user inputs",
|
|
191
|
+
"Add security comments to document potential risks in legacy code",
|
|
192
|
+
"Run bandit security scanner regularly to identify new vulnerabilities",
|
|
193
|
+
"Review all subprocess calls for potential injection vulnerabilities",
|
|
194
|
+
"Ensure all cryptographic operations use secure algorithms and proper key management",
|
|
195
|
+
]
|
|
196
|
+
|
|
197
|
+
def _create_error_fix_result(self, error: Exception) -> FixResult:
|
|
198
|
+
return FixResult(
|
|
199
|
+
success=False,
|
|
200
|
+
confidence=0.0,
|
|
201
|
+
remaining_issues=[f"Failed to fix security issue: {error}"],
|
|
202
|
+
recommendations=[
|
|
203
|
+
"Manual security review may be required",
|
|
204
|
+
"Consider running bandit security scanner",
|
|
205
|
+
"Review code for common security anti-patterns",
|
|
206
|
+
],
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
def _identify_vulnerability_type(self, issue: Issue) -> str:
|
|
210
|
+
message = issue.message
|
|
211
|
+
|
|
212
|
+
if self._is_regex_validation_issue(issue):
|
|
213
|
+
return "regex_validation"
|
|
214
|
+
|
|
215
|
+
pattern_checks = self._check_enhanced_patterns(message)
|
|
216
|
+
if pattern_checks:
|
|
217
|
+
return pattern_checks
|
|
218
|
+
|
|
219
|
+
bandit_checks = self._check_bandit_patterns(message)
|
|
220
|
+
if bandit_checks:
|
|
221
|
+
return bandit_checks
|
|
222
|
+
|
|
223
|
+
legacy_checks = self._check_legacy_patterns(message)
|
|
224
|
+
if legacy_checks:
|
|
225
|
+
return legacy_checks
|
|
226
|
+
|
|
227
|
+
if self._is_jwt_secret_issue(message):
|
|
228
|
+
return "jwt_secrets"
|
|
229
|
+
|
|
230
|
+
return "unknown"
|
|
231
|
+
|
|
232
|
+
def _is_regex_validation_issue(self, issue: Issue) -> bool:
|
|
233
|
+
if issue.type == IssueType.REGEX_VALIDATION:
|
|
234
|
+
return True
|
|
235
|
+
|
|
236
|
+
message_lower = issue.message.lower()
|
|
237
|
+
return any(
|
|
238
|
+
keyword in message_lower
|
|
239
|
+
for keyword in (
|
|
240
|
+
"validate-regex-patterns",
|
|
241
|
+
"raw regex",
|
|
242
|
+
"unsafe regex",
|
|
243
|
+
r"\g<",
|
|
244
|
+
"redos",
|
|
245
|
+
)
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
def _check_enhanced_patterns(self, message: str) -> str | None:
|
|
249
|
+
pattern_map = {
|
|
250
|
+
"detect_crypto_weak_algorithms": "weak_crypto",
|
|
251
|
+
"detect_hardcoded_credentials_advanced": "hardcoded_secrets",
|
|
252
|
+
"detect_subprocess_shell_injection": "shell_injection",
|
|
253
|
+
"detect_unsafe_pickle_usage": "pickle_usage",
|
|
254
|
+
"detect_regex_redos_vulnerable": "regex_validation",
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
for pattern_name, vulnerability_type in pattern_map.items():
|
|
258
|
+
if SAFE_PATTERNS[pattern_name].test(message):
|
|
259
|
+
return vulnerability_type
|
|
260
|
+
|
|
261
|
+
return None
|
|
262
|
+
|
|
263
|
+
def _check_bandit_patterns(self, message: str) -> str | None:
|
|
264
|
+
if "B108" in message:
|
|
265
|
+
return "hardcoded_temp_paths"
|
|
266
|
+
if "B602" in message or "shell=True" in message:
|
|
267
|
+
return "shell_injection"
|
|
268
|
+
if "B301" in message or "pickle" in message.lower():
|
|
269
|
+
return "pickle_usage"
|
|
270
|
+
if "B506" in message or "yaml.load" in message:
|
|
271
|
+
return "unsafe_yaml"
|
|
272
|
+
if any(crypto in message.lower() for crypto in ("md5", "sha1", "des", "rc4")):
|
|
273
|
+
return "weak_crypto"
|
|
274
|
+
|
|
275
|
+
return None
|
|
276
|
+
|
|
277
|
+
def _check_legacy_patterns(self, message: str) -> str | None:
|
|
278
|
+
pattern_map = {
|
|
279
|
+
"detect_hardcoded_temp_paths_basic": "hardcoded_temp_paths",
|
|
280
|
+
"detect_hardcoded_secrets": "hardcoded_secrets",
|
|
281
|
+
"detect_insecure_random_usage": "insecure_random",
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
for pattern_name, vulnerability_type in pattern_map.items():
|
|
285
|
+
if SAFE_PATTERNS[pattern_name].test(message):
|
|
286
|
+
return vulnerability_type
|
|
287
|
+
|
|
288
|
+
return None
|
|
289
|
+
|
|
290
|
+
def _is_jwt_secret_issue(self, message: str) -> bool:
|
|
291
|
+
message_lower = message.lower()
|
|
292
|
+
return "jwt" in message_lower and (
|
|
293
|
+
"secret" in message_lower or "hardcoded" in message_lower
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
async def _fix_regex_validation_issues(self, issue: Issue) -> dict[str, list[str]]:
|
|
297
|
+
fixes: list[str] = []
|
|
298
|
+
files: list[str] = []
|
|
299
|
+
|
|
300
|
+
if not issue.file_path:
|
|
301
|
+
await self._fix_regex_patterns_project_wide(fixes, files)
|
|
302
|
+
return {"fixes": fixes, "files": files}
|
|
303
|
+
|
|
304
|
+
file_path = Path(issue.file_path)
|
|
305
|
+
if not file_path.exists():
|
|
306
|
+
return {"fixes": fixes, "files": files}
|
|
307
|
+
|
|
308
|
+
content = self.context.get_file_content(file_path)
|
|
309
|
+
if not content:
|
|
310
|
+
return {"fixes": fixes, "files": files}
|
|
311
|
+
|
|
312
|
+
original_content = content
|
|
313
|
+
content = await self._apply_regex_pattern_fixes(content)
|
|
314
|
+
|
|
315
|
+
if content != original_content:
|
|
316
|
+
if self.context.write_file_content(file_path, content):
|
|
317
|
+
fixes.append(f"Fixed unsafe regex patterns in {issue.file_path}")
|
|
318
|
+
files.append(str(file_path))
|
|
319
|
+
self.log(f"Fixed regex patterns in {issue.file_path}")
|
|
320
|
+
|
|
321
|
+
return {"fixes": fixes, "files": files}
|
|
322
|
+
|
|
323
|
+
async def _fix_regex_patterns_project_wide(
|
|
324
|
+
self, fixes: list[str], files: list[str]
|
|
325
|
+
) -> None:
|
|
326
|
+
try:
|
|
327
|
+
python_files = self._get_python_files_for_security_scan()
|
|
328
|
+
await self._process_python_files_for_regex_fixes(python_files, fixes, files)
|
|
329
|
+
except Exception as e:
|
|
330
|
+
self.log(f"Error during project-wide regex fixes: {e}", "ERROR")
|
|
331
|
+
|
|
332
|
+
def _get_python_files_for_security_scan(self) -> list[Path]:
|
|
333
|
+
python_files = list(self.context.project_path.rglob("*.py"))
|
|
334
|
+
return [
|
|
335
|
+
f for f in python_files if not self._should_skip_file_for_security_scan(f)
|
|
336
|
+
]
|
|
337
|
+
|
|
338
|
+
def _should_skip_file_for_security_scan(self, file_path: Path) -> bool:
|
|
339
|
+
skip_patterns = [".venv", "__pycache__", ".git"]
|
|
340
|
+
return any(part in str(file_path) for part in skip_patterns)
|
|
341
|
+
|
|
342
|
+
async def _process_python_files_for_regex_fixes(
|
|
343
|
+
self, python_files: list[Path], fixes: list[str], files: list[str]
|
|
344
|
+
) -> None:
|
|
345
|
+
for file_path in python_files:
|
|
346
|
+
await self._process_single_file_for_regex_fixes(file_path, fixes, files)
|
|
347
|
+
|
|
348
|
+
async def _process_single_file_for_regex_fixes(
|
|
349
|
+
self, file_path: Path, fixes: list[str], files: list[str]
|
|
350
|
+
) -> None:
|
|
351
|
+
content = self.context.get_file_content(file_path)
|
|
352
|
+
if not content:
|
|
353
|
+
return
|
|
354
|
+
|
|
355
|
+
original_content = content
|
|
356
|
+
content = await self._apply_regex_pattern_fixes(content)
|
|
357
|
+
|
|
358
|
+
if self._should_save_regex_fixes(content, original_content):
|
|
359
|
+
await self._save_regex_fixes_to_file(file_path, content, fixes, files)
|
|
360
|
+
|
|
361
|
+
def _should_save_regex_fixes(self, content: str, original_content: str) -> bool:
|
|
362
|
+
return content != original_content
|
|
363
|
+
|
|
364
|
+
async def _save_regex_fixes_to_file(
|
|
365
|
+
self, file_path: Path, content: str, fixes: list[str], files: list[str]
|
|
366
|
+
) -> None:
|
|
367
|
+
if self.context.write_file_content(file_path, content):
|
|
368
|
+
fixes.append(f"Fixed unsafe regex patterns in {file_path}")
|
|
369
|
+
files.append(str(file_path))
|
|
370
|
+
self.log(f"Fixed regex patterns in {file_path}")
|
|
371
|
+
|
|
372
|
+
async def _apply_regex_pattern_fixes(self, content: str) -> str:
|
|
373
|
+
from crackerjack.services.regex_utils import (
|
|
374
|
+
replace_unsafe_regex_with_safe_patterns,
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
try:
|
|
378
|
+
fixed_content = replace_unsafe_regex_with_safe_patterns(content)
|
|
379
|
+
return fixed_content
|
|
380
|
+
except Exception as e:
|
|
381
|
+
self.log(f"Error applying regex fixes: {e}", "ERROR")
|
|
382
|
+
return content
|
|
383
|
+
|
|
384
|
+
async def _fix_hardcoded_temp_paths(self, issue: Issue) -> dict[str, list[str]]:
|
|
385
|
+
fixes: list[str] = []
|
|
386
|
+
files: list[str] = []
|
|
387
|
+
|
|
388
|
+
if not issue.file_path:
|
|
389
|
+
return {"fixes": fixes, "files": files}
|
|
390
|
+
|
|
391
|
+
file_path = Path(issue.file_path)
|
|
392
|
+
if not file_path.exists():
|
|
393
|
+
return {"fixes": fixes, "files": files}
|
|
394
|
+
|
|
395
|
+
content = self.context.get_file_content(file_path)
|
|
396
|
+
if not content:
|
|
397
|
+
return {"fixes": fixes, "files": files}
|
|
398
|
+
|
|
399
|
+
lines = content.split("\n")
|
|
400
|
+
lines, modified = self._process_temp_path_fixes(lines)
|
|
401
|
+
|
|
402
|
+
if modified:
|
|
403
|
+
if self.context.write_file_content(file_path, "\n".join(lines)):
|
|
404
|
+
fixes.append(f"Fixed hardcoded temp paths in {issue.file_path}")
|
|
405
|
+
files.append(str(file_path))
|
|
406
|
+
self.log(f"Fixed hardcoded temp paths in {issue.file_path}")
|
|
407
|
+
|
|
408
|
+
return {"fixes": fixes, "files": files}
|
|
409
|
+
|
|
410
|
+
def _process_temp_path_fixes(self, lines: list[str]) -> tuple[list[str], bool]:
|
|
411
|
+
modified = False
|
|
412
|
+
|
|
413
|
+
lines, import_added = self._ensure_tempfile_import(lines)
|
|
414
|
+
if import_added:
|
|
415
|
+
modified = True
|
|
416
|
+
|
|
417
|
+
lines, paths_replaced = self._replace_hardcoded_temp_paths(lines)
|
|
418
|
+
if paths_replaced:
|
|
419
|
+
modified = True
|
|
420
|
+
|
|
421
|
+
return lines, modified
|
|
422
|
+
|
|
423
|
+
def _ensure_tempfile_import(self, lines: list[str]) -> tuple[list[str], bool]:
|
|
424
|
+
has_tempfile_import = any("import tempfile" in line for line in lines)
|
|
425
|
+
if has_tempfile_import:
|
|
426
|
+
return lines, False
|
|
427
|
+
|
|
428
|
+
import_section_end = 0
|
|
429
|
+
for i, line in enumerate(lines):
|
|
430
|
+
if line.strip().startswith(("import ", "from ")):
|
|
431
|
+
import_section_end = i + 1
|
|
432
|
+
elif line.strip() == "" and import_section_end > 0:
|
|
433
|
+
break
|
|
434
|
+
|
|
435
|
+
lines.insert(import_section_end, "import tempfile")
|
|
436
|
+
return lines, True
|
|
437
|
+
|
|
438
|
+
def _replace_hardcoded_temp_paths(self, lines: list[str]) -> tuple[list[str], bool]:
|
|
439
|
+
new_content = "\n".join(lines)
|
|
440
|
+
|
|
441
|
+
if SAFE_PATTERNS["detect_hardcoded_temp_paths_basic"].test(new_content):
|
|
442
|
+
new_content = SAFE_PATTERNS["replace_hardcoded_temp_paths"].apply(
|
|
443
|
+
new_content
|
|
444
|
+
)
|
|
445
|
+
new_content = SAFE_PATTERNS["replace_hardcoded_temp_strings"].apply(
|
|
446
|
+
new_content
|
|
447
|
+
)
|
|
448
|
+
new_content = SAFE_PATTERNS["replace_hardcoded_temp_single_quotes"].apply(
|
|
449
|
+
new_content
|
|
450
|
+
)
|
|
451
|
+
new_content = SAFE_PATTERNS["replace_test_path_patterns"].apply(new_content)
|
|
452
|
+
lines = new_content.split("\n")
|
|
453
|
+
return lines, True
|
|
454
|
+
|
|
455
|
+
return lines, False
|
|
456
|
+
|
|
457
|
+
async def _fix_shell_injection(self, issue: Issue) -> dict[str, list[str]]:
|
|
458
|
+
fixes: list[str] = []
|
|
459
|
+
files: list[str] = []
|
|
460
|
+
|
|
461
|
+
if not issue.file_path:
|
|
462
|
+
return {"fixes": fixes, "files": files}
|
|
463
|
+
|
|
464
|
+
file_path = Path(issue.file_path)
|
|
465
|
+
content = self.context.get_file_content(file_path)
|
|
466
|
+
if not content:
|
|
467
|
+
return {"fixes": fixes, "files": files}
|
|
468
|
+
|
|
469
|
+
original_content = content
|
|
470
|
+
|
|
471
|
+
from crackerjack.services.regex_patterns import apply_security_fixes
|
|
472
|
+
|
|
473
|
+
content = apply_security_fixes(content)
|
|
474
|
+
|
|
475
|
+
if content != original_content:
|
|
476
|
+
if self.context.write_file_content(file_path, content):
|
|
477
|
+
fixes.append(
|
|
478
|
+
f"Fixed shell injection vulnerability in {issue.file_path}",
|
|
479
|
+
)
|
|
480
|
+
files.append(str(file_path))
|
|
481
|
+
self.log(f"Fixed shell injection in {issue.file_path}")
|
|
482
|
+
|
|
483
|
+
return {"fixes": fixes, "files": files}
|
|
484
|
+
|
|
485
|
+
async def _fix_hardcoded_secrets(self, issue: Issue) -> dict[str, list[str]]:
|
|
486
|
+
fixes: list[str] = []
|
|
487
|
+
files: list[str] = []
|
|
488
|
+
|
|
489
|
+
if not issue.file_path:
|
|
490
|
+
return {"fixes": fixes, "files": files}
|
|
491
|
+
|
|
492
|
+
file_path = Path(issue.file_path)
|
|
493
|
+
content = self.context.get_file_content(file_path)
|
|
494
|
+
if not content:
|
|
495
|
+
return {"fixes": fixes, "files": files}
|
|
496
|
+
|
|
497
|
+
lines = content.split("\n")
|
|
498
|
+
lines, modified = self._process_hardcoded_secrets_in_lines(lines)
|
|
499
|
+
|
|
500
|
+
if modified:
|
|
501
|
+
if self.context.write_file_content(file_path, "\n".join(lines)):
|
|
502
|
+
fixes.append(f"Fixed hardcoded secrets in {issue.file_path}")
|
|
503
|
+
files.append(str(file_path))
|
|
504
|
+
self.log(f"Fixed hardcoded secrets in {issue.file_path}")
|
|
505
|
+
|
|
506
|
+
return {"fixes": fixes, "files": files}
|
|
507
|
+
|
|
508
|
+
def _process_hardcoded_secrets_in_lines(
|
|
509
|
+
self,
|
|
510
|
+
lines: list[str],
|
|
511
|
+
) -> tuple[list[str], bool]:
|
|
512
|
+
modified = False
|
|
513
|
+
|
|
514
|
+
lines, import_added = self._ensure_os_import(lines)
|
|
515
|
+
if import_added:
|
|
516
|
+
modified = True
|
|
517
|
+
|
|
518
|
+
for i, line in enumerate(lines):
|
|
519
|
+
if self._line_contains_hardcoded_secret(line):
|
|
520
|
+
new_line = self._replace_hardcoded_secret_with_env_var(line)
|
|
521
|
+
if new_line != line:
|
|
522
|
+
lines[i] = new_line
|
|
523
|
+
modified = True
|
|
524
|
+
|
|
525
|
+
return lines, modified
|
|
526
|
+
|
|
527
|
+
def _ensure_os_import(self, lines: list[str]) -> tuple[list[str], bool]:
|
|
528
|
+
has_os_import = any("import os" in line for line in lines)
|
|
529
|
+
if has_os_import:
|
|
530
|
+
return lines, False
|
|
531
|
+
|
|
532
|
+
for i, line in enumerate(lines):
|
|
533
|
+
if line.strip().startswith(("import ", "from ")):
|
|
534
|
+
lines.insert(i, "import os")
|
|
535
|
+
return lines, True
|
|
536
|
+
|
|
537
|
+
return lines, False
|
|
538
|
+
|
|
539
|
+
def _line_contains_hardcoded_secret(self, line: str) -> bool:
|
|
540
|
+
return SAFE_PATTERNS["detect_hardcoded_secrets"].test(line)
|
|
541
|
+
|
|
542
|
+
def _replace_hardcoded_secret_with_env_var(self, line: str) -> str:
|
|
543
|
+
var_name_result = SAFE_PATTERNS["extract_variable_name_from_assignment"].apply(
|
|
544
|
+
line
|
|
545
|
+
)
|
|
546
|
+
if var_name_result != line: # Pattern matched and extracted variable name
|
|
547
|
+
var_name = var_name_result
|
|
548
|
+
env_var_name = var_name.upper()
|
|
549
|
+
return f"{var_name} = os.getenv('{env_var_name}', '')"
|
|
550
|
+
return line
|
|
551
|
+
|
|
552
|
+
async def _fix_unsafe_yaml(self, issue: Issue) -> dict[str, list[str]]:
|
|
553
|
+
fixes: list[str] = []
|
|
554
|
+
files: list[str] = []
|
|
555
|
+
|
|
556
|
+
if not issue.file_path:
|
|
557
|
+
return {"fixes": fixes, "files": files}
|
|
558
|
+
|
|
559
|
+
file_path = Path(issue.file_path)
|
|
560
|
+
content = self.context.get_file_content(file_path)
|
|
561
|
+
if not content:
|
|
562
|
+
return {"fixes": fixes, "files": files}
|
|
563
|
+
|
|
564
|
+
original_content = content
|
|
565
|
+
|
|
566
|
+
from crackerjack.services.regex_patterns import SAFE_PATTERNS
|
|
567
|
+
|
|
568
|
+
content = SAFE_PATTERNS["fix_unsafe_yaml_load"].apply(content)
|
|
569
|
+
|
|
570
|
+
if content != original_content:
|
|
571
|
+
if self.context.write_file_content(file_path, content):
|
|
572
|
+
fixes.append(f"Fixed unsafe YAML loading in {issue.file_path}")
|
|
573
|
+
files.append(str(file_path))
|
|
574
|
+
self.log(f"Fixed unsafe YAML loading in {issue.file_path}")
|
|
575
|
+
|
|
576
|
+
return {"fixes": fixes, "files": files}
|
|
577
|
+
|
|
578
|
+
async def _fix_eval_usage(self, issue: Issue) -> dict[str, list[str]]:
|
|
579
|
+
fixes: list[str] = []
|
|
580
|
+
files: list[str] = []
|
|
581
|
+
|
|
582
|
+
fixes.append(
|
|
583
|
+
f"Identified eval() usage in {issue.file_path} - manual review required",
|
|
584
|
+
)
|
|
585
|
+
|
|
586
|
+
return {"fixes": fixes, "files": files}
|
|
587
|
+
|
|
588
|
+
async def _fix_weak_crypto(self, issue: Issue) -> dict[str, list[str]]:
|
|
589
|
+
fixes: list[str] = []
|
|
590
|
+
files: list[str] = []
|
|
591
|
+
|
|
592
|
+
if not issue.file_path:
|
|
593
|
+
return {"fixes": fixes, "files": files}
|
|
594
|
+
|
|
595
|
+
file_path = Path(issue.file_path)
|
|
596
|
+
content = self.context.get_file_content(file_path)
|
|
597
|
+
if not content:
|
|
598
|
+
return {"fixes": fixes, "files": files}
|
|
599
|
+
|
|
600
|
+
original_content = content
|
|
601
|
+
|
|
602
|
+
from crackerjack.services.regex_patterns import SAFE_PATTERNS
|
|
603
|
+
|
|
604
|
+
content = SAFE_PATTERNS["fix_weak_md5_hash"].apply(content)
|
|
605
|
+
content = SAFE_PATTERNS["fix_weak_sha1_hash"].apply(content)
|
|
606
|
+
|
|
607
|
+
if content != original_content:
|
|
608
|
+
if self.context.write_file_content(file_path, content):
|
|
609
|
+
fixes.append(f"Upgraded weak cryptographic hashes in {issue.file_path}")
|
|
610
|
+
files.append(str(file_path))
|
|
611
|
+
self.log(f"Fixed weak crypto in {issue.file_path}")
|
|
612
|
+
|
|
613
|
+
return {"fixes": fixes, "files": files}
|
|
614
|
+
|
|
615
|
+
async def _fix_jwt_secrets(self, issue: Issue) -> dict[str, list[str]]:
|
|
616
|
+
fixes: list[str] = []
|
|
617
|
+
files: list[str] = []
|
|
618
|
+
|
|
619
|
+
if not issue.file_path:
|
|
620
|
+
return {"fixes": fixes, "files": files}
|
|
621
|
+
|
|
622
|
+
file_path = Path(issue.file_path)
|
|
623
|
+
content = self.context.get_file_content(file_path)
|
|
624
|
+
if not content:
|
|
625
|
+
return {"fixes": fixes, "files": files}
|
|
626
|
+
|
|
627
|
+
original_content = content
|
|
628
|
+
|
|
629
|
+
content = SAFE_PATTERNS["fix_hardcoded_jwt_secret"].apply(content)
|
|
630
|
+
|
|
631
|
+
if "os.getenv" in content and "import os" not in content:
|
|
632
|
+
lines = content.split("\n")
|
|
633
|
+
import_index = 0
|
|
634
|
+
for i, line in enumerate(lines):
|
|
635
|
+
if line.strip().startswith(("import ", "from ")):
|
|
636
|
+
import_index = i + 1
|
|
637
|
+
lines.insert(import_index, "import os")
|
|
638
|
+
content = "\n".join(lines)
|
|
639
|
+
|
|
640
|
+
if content != original_content:
|
|
641
|
+
if self.context.write_file_content(file_path, content):
|
|
642
|
+
fixes.append(f"Fixed hardcoded JWT secrets in {issue.file_path}")
|
|
643
|
+
files.append(str(file_path))
|
|
644
|
+
self.log(f"Fixed JWT secrets in {issue.file_path}")
|
|
645
|
+
|
|
646
|
+
return {"fixes": fixes, "files": files}
|
|
647
|
+
|
|
648
|
+
async def _fix_pickle_usage(self, issue: Issue) -> dict[str, list[str]]:
|
|
649
|
+
fixes: list[str] = []
|
|
650
|
+
files: list[str] = []
|
|
651
|
+
|
|
652
|
+
if not issue.file_path:
|
|
653
|
+
return {"fixes": fixes, "files": files}
|
|
654
|
+
|
|
655
|
+
file_path = Path(issue.file_path)
|
|
656
|
+
content = self.context.get_file_content(file_path)
|
|
657
|
+
if not content:
|
|
658
|
+
return {"fixes": fixes, "files": files}
|
|
659
|
+
|
|
660
|
+
fixes.append(
|
|
661
|
+
f"Documented unsafe pickle usage in {issue.file_path} - manual review required"
|
|
662
|
+
)
|
|
663
|
+
|
|
664
|
+
if "pickle.load" in content:
|
|
665
|
+
lines = content.split("\n")
|
|
666
|
+
for i, line in enumerate(lines):
|
|
667
|
+
if "pickle.load" in line and "# SECURITY: " not in line:
|
|
668
|
+
lines[i] = (
|
|
669
|
+
line + " # SECURITY: pickle.load is unsafe with untrusted data"
|
|
670
|
+
)
|
|
671
|
+
if self.context.write_file_content(file_path, "\n".join(lines)):
|
|
672
|
+
fixes.append(
|
|
673
|
+
f"Added security warning for pickle usage in {issue.file_path}"
|
|
674
|
+
)
|
|
675
|
+
files.append(str(file_path))
|
|
676
|
+
self.log(
|
|
677
|
+
f"Added security warning for pickle in {issue.file_path}"
|
|
678
|
+
)
|
|
679
|
+
break
|
|
680
|
+
|
|
681
|
+
return {"fixes": fixes, "files": files}
|
|
682
|
+
|
|
683
|
+
async def _fix_insecure_random(self, issue: Issue) -> dict[str, list[str]]:
|
|
684
|
+
fixes: list[str] = []
|
|
685
|
+
files: list[str] = []
|
|
686
|
+
|
|
687
|
+
if not issue.file_path:
|
|
688
|
+
return {"fixes": fixes, "files": files}
|
|
689
|
+
|
|
690
|
+
file_path = Path(issue.file_path)
|
|
691
|
+
content = self.context.get_file_content(file_path)
|
|
692
|
+
if not content:
|
|
693
|
+
return {"fixes": fixes, "files": files}
|
|
694
|
+
|
|
695
|
+
original_content = content
|
|
696
|
+
|
|
697
|
+
content = SAFE_PATTERNS["fix_insecure_random_choice"].apply(content)
|
|
698
|
+
|
|
699
|
+
if "secrets.choice" in content and "import secrets" not in content:
|
|
700
|
+
lines = content.split("\n")
|
|
701
|
+
import_index = 0
|
|
702
|
+
for i, line in enumerate(lines):
|
|
703
|
+
if line.strip().startswith(("import ", "from ")):
|
|
704
|
+
import_index = i + 1
|
|
705
|
+
lines.insert(import_index, "import secrets")
|
|
706
|
+
content = "\n".join(lines)
|
|
707
|
+
|
|
708
|
+
if content != original_content:
|
|
709
|
+
if self.context.write_file_content(file_path, content):
|
|
710
|
+
fixes.append(f"Fixed insecure random usage in {issue.file_path}")
|
|
711
|
+
files.append(str(file_path))
|
|
712
|
+
self.log(f"Fixed insecure random usage in {issue.file_path}")
|
|
713
|
+
|
|
714
|
+
return {"fixes": fixes, "files": files}
|
|
715
|
+
|
|
716
|
+
async def _run_bandit_analysis(self) -> list[str]:
|
|
717
|
+
fixes: list[str] = []
|
|
718
|
+
|
|
719
|
+
try:
|
|
720
|
+
returncode, _, _ = await self.run_command(
|
|
721
|
+
["uv", "run", "bandit", "-r", "crackerjack/", "-f", "txt"],
|
|
722
|
+
)
|
|
723
|
+
|
|
724
|
+
if returncode == 0:
|
|
725
|
+
fixes.append("Bandit security scan completed successfully")
|
|
726
|
+
else:
|
|
727
|
+
fixes.append("Bandit identified security issues for review")
|
|
728
|
+
|
|
729
|
+
except Exception as e:
|
|
730
|
+
self.log(f"Bandit analysis failed: {e}", "WARN")
|
|
731
|
+
|
|
732
|
+
return fixes
|
|
733
|
+
|
|
734
|
+
async def _fix_file_security_issues(self, file_path: str) -> dict[str, list[str]]:
|
|
735
|
+
fixes: list[str] = []
|
|
736
|
+
files: list[str] = []
|
|
737
|
+
|
|
738
|
+
try:
|
|
739
|
+
path = Path(file_path)
|
|
740
|
+
if not self._is_valid_file_path(path):
|
|
741
|
+
return {"fixes": fixes, "files": files}
|
|
742
|
+
|
|
743
|
+
content = self.context.get_file_content(path)
|
|
744
|
+
if not content:
|
|
745
|
+
return {"fixes": fixes, "files": files}
|
|
746
|
+
|
|
747
|
+
original_content = content
|
|
748
|
+
content = await self._apply_security_fixes_to_content(content)
|
|
749
|
+
|
|
750
|
+
if content != original_content:
|
|
751
|
+
if self.context.write_file_content(path, content):
|
|
752
|
+
fixes.append(f"Applied general security fixes to {file_path}")
|
|
753
|
+
files.append(file_path)
|
|
754
|
+
self.log(f"Applied security fixes to {file_path}")
|
|
755
|
+
|
|
756
|
+
except Exception as e:
|
|
757
|
+
self.log(f"Error fixing file security issues in {file_path}: {e}", "ERROR")
|
|
758
|
+
|
|
759
|
+
return {"fixes": fixes, "files": files}
|
|
760
|
+
|
|
761
|
+
def _is_valid_file_path(self, path: Path) -> bool:
|
|
762
|
+
return path.exists() and path.is_file()
|
|
763
|
+
|
|
764
|
+
async def _apply_security_fixes_to_content(self, content: str) -> str:
|
|
765
|
+
content = await self._fix_insecure_random_usage(content)
|
|
766
|
+
return self._remove_debug_prints_with_secrets(content)
|
|
767
|
+
|
|
768
|
+
async def _fix_insecure_random_usage(self, content: str) -> str:
|
|
769
|
+
if not SAFE_PATTERNS["detect_insecure_random_usage"].test(content):
|
|
770
|
+
return content
|
|
771
|
+
|
|
772
|
+
content = self._add_secrets_import_if_needed(content)
|
|
773
|
+
|
|
774
|
+
return SAFE_PATTERNS["fix_insecure_random_choice"].apply(content)
|
|
775
|
+
|
|
776
|
+
def _add_secrets_import_if_needed(self, content: str) -> str:
|
|
777
|
+
if "import secrets" in content:
|
|
778
|
+
return content
|
|
779
|
+
|
|
780
|
+
lines = content.split("\n")
|
|
781
|
+
for i, line in enumerate(lines):
|
|
782
|
+
if line.strip().startswith(("import ", "from ")):
|
|
783
|
+
lines.insert(i + 1, "import secrets")
|
|
784
|
+
break
|
|
785
|
+
return "\n".join(lines)
|
|
786
|
+
|
|
787
|
+
def _remove_debug_prints_with_secrets(self, content: str) -> str:
|
|
788
|
+
from crackerjack.services.regex_patterns import SAFE_PATTERNS
|
|
789
|
+
|
|
790
|
+
return SAFE_PATTERNS["remove_debug_prints_with_secrets"].apply(content)
|
|
791
|
+
|
|
792
|
+
|
|
793
|
+
agent_registry.register(SecurityAgent)
|