crackerjack 0.37.9__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.
Files changed (425) hide show
  1. crackerjack/README.md +19 -0
  2. crackerjack/__init__.py +30 -1
  3. crackerjack/__main__.py +342 -1263
  4. crackerjack/adapters/README.md +18 -0
  5. crackerjack/adapters/__init__.py +27 -5
  6. crackerjack/adapters/_output_paths.py +167 -0
  7. crackerjack/adapters/_qa_adapter_base.py +309 -0
  8. crackerjack/adapters/_tool_adapter_base.py +706 -0
  9. crackerjack/adapters/ai/README.md +65 -0
  10. crackerjack/adapters/ai/__init__.py +5 -0
  11. crackerjack/adapters/ai/claude.py +853 -0
  12. crackerjack/adapters/complexity/README.md +53 -0
  13. crackerjack/adapters/complexity/__init__.py +10 -0
  14. crackerjack/adapters/complexity/complexipy.py +641 -0
  15. crackerjack/adapters/dependency/__init__.py +22 -0
  16. crackerjack/adapters/dependency/pip_audit.py +418 -0
  17. crackerjack/adapters/format/README.md +72 -0
  18. crackerjack/adapters/format/__init__.py +11 -0
  19. crackerjack/adapters/format/mdformat.py +313 -0
  20. crackerjack/adapters/format/ruff.py +516 -0
  21. crackerjack/adapters/lint/README.md +47 -0
  22. crackerjack/adapters/lint/__init__.py +11 -0
  23. crackerjack/adapters/lint/codespell.py +273 -0
  24. crackerjack/adapters/lsp/README.md +49 -0
  25. crackerjack/adapters/lsp/__init__.py +27 -0
  26. crackerjack/adapters/{rust_tool_manager.py → lsp/_manager.py} +3 -3
  27. crackerjack/adapters/{skylos_adapter.py → lsp/skylos.py} +59 -7
  28. crackerjack/adapters/{zuban_adapter.py → lsp/zuban.py} +3 -6
  29. crackerjack/adapters/refactor/README.md +59 -0
  30. crackerjack/adapters/refactor/__init__.py +12 -0
  31. crackerjack/adapters/refactor/creosote.py +318 -0
  32. crackerjack/adapters/refactor/refurb.py +406 -0
  33. crackerjack/adapters/refactor/skylos.py +494 -0
  34. crackerjack/adapters/sast/README.md +132 -0
  35. crackerjack/adapters/sast/__init__.py +32 -0
  36. crackerjack/adapters/sast/_base.py +201 -0
  37. crackerjack/adapters/sast/bandit.py +423 -0
  38. crackerjack/adapters/sast/pyscn.py +405 -0
  39. crackerjack/adapters/sast/semgrep.py +241 -0
  40. crackerjack/adapters/security/README.md +111 -0
  41. crackerjack/adapters/security/__init__.py +17 -0
  42. crackerjack/adapters/security/gitleaks.py +339 -0
  43. crackerjack/adapters/type/README.md +52 -0
  44. crackerjack/adapters/type/__init__.py +12 -0
  45. crackerjack/adapters/type/pyrefly.py +402 -0
  46. crackerjack/adapters/type/ty.py +402 -0
  47. crackerjack/adapters/type/zuban.py +522 -0
  48. crackerjack/adapters/utility/README.md +51 -0
  49. crackerjack/adapters/utility/__init__.py +10 -0
  50. crackerjack/adapters/utility/checks.py +884 -0
  51. crackerjack/agents/README.md +264 -0
  52. crackerjack/agents/__init__.py +40 -12
  53. crackerjack/agents/base.py +1 -0
  54. crackerjack/agents/claude_code_bridge.py +641 -0
  55. crackerjack/agents/coordinator.py +49 -53
  56. crackerjack/agents/dry_agent.py +187 -3
  57. crackerjack/agents/enhanced_coordinator.py +279 -0
  58. crackerjack/agents/enhanced_proactive_agent.py +185 -0
  59. crackerjack/agents/error_middleware.py +53 -0
  60. crackerjack/agents/formatting_agent.py +6 -8
  61. crackerjack/agents/helpers/__init__.py +9 -0
  62. crackerjack/agents/helpers/performance/__init__.py +22 -0
  63. crackerjack/agents/helpers/performance/performance_ast_analyzer.py +357 -0
  64. crackerjack/agents/helpers/performance/performance_pattern_detector.py +909 -0
  65. crackerjack/agents/helpers/performance/performance_recommender.py +572 -0
  66. crackerjack/agents/helpers/refactoring/__init__.py +22 -0
  67. crackerjack/agents/helpers/refactoring/code_transformer.py +536 -0
  68. crackerjack/agents/helpers/refactoring/complexity_analyzer.py +344 -0
  69. crackerjack/agents/helpers/refactoring/dead_code_detector.py +437 -0
  70. crackerjack/agents/helpers/test_creation/__init__.py +19 -0
  71. crackerjack/agents/helpers/test_creation/test_ast_analyzer.py +216 -0
  72. crackerjack/agents/helpers/test_creation/test_coverage_analyzer.py +643 -0
  73. crackerjack/agents/helpers/test_creation/test_template_generator.py +1031 -0
  74. crackerjack/agents/performance_agent.py +121 -1152
  75. crackerjack/agents/refactoring_agent.py +156 -655
  76. crackerjack/agents/semantic_agent.py +479 -0
  77. crackerjack/agents/semantic_helpers.py +356 -0
  78. crackerjack/agents/test_creation_agent.py +19 -1605
  79. crackerjack/api.py +5 -7
  80. crackerjack/cli/README.md +394 -0
  81. crackerjack/cli/__init__.py +1 -1
  82. crackerjack/cli/cache_handlers.py +23 -18
  83. crackerjack/cli/cache_handlers_enhanced.py +1 -4
  84. crackerjack/cli/facade.py +70 -8
  85. crackerjack/cli/formatting.py +13 -0
  86. crackerjack/cli/handlers/__init__.py +85 -0
  87. crackerjack/cli/handlers/advanced.py +103 -0
  88. crackerjack/cli/handlers/ai_features.py +62 -0
  89. crackerjack/cli/handlers/analytics.py +479 -0
  90. crackerjack/cli/handlers/changelog.py +271 -0
  91. crackerjack/cli/handlers/config_handlers.py +16 -0
  92. crackerjack/cli/handlers/coverage.py +84 -0
  93. crackerjack/cli/handlers/documentation.py +280 -0
  94. crackerjack/cli/handlers/main_handlers.py +497 -0
  95. crackerjack/cli/handlers/monitoring.py +371 -0
  96. crackerjack/cli/handlers.py +249 -49
  97. crackerjack/cli/interactive.py +8 -5
  98. crackerjack/cli/options.py +203 -110
  99. crackerjack/cli/semantic_handlers.py +292 -0
  100. crackerjack/cli/version.py +19 -0
  101. crackerjack/code_cleaner.py +60 -24
  102. crackerjack/config/README.md +472 -0
  103. crackerjack/config/__init__.py +256 -0
  104. crackerjack/config/global_lock_config.py +191 -54
  105. crackerjack/config/hooks.py +188 -16
  106. crackerjack/config/loader.py +239 -0
  107. crackerjack/config/settings.py +141 -0
  108. crackerjack/config/tool_commands.py +331 -0
  109. crackerjack/core/README.md +393 -0
  110. crackerjack/core/async_workflow_orchestrator.py +79 -53
  111. crackerjack/core/autofix_coordinator.py +22 -9
  112. crackerjack/core/container.py +10 -9
  113. crackerjack/core/enhanced_container.py +9 -9
  114. crackerjack/core/performance.py +1 -1
  115. crackerjack/core/performance_monitor.py +5 -3
  116. crackerjack/core/phase_coordinator.py +1018 -634
  117. crackerjack/core/proactive_workflow.py +3 -3
  118. crackerjack/core/retry.py +275 -0
  119. crackerjack/core/service_watchdog.py +167 -23
  120. crackerjack/core/session_coordinator.py +187 -382
  121. crackerjack/core/timeout_manager.py +161 -44
  122. crackerjack/core/workflow/__init__.py +21 -0
  123. crackerjack/core/workflow/workflow_ai_coordinator.py +863 -0
  124. crackerjack/core/workflow/workflow_event_orchestrator.py +1107 -0
  125. crackerjack/core/workflow/workflow_issue_parser.py +714 -0
  126. crackerjack/core/workflow/workflow_phase_executor.py +1158 -0
  127. crackerjack/core/workflow/workflow_security_gates.py +400 -0
  128. crackerjack/core/workflow_orchestrator.py +1247 -953
  129. crackerjack/data/README.md +11 -0
  130. crackerjack/data/__init__.py +8 -0
  131. crackerjack/data/models.py +79 -0
  132. crackerjack/data/repository.py +210 -0
  133. crackerjack/decorators/README.md +180 -0
  134. crackerjack/decorators/__init__.py +35 -0
  135. crackerjack/decorators/error_handling.py +649 -0
  136. crackerjack/decorators/error_handling_decorators.py +334 -0
  137. crackerjack/decorators/helpers.py +58 -0
  138. crackerjack/decorators/patterns.py +281 -0
  139. crackerjack/decorators/utils.py +58 -0
  140. crackerjack/docs/README.md +11 -0
  141. crackerjack/docs/generated/api/CLI_REFERENCE.md +1 -1
  142. crackerjack/documentation/README.md +11 -0
  143. crackerjack/documentation/ai_templates.py +1 -1
  144. crackerjack/documentation/dual_output_generator.py +11 -9
  145. crackerjack/documentation/reference_generator.py +104 -59
  146. crackerjack/dynamic_config.py +52 -61
  147. crackerjack/errors.py +1 -1
  148. crackerjack/events/README.md +11 -0
  149. crackerjack/events/__init__.py +16 -0
  150. crackerjack/events/telemetry.py +175 -0
  151. crackerjack/events/workflow_bus.py +346 -0
  152. crackerjack/exceptions/README.md +301 -0
  153. crackerjack/exceptions/__init__.py +5 -0
  154. crackerjack/exceptions/config.py +4 -0
  155. crackerjack/exceptions/tool_execution_error.py +245 -0
  156. crackerjack/executors/README.md +591 -0
  157. crackerjack/executors/__init__.py +2 -0
  158. crackerjack/executors/async_hook_executor.py +539 -77
  159. crackerjack/executors/cached_hook_executor.py +3 -3
  160. crackerjack/executors/hook_executor.py +967 -102
  161. crackerjack/executors/hook_lock_manager.py +31 -22
  162. crackerjack/executors/individual_hook_executor.py +66 -32
  163. crackerjack/executors/lsp_aware_hook_executor.py +136 -57
  164. crackerjack/executors/progress_hook_executor.py +282 -0
  165. crackerjack/executors/tool_proxy.py +23 -7
  166. crackerjack/hooks/README.md +485 -0
  167. crackerjack/hooks/lsp_hook.py +8 -9
  168. crackerjack/intelligence/README.md +557 -0
  169. crackerjack/interactive.py +37 -10
  170. crackerjack/managers/README.md +369 -0
  171. crackerjack/managers/async_hook_manager.py +41 -57
  172. crackerjack/managers/hook_manager.py +449 -79
  173. crackerjack/managers/publish_manager.py +81 -36
  174. crackerjack/managers/test_command_builder.py +290 -12
  175. crackerjack/managers/test_executor.py +93 -8
  176. crackerjack/managers/test_manager.py +1082 -75
  177. crackerjack/managers/test_progress.py +118 -26
  178. crackerjack/mcp/README.md +374 -0
  179. crackerjack/mcp/cache.py +25 -2
  180. crackerjack/mcp/client_runner.py +35 -18
  181. crackerjack/mcp/context.py +9 -9
  182. crackerjack/mcp/dashboard.py +24 -8
  183. crackerjack/mcp/enhanced_progress_monitor.py +34 -23
  184. crackerjack/mcp/file_monitor.py +27 -6
  185. crackerjack/mcp/progress_components.py +45 -34
  186. crackerjack/mcp/progress_monitor.py +6 -9
  187. crackerjack/mcp/rate_limiter.py +11 -7
  188. crackerjack/mcp/server.py +2 -0
  189. crackerjack/mcp/server_core.py +187 -55
  190. crackerjack/mcp/service_watchdog.py +12 -9
  191. crackerjack/mcp/task_manager.py +2 -2
  192. crackerjack/mcp/tools/README.md +27 -0
  193. crackerjack/mcp/tools/__init__.py +2 -0
  194. crackerjack/mcp/tools/core_tools.py +75 -52
  195. crackerjack/mcp/tools/execution_tools.py +87 -31
  196. crackerjack/mcp/tools/intelligence_tools.py +2 -2
  197. crackerjack/mcp/tools/proactive_tools.py +1 -1
  198. crackerjack/mcp/tools/semantic_tools.py +584 -0
  199. crackerjack/mcp/tools/utility_tools.py +180 -132
  200. crackerjack/mcp/tools/workflow_executor.py +87 -46
  201. crackerjack/mcp/websocket/README.md +31 -0
  202. crackerjack/mcp/websocket/app.py +11 -1
  203. crackerjack/mcp/websocket/event_bridge.py +188 -0
  204. crackerjack/mcp/websocket/jobs.py +27 -4
  205. crackerjack/mcp/websocket/monitoring/__init__.py +25 -0
  206. crackerjack/mcp/websocket/monitoring/api/__init__.py +19 -0
  207. crackerjack/mcp/websocket/monitoring/api/dependencies.py +141 -0
  208. crackerjack/mcp/websocket/monitoring/api/heatmap.py +154 -0
  209. crackerjack/mcp/websocket/monitoring/api/intelligence.py +199 -0
  210. crackerjack/mcp/websocket/monitoring/api/metrics.py +203 -0
  211. crackerjack/mcp/websocket/monitoring/api/telemetry.py +101 -0
  212. crackerjack/mcp/websocket/monitoring/dashboard.py +18 -0
  213. crackerjack/mcp/websocket/monitoring/factory.py +109 -0
  214. crackerjack/mcp/websocket/monitoring/filters.py +10 -0
  215. crackerjack/mcp/websocket/monitoring/metrics.py +64 -0
  216. crackerjack/mcp/websocket/monitoring/models.py +90 -0
  217. crackerjack/mcp/websocket/monitoring/utils.py +171 -0
  218. crackerjack/mcp/websocket/monitoring/websocket_manager.py +78 -0
  219. crackerjack/mcp/websocket/monitoring/websockets/__init__.py +17 -0
  220. crackerjack/mcp/websocket/monitoring/websockets/dependencies.py +126 -0
  221. crackerjack/mcp/websocket/monitoring/websockets/heatmap.py +176 -0
  222. crackerjack/mcp/websocket/monitoring/websockets/intelligence.py +291 -0
  223. crackerjack/mcp/websocket/monitoring/websockets/metrics.py +291 -0
  224. crackerjack/mcp/websocket/monitoring_endpoints.py +16 -2930
  225. crackerjack/mcp/websocket/server.py +1 -3
  226. crackerjack/mcp/websocket/websocket_handler.py +107 -6
  227. crackerjack/models/README.md +308 -0
  228. crackerjack/models/__init__.py +10 -1
  229. crackerjack/models/config.py +639 -22
  230. crackerjack/models/config_adapter.py +6 -6
  231. crackerjack/models/protocols.py +1167 -23
  232. crackerjack/models/pydantic_models.py +320 -0
  233. crackerjack/models/qa_config.py +145 -0
  234. crackerjack/models/qa_results.py +134 -0
  235. crackerjack/models/results.py +35 -0
  236. crackerjack/models/semantic_models.py +258 -0
  237. crackerjack/models/task.py +19 -3
  238. crackerjack/models/test_models.py +60 -0
  239. crackerjack/monitoring/README.md +11 -0
  240. crackerjack/monitoring/ai_agent_watchdog.py +5 -4
  241. crackerjack/monitoring/metrics_collector.py +4 -3
  242. crackerjack/monitoring/regression_prevention.py +4 -3
  243. crackerjack/monitoring/websocket_server.py +4 -241
  244. crackerjack/orchestration/README.md +340 -0
  245. crackerjack/orchestration/__init__.py +43 -0
  246. crackerjack/orchestration/advanced_orchestrator.py +20 -67
  247. crackerjack/orchestration/cache/README.md +312 -0
  248. crackerjack/orchestration/cache/__init__.py +37 -0
  249. crackerjack/orchestration/cache/memory_cache.py +338 -0
  250. crackerjack/orchestration/cache/tool_proxy_cache.py +340 -0
  251. crackerjack/orchestration/config.py +297 -0
  252. crackerjack/orchestration/coverage_improvement.py +13 -6
  253. crackerjack/orchestration/execution_strategies.py +6 -6
  254. crackerjack/orchestration/hook_orchestrator.py +1398 -0
  255. crackerjack/orchestration/strategies/README.md +401 -0
  256. crackerjack/orchestration/strategies/__init__.py +39 -0
  257. crackerjack/orchestration/strategies/adaptive_strategy.py +630 -0
  258. crackerjack/orchestration/strategies/parallel_strategy.py +237 -0
  259. crackerjack/orchestration/strategies/sequential_strategy.py +299 -0
  260. crackerjack/orchestration/test_progress_streamer.py +1 -1
  261. crackerjack/plugins/README.md +11 -0
  262. crackerjack/plugins/hooks.py +3 -2
  263. crackerjack/plugins/loader.py +3 -3
  264. crackerjack/plugins/managers.py +1 -1
  265. crackerjack/py313.py +191 -0
  266. crackerjack/security/README.md +11 -0
  267. crackerjack/services/README.md +374 -0
  268. crackerjack/services/__init__.py +8 -21
  269. crackerjack/services/ai/README.md +295 -0
  270. crackerjack/services/ai/__init__.py +7 -0
  271. crackerjack/services/ai/advanced_optimizer.py +878 -0
  272. crackerjack/services/{contextual_ai_assistant.py → ai/contextual_ai_assistant.py} +5 -3
  273. crackerjack/services/ai/embeddings.py +444 -0
  274. crackerjack/services/ai/intelligent_commit.py +328 -0
  275. crackerjack/services/ai/predictive_analytics.py +510 -0
  276. crackerjack/services/api_extractor.py +5 -3
  277. crackerjack/services/bounded_status_operations.py +45 -5
  278. crackerjack/services/cache.py +249 -318
  279. crackerjack/services/changelog_automation.py +7 -3
  280. crackerjack/services/command_execution_service.py +305 -0
  281. crackerjack/services/config_integrity.py +83 -39
  282. crackerjack/services/config_merge.py +9 -6
  283. crackerjack/services/config_service.py +198 -0
  284. crackerjack/services/config_template.py +13 -26
  285. crackerjack/services/coverage_badge_service.py +6 -4
  286. crackerjack/services/coverage_ratchet.py +53 -27
  287. crackerjack/services/debug.py +18 -7
  288. crackerjack/services/dependency_analyzer.py +4 -4
  289. crackerjack/services/dependency_monitor.py +13 -13
  290. crackerjack/services/documentation_generator.py +4 -2
  291. crackerjack/services/documentation_service.py +62 -33
  292. crackerjack/services/enhanced_filesystem.py +81 -27
  293. crackerjack/services/enterprise_optimizer.py +1 -1
  294. crackerjack/services/error_pattern_analyzer.py +10 -10
  295. crackerjack/services/file_filter.py +221 -0
  296. crackerjack/services/file_hasher.py +5 -7
  297. crackerjack/services/file_io_service.py +361 -0
  298. crackerjack/services/file_modifier.py +615 -0
  299. crackerjack/services/filesystem.py +80 -109
  300. crackerjack/services/git.py +99 -5
  301. crackerjack/services/health_metrics.py +4 -6
  302. crackerjack/services/heatmap_generator.py +12 -3
  303. crackerjack/services/incremental_executor.py +380 -0
  304. crackerjack/services/initialization.py +101 -49
  305. crackerjack/services/log_manager.py +2 -2
  306. crackerjack/services/logging.py +120 -68
  307. crackerjack/services/lsp_client.py +12 -12
  308. crackerjack/services/memory_optimizer.py +27 -22
  309. crackerjack/services/monitoring/README.md +30 -0
  310. crackerjack/services/monitoring/__init__.py +9 -0
  311. crackerjack/services/monitoring/dependency_monitor.py +678 -0
  312. crackerjack/services/monitoring/error_pattern_analyzer.py +676 -0
  313. crackerjack/services/monitoring/health_metrics.py +716 -0
  314. crackerjack/services/monitoring/metrics.py +587 -0
  315. crackerjack/services/{performance_benchmarks.py → monitoring/performance_benchmarks.py} +100 -14
  316. crackerjack/services/{performance_cache.py → monitoring/performance_cache.py} +21 -15
  317. crackerjack/services/{performance_monitor.py → monitoring/performance_monitor.py} +10 -6
  318. crackerjack/services/parallel_executor.py +166 -55
  319. crackerjack/services/patterns/__init__.py +142 -0
  320. crackerjack/services/patterns/agents.py +107 -0
  321. crackerjack/services/patterns/code/__init__.py +15 -0
  322. crackerjack/services/patterns/code/detection.py +118 -0
  323. crackerjack/services/patterns/code/imports.py +107 -0
  324. crackerjack/services/patterns/code/paths.py +159 -0
  325. crackerjack/services/patterns/code/performance.py +119 -0
  326. crackerjack/services/patterns/code/replacement.py +36 -0
  327. crackerjack/services/patterns/core.py +212 -0
  328. crackerjack/services/patterns/documentation/__init__.py +14 -0
  329. crackerjack/services/patterns/documentation/badges_markdown.py +96 -0
  330. crackerjack/services/patterns/documentation/comments_blocks.py +83 -0
  331. crackerjack/services/patterns/documentation/docstrings.py +89 -0
  332. crackerjack/services/patterns/formatting.py +226 -0
  333. crackerjack/services/patterns/operations.py +339 -0
  334. crackerjack/services/patterns/security/__init__.py +23 -0
  335. crackerjack/services/patterns/security/code_injection.py +122 -0
  336. crackerjack/services/patterns/security/credentials.py +190 -0
  337. crackerjack/services/patterns/security/path_traversal.py +221 -0
  338. crackerjack/services/patterns/security/unsafe_operations.py +216 -0
  339. crackerjack/services/patterns/templates.py +62 -0
  340. crackerjack/services/patterns/testing/__init__.py +18 -0
  341. crackerjack/services/patterns/testing/error_patterns.py +107 -0
  342. crackerjack/services/patterns/testing/pytest_output.py +126 -0
  343. crackerjack/services/patterns/tool_output/__init__.py +16 -0
  344. crackerjack/services/patterns/tool_output/bandit.py +72 -0
  345. crackerjack/services/patterns/tool_output/other.py +97 -0
  346. crackerjack/services/patterns/tool_output/pyright.py +67 -0
  347. crackerjack/services/patterns/tool_output/ruff.py +44 -0
  348. crackerjack/services/patterns/url_sanitization.py +114 -0
  349. crackerjack/services/patterns/utilities.py +42 -0
  350. crackerjack/services/patterns/utils.py +339 -0
  351. crackerjack/services/patterns/validation.py +46 -0
  352. crackerjack/services/patterns/versioning.py +62 -0
  353. crackerjack/services/predictive_analytics.py +21 -8
  354. crackerjack/services/profiler.py +280 -0
  355. crackerjack/services/quality/README.md +415 -0
  356. crackerjack/services/quality/__init__.py +11 -0
  357. crackerjack/services/quality/anomaly_detector.py +392 -0
  358. crackerjack/services/quality/pattern_cache.py +333 -0
  359. crackerjack/services/quality/pattern_detector.py +479 -0
  360. crackerjack/services/quality/qa_orchestrator.py +491 -0
  361. crackerjack/services/{quality_baseline.py → quality/quality_baseline.py} +163 -2
  362. crackerjack/services/{quality_baseline_enhanced.py → quality/quality_baseline_enhanced.py} +4 -1
  363. crackerjack/services/{quality_intelligence.py → quality/quality_intelligence.py} +180 -16
  364. crackerjack/services/regex_patterns.py +58 -2987
  365. crackerjack/services/regex_utils.py +55 -29
  366. crackerjack/services/secure_status_formatter.py +42 -15
  367. crackerjack/services/secure_subprocess.py +35 -2
  368. crackerjack/services/security.py +16 -8
  369. crackerjack/services/server_manager.py +40 -51
  370. crackerjack/services/smart_scheduling.py +46 -6
  371. crackerjack/services/status_authentication.py +3 -3
  372. crackerjack/services/thread_safe_status_collector.py +1 -0
  373. crackerjack/services/tool_filter.py +368 -0
  374. crackerjack/services/tool_version_service.py +9 -5
  375. crackerjack/services/unified_config.py +43 -351
  376. crackerjack/services/vector_store.py +689 -0
  377. crackerjack/services/version_analyzer.py +6 -4
  378. crackerjack/services/version_checker.py +14 -8
  379. crackerjack/services/zuban_lsp_service.py +5 -4
  380. crackerjack/slash_commands/README.md +11 -0
  381. crackerjack/slash_commands/init.md +2 -12
  382. crackerjack/slash_commands/run.md +84 -50
  383. crackerjack/tools/README.md +11 -0
  384. crackerjack/tools/__init__.py +30 -0
  385. crackerjack/tools/_git_utils.py +105 -0
  386. crackerjack/tools/check_added_large_files.py +139 -0
  387. crackerjack/tools/check_ast.py +105 -0
  388. crackerjack/tools/check_json.py +103 -0
  389. crackerjack/tools/check_jsonschema.py +297 -0
  390. crackerjack/tools/check_toml.py +103 -0
  391. crackerjack/tools/check_yaml.py +110 -0
  392. crackerjack/tools/codespell_wrapper.py +72 -0
  393. crackerjack/tools/end_of_file_fixer.py +202 -0
  394. crackerjack/tools/format_json.py +128 -0
  395. crackerjack/tools/mdformat_wrapper.py +114 -0
  396. crackerjack/tools/trailing_whitespace.py +198 -0
  397. crackerjack/tools/validate_regex_patterns.py +7 -3
  398. crackerjack/ui/README.md +11 -0
  399. crackerjack/ui/dashboard_renderer.py +28 -0
  400. crackerjack/ui/templates/README.md +11 -0
  401. crackerjack/utils/console_utils.py +13 -0
  402. crackerjack/utils/dependency_guard.py +230 -0
  403. crackerjack/utils/retry_utils.py +275 -0
  404. crackerjack/workflows/README.md +590 -0
  405. crackerjack/workflows/__init__.py +46 -0
  406. crackerjack/workflows/actions.py +811 -0
  407. crackerjack/workflows/auto_fix.py +444 -0
  408. crackerjack/workflows/container_builder.py +499 -0
  409. crackerjack/workflows/definitions.py +443 -0
  410. crackerjack/workflows/engine.py +177 -0
  411. crackerjack/workflows/event_bridge.py +242 -0
  412. {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/METADATA +678 -98
  413. crackerjack-0.45.2.dist-info/RECORD +478 -0
  414. {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/WHEEL +1 -1
  415. crackerjack/managers/test_manager_backup.py +0 -1075
  416. crackerjack/mcp/tools/execution_tools_backup.py +0 -1011
  417. crackerjack/mixins/__init__.py +0 -3
  418. crackerjack/mixins/error_handling.py +0 -145
  419. crackerjack/services/config.py +0 -358
  420. crackerjack/ui/server_panels.py +0 -125
  421. crackerjack-0.37.9.dist-info/RECORD +0 -231
  422. /crackerjack/adapters/{rust_tool_adapter.py → lsp/_base.py} +0 -0
  423. /crackerjack/adapters/{lsp_client.py → lsp/_client.py} +0 -0
  424. {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/entry_points.txt +0 -0
  425. {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/licenses/LICENSE +0 -0
@@ -1,2987 +1,58 @@
1
- import re
2
- import threading
3
- import time
4
- import typing as t
5
- from dataclasses import dataclass, field
6
- from re import Pattern
7
-
8
- MAX_INPUT_SIZE = 10 * 1024 * 1024
9
- MAX_ITERATIONS = 10
10
- PATTERN_CACHE_SIZE = 100
11
-
12
-
13
- class CompiledPatternCache:
14
- _lock = threading.RLock()
15
- _cache: dict[str, Pattern[str]] = {}
16
- _max_size = PATTERN_CACHE_SIZE
17
-
18
- @classmethod
19
- def get_compiled_pattern(cls, pattern: str) -> Pattern[str]:
20
- return cls.get_compiled_pattern_with_flags(pattern, pattern, 0)
21
-
22
- @classmethod
23
- def get_compiled_pattern_with_flags(
24
- cls, cache_key: str, pattern: str, flags: int
25
- ) -> Pattern[str]:
26
- with cls._lock:
27
- if cache_key in cls._cache:
28
- return cls._cache[cache_key]
29
-
30
- try:
31
- compiled = re.compile(pattern, flags)
32
- except re.error as e:
33
- raise ValueError(f"Invalid regex pattern '{pattern}': {e}")
34
-
35
- if len(cls._cache) >= cls._max_size:
36
- oldest_key = next(iter(cls._cache))
37
- del cls._cache[oldest_key]
38
-
39
- cls._cache[cache_key] = compiled
40
- return compiled
41
-
42
- @classmethod
43
- def clear_cache(cls) -> None:
44
- with cls._lock:
45
- cls._cache.clear()
46
-
47
- @classmethod
48
- def get_cache_stats(cls) -> dict[str, int | list[str]]:
49
- with cls._lock:
50
- return {
51
- "size": len(cls._cache),
52
- "max_size": cls._max_size,
53
- "patterns": list[t.Any](cls._cache.keys()),
54
- }
55
-
56
-
57
- def validate_pattern_safety(pattern: str) -> list[str]:
58
- warnings = []
59
-
60
- if ".*.*" in pattern:
61
- warnings.append("Multiple .* constructs may cause performance issues")
62
-
63
- if ".+.+" in pattern:
64
- warnings.append("Multiple .+ constructs may cause performance issues")
65
-
66
- nested_quantifiers = re.findall(r"[+*?]\??[+*?]", pattern)
67
- if nested_quantifiers:
68
- warnings.append(f"Nested quantifiers detected: {nested_quantifiers}")
69
-
70
- if "|" in pattern and pattern.count("|") > 10:
71
- warnings.append("Many alternations may cause performance issues")
72
-
73
- return warnings
74
-
75
-
76
- @dataclass
77
- class ValidatedPattern:
78
- name: str
79
- pattern: str
80
- replacement: str
81
- test_cases: list[tuple[str, str]]
82
- description: str = ""
83
- global_replace: bool = False
84
- flags: int = 0
85
- _compiled_pattern: Pattern[str] | None = field(default=None, init=False)
86
-
87
- def __post_init__(self) -> None:
88
- self._validate()
89
-
90
- def _validate(self) -> None:
91
- try:
92
- self._get_compiled_pattern()
93
- except ValueError as e:
94
- if "Invalid regex pattern" in str(e):
95
- error_msg = str(e).replace(f"'{self.pattern}'", f"'{self.name}'")
96
- raise ValueError(error_msg) from e
97
- raise
98
-
99
- if r"\g < " in self.replacement or r" >" in self.replacement:
100
- raise ValueError(
101
- f"Bad replacement syntax in '{self.name}': {self.replacement}. "
102
- "Use \\g<1> not \\g<1>" # REGEX OK: educational example
103
- )
104
-
105
- warnings = validate_pattern_safety(self.pattern)
106
- if warnings:
107
- pass
108
-
109
- for input_text, expected in self.test_cases:
110
- try:
111
- count = 0 if self.global_replace else 1
112
- result = self._apply_internal(input_text, count)
113
- if result != expected:
114
- raise ValueError(
115
- f"Pattern '{self.name}' failed test case: "
116
- f"'{input_text}' -> '{result}' != expected '{expected}'"
117
- )
118
- except re.error as e:
119
- raise ValueError(f"Pattern '{self.name}' failed on '{input_text}': {e}")
120
-
121
- def _get_compiled_pattern(self) -> Pattern[str]:
122
- cache_key = f"{self.pattern}|flags: {self.flags}"
123
- return CompiledPatternCache.get_compiled_pattern_with_flags(
124
- cache_key, self.pattern, self.flags
125
- )
126
-
127
- def _apply_internal(self, text: str, count: int = 1) -> str:
128
- if len(text) > MAX_INPUT_SIZE:
129
- raise ValueError(
130
- f"Input text too large: {len(text)} bytes > {MAX_INPUT_SIZE}"
131
- )
132
-
133
- return self._get_compiled_pattern().sub(self.replacement, text, count=count)
134
-
135
- def apply(self, text: str) -> str:
136
- count = 0 if self.global_replace else 1
137
- return self._apply_internal(text, count)
138
-
139
- def apply_iteratively(self, text: str, max_iterations: int = MAX_ITERATIONS) -> str:
140
- if max_iterations <= 0:
141
- raise ValueError("max_iterations must be positive")
142
-
143
- result = text
144
- for _ in range(max_iterations):
145
- new_result = self.apply(result)
146
- if new_result == result:
147
- break
148
- result = new_result
149
- else:
150
- pass
151
-
152
- return result
153
-
154
- def apply_with_timeout(self, text: str, timeout_seconds: float = 1.0) -> str:
155
- import signal
156
-
157
- def timeout_handler(signum: int, frame: t.Any) -> None:
158
- raise TimeoutError(
159
- f"Pattern '{self.name}' timed out after {timeout_seconds}s"
160
- )
161
-
162
- old_handler = signal.signal(signal.SIGALRM, timeout_handler)
163
- signal.alarm(int(timeout_seconds))
164
-
165
- try:
166
- result = self.apply(text)
167
- finally:
168
- signal.alarm(0)
169
- signal.signal(signal.SIGALRM, old_handler)
170
-
171
- return result
172
-
173
- def test(self, text: str) -> bool:
174
- compiled = self._get_compiled_pattern()
175
- return bool(compiled.search(text))
176
-
177
- def search(self, text: str) -> re.Match[str] | None:
178
- if len(text) > MAX_INPUT_SIZE:
179
- raise ValueError(
180
- f"Input text too large: {len(text)} bytes > {MAX_INPUT_SIZE}"
181
- )
182
- return self._get_compiled_pattern().search(text)
183
-
184
- def findall(self, text: str) -> list[str]:
185
- if len(text) > MAX_INPUT_SIZE:
186
- raise ValueError(
187
- f"Input text too large: {len(text)} bytes > {MAX_INPUT_SIZE}"
188
- )
189
- return self._get_compiled_pattern().findall(text)
190
-
191
- def get_performance_stats(
192
- self, text: str, iterations: int = 100
193
- ) -> dict[str, float]:
194
- times = []
195
-
196
- for _ in range(iterations):
197
- start = time.perf_counter()
198
- self.apply(text)
199
- end = time.perf_counter()
200
- times.append(end - start)
201
-
202
- return {
203
- "mean_time": sum(times) / len(times),
204
- "min_time": min(times),
205
- "max_time": max(times),
206
- "total_time": sum(times),
207
- }
208
-
209
-
210
- SAFE_PATTERNS: dict[str, ValidatedPattern] = {
211
- "fix_command_spacing": ValidatedPattern(
212
- name="fix_command_spacing",
213
- pattern=r"python\s*-\s*m\s+(\w+)",
214
- replacement=r"python -m \1",
215
- description="Fix spacing in 'python -m command' patterns",
216
- test_cases=[
217
- ("python - m crackerjack", "python -m crackerjack"),
218
- ("python -m crackerjack", "python -m crackerjack"),
219
- ("python - m pytest", "python -m pytest"),
220
- ("other python - m stuff", "other python -m stuff"),
221
- ],
222
- ),
223
- "fix_long_flag_spacing": ValidatedPattern(
224
- name="fix_long_flag_spacing",
225
- pattern=r"-\s*-\s*(\w+(?: -\w+)*)",
226
- replacement=r"--\1",
227
- description="Fix spacing in long flags like '--help'",
228
- test_cases=[
229
- ("- - help", "--help"),
230
- ("- - ai-fix", "--ai-fix"),
231
- ("--help", "--help"),
232
- ("- - start-websocket-server", "--start-websocket-server"),
233
- ],
234
- ),
235
- "fix_short_flag_spacing": ValidatedPattern(
236
- name="fix_short_flag_spacing",
237
- pattern=r"(?<!\w)-\s+(\w)(?!\w)",
238
- replacement=r"-\1",
239
- description="Fix spacing in short flags like '-t'",
240
- test_cases=[
241
- ("python -m crackerjack - t", "python -m crackerjack -t"),
242
- ("- q", "-q"),
243
- ("-t", "-t"),
244
- ("some - x flag", "some -x flag"),
245
- ],
246
- ),
247
- "fix_hyphenated_names": ValidatedPattern(
248
- name="fix_hyphenated_names",
249
- pattern=r"(\w+)\s*-\s*(\w+)",
250
- replacement=r"\1-\2",
251
- description="Fix spacing in hyphenated names and identifiers",
252
- test_cases=[
253
- ("python - pro", "python-pro"),
254
- (
255
- "pytest - hypothesis - specialist",
256
- "pytest-hypothesis - specialist",
257
- ),
258
- ("backend - architect", "backend-architect"),
259
- ("python-pro", "python-pro"),
260
- ("end - of - file-fixer", "end-of - file-fixer"),
261
- ],
262
- ),
263
- "fix_hyphenated_names_global": ValidatedPattern(
264
- name="fix_hyphenated_names_global",
265
- pattern=r"(\w+)\s+-\s+(\w+)",
266
- replacement=r"\1-\2",
267
- description="Globally fix spacing in hyphenated names (single pass only)",
268
- global_replace=True,
269
- test_cases=[
270
- ("python - pro", "python-pro"),
271
- ("end - of - file", "end-of - file"),
272
- ("already-hyphenated", "already-hyphenated"),
273
- ("start - middle - end", "start-middle - end"),
274
- ],
275
- ),
276
- "fix_spaced_hyphens": ValidatedPattern(
277
- name="fix_spaced_hyphens",
278
- pattern=r"(\w+)\s+-\s+(\w+)",
279
- replacement=r"\1-\2",
280
- description="Fix spaced hyphens with spaces around dashes (use apply_iteratively for multi-word)",
281
- global_replace=True,
282
- test_cases=[
283
- ("python - pro", "python-pro"),
284
- (
285
- "pytest - hypothesis - specialist",
286
- "pytest-hypothesis - specialist",
287
- ),
288
- (
289
- "end - of - file - fixer",
290
- "end-of - file-fixer",
291
- ),
292
- ("already-hyphenated", "already-hyphenated"),
293
- ("mixed-case with - spaces", "mixed-case with-spaces"),
294
- ],
295
- ),
296
- "fix_debug_log_pattern": ValidatedPattern(
297
- name="fix_debug_log_pattern",
298
- pattern=r"crackerjack\s*-\s*debug",
299
- replacement="crackerjack-debug",
300
- description="Fix spacing in debug log patterns",
301
- test_cases=[
302
- ("crackerjack - debug-12345.log", "crackerjack-debug-12345.log"),
303
- ("crackerjack-debug.log", "crackerjack-debug.log"),
304
- ("old crackerjack - debug files", "old crackerjack-debug files"),
305
- ],
306
- ),
307
- "fix_job_file_pattern": ValidatedPattern(
308
- name="fix_job_file_pattern",
309
- pattern=r"job\s*-\s*(\{[^}]+\}|\w+)",
310
- replacement=r"job-\1",
311
- description="Fix spacing in job file patterns",
312
- test_cases=[
313
- ("job - {self.web_job_id}.json", "job-{self.web_job_id}.json"),
314
- ("job - abc123.json", "job-abc123.json"),
315
- ("job-existing.json", "job-existing.json"),
316
- ],
317
- ),
318
- "fix_markdown_bold": ValidatedPattern(
319
- name="fix_markdown_bold",
320
- pattern=r"\*\s+\*(.+?)\s*\*\s+\*",
321
- replacement=r"**\1**",
322
- description="Fix spacing in markdown bold patterns",
323
- test_cases=[
324
- ("* *Bold Text * *", "**Bold Text**"),
325
- ("* *🧪 pytest-specialist * *", "**🧪 pytest-specialist**"),
326
- ("**Already Bold**", "**Already Bold**"),
327
- ],
328
- ),
329
- "mask_pypi_token": ValidatedPattern(
330
- name="mask_pypi_token",
331
- pattern=r"\bpypi-[a-zA-Z0-9_-]{12,}\b",
332
- replacement="pypi-****",
333
- description="Mask PyPI authentication tokens (word boundaries to prevent"
334
- " false matches)",
335
- global_replace=True,
336
- test_cases=[
337
- ("pypi-AgEIcHlwaS5vcmcCJGE4M2Y3ZjI", "pypi-****"),
338
- (
339
- "Using token: pypi-AgEIcHlwaS5vcmcCJGE4M2Y3ZjI for upload",
340
- "Using token: pypi-**** for upload",
341
- ),
342
- ("pypi-short", "pypi-short"),
343
- (
344
- "not pypi-AgEIcHlwaS5vcmcCJGE4M2Y3ZjI",
345
- "not pypi-****",
346
- ),
347
- (
348
- "Multiple pypi-token1234567890 and pypi-anothertokenhere",
349
- "Multiple pypi-**** and pypi-****",
350
- ),
351
- ],
352
- ),
353
- "mask_github_token": ValidatedPattern(
354
- name="mask_github_token",
355
- pattern=r"\bghp_[a-zA-Z0-9]{36}\b",
356
- replacement="ghp_****",
357
- description="Mask GitHub personal access tokens (exactly 40 chars total"
358
- " with word boundaries)",
359
- global_replace=True,
360
- test_cases=[
361
- ("ghp_1234567890abcdef1234567890abcdef1234", "ghp_****"),
362
- (
363
- "GITHUB_TOKEN=ghp_1234567890abcdef1234567890abcdef1234",
364
- "GITHUB_TOKEN=ghp_****",
365
- ),
366
- ("ghp_short", "ghp_short"),
367
- (
368
- "ghp_1234567890abcdef1234567890abcdef12345",
369
- "ghp_1234567890abcdef1234567890abcdef12345",
370
- ),
371
- (
372
- "Multiple ghp_1234567890abcdef1234567890abcdef1234 and"
373
- " ghp_abcdef1234567890abcdef12345678901234",
374
- "Multiple ghp_**** and ghp_****",
375
- ),
376
- ],
377
- ),
378
- "mask_generic_long_token": ValidatedPattern(
379
- name="mask_generic_long_token",
380
- pattern=r"\b[a-zA-Z0-9_-]{32,}\b",
381
- replacement="****",
382
- description="Mask generic long tokens (32+ chars, word boundaries to avoid"
383
- " false positives)",
384
- global_replace=True,
385
- test_cases=[
386
- ("secret_key=abcdef1234567890abcdef1234567890abcdef", "secret_key=****"),
387
- (
388
- "Short token abc123def456",
389
- "Short token abc123def456",
390
- ),
391
- (
392
- "File path "
393
- "/very/long/path/that/should/not/be/masked/even/though/its/long",
394
- "File path "
395
- "/very/long/path/that/should/not/be/masked/even/though/its/long",
396
- ),
397
- ("API_KEY=verylongapikeyhere1234567890123456", "API_KEY=****"),
398
- (
399
- "Long-token_with-underscores_123456789012345678",
400
- "****",
401
- ),
402
- ],
403
- ),
404
- "mask_token_assignment": ValidatedPattern(
405
- name="mask_token_assignment",
406
- pattern=r"(?i)\b(token\s*[=: ]\s*)['\"]([^'\"]{8,})['\"]",
407
- replacement=r"\1'****'",
408
- description="Mask token assignments in various formats (case insensitive)",
409
- global_replace=True,
410
- test_cases=[
411
- ('token="abc123def456789"', "token='****'"),
412
- ("token='long_secret_token_here'", "token='****'"),
413
- ('token: "another_secret_token"', "token: '****'"),
414
- ("token = 'spaced_assignment_token'", "token = '****'"),
415
- ('token="short"', 'token="short"'),
416
- (
417
- "not_token='should_not_be_masked'",
418
- "not_token='should_not_be_masked'",
419
- ),
420
- ('TOKEN="UPPERCASE_TOKEN_HERE"', "TOKEN='****'"),
421
- ],
422
- ),
423
- "mask_password_assignment": ValidatedPattern(
424
- name="mask_password_assignment",
425
- pattern=r"(?i)\b(password\s*[=: ]\s*)['\"]([^'\"]{8,})['\"]",
426
- replacement=r"\1'****'",
427
- description="Mask password assignments in various formats (case insensitive)",
428
- global_replace=True,
429
- test_cases=[
430
- ('password="secret123456"', "password='****'"),
431
- ("password='my_long_password'", "password='****'"),
432
- ('password: "another_secret_password"', "password: '****'"),
433
- ("password = 'spaced_password_assignment'", "password = '****'"),
434
- ('password="short"', 'password="short"'),
435
- (
436
- "not_password='should_not_be_masked'",
437
- "not_password='should_not_be_masked'",
438
- ),
439
- ('PASSWORD="UPPERCASE_PASSWORD"', "PASSWORD='****'"),
440
- ],
441
- ),
442
- "update_pyproject_version": ValidatedPattern(
443
- name="update_pyproject_version",
444
- pattern=r'^(version\s*=\s*["\'])([^"\']+)(["\'])$',
445
- replacement=r"\g<1>NEW_VERSION\g<3>",
446
- description="Update version in pyproject.toml files (NEW_VERSION placeholder"
447
- " replaced dynamically)",
448
- test_cases=[
449
- ('version = "1.2.3"', 'version = "NEW_VERSION"'),
450
- ("version='0.1.0'", "version='NEW_VERSION'"),
451
- ('version="1.0.0-beta"', 'version="NEW_VERSION"'),
452
- ("version = '2.1.0'", "version = 'NEW_VERSION'"),
453
- ("version='10.20.30'", "version='NEW_VERSION'"),
454
- ('name = "my-package"', 'name = "my-package"'),
455
- ],
456
- ),
457
- "remove_trailing_whitespace": ValidatedPattern(
458
- name="remove_trailing_whitespace",
459
- pattern=r"[ \t]+$",
460
- replacement="",
461
- description="Remove trailing whitespace from lines",
462
- global_replace=True,
463
- test_cases=[
464
- ("line with spaces ", "line with spaces"),
465
- ("line with tabs\t\t", "line with tabs"),
466
- ("normal line", "normal line"),
467
- ("mixed \t ", "mixed"),
468
- ("", ""),
469
- ],
470
- ),
471
- "normalize_multiple_newlines": ValidatedPattern(
472
- name="normalize_multiple_newlines",
473
- pattern=r"\n{3,}",
474
- replacement="\n\n",
475
- description="Normalize multiple consecutive newlines to maximum 2",
476
- global_replace=True,
477
- test_cases=[
478
- ("line1\n\n\nline2", "line1\n\nline2"),
479
- ("line1\n\n\n\n\nline2", "line1\n\nline2"),
480
- ("line1\n\nline2", "line1\n\nline2"),
481
- ("line1\nline2", "line1\nline2"),
482
- ],
483
- ),
484
- "fix_subprocess_run_shell": ValidatedPattern(
485
- name="fix_subprocess_run_shell",
486
- pattern=r"subprocess\.run\(([^,]+),\s*shell=True\)",
487
- replacement=r"subprocess.run(\1.split())",
488
- description="Remove shell=True from subprocess.run calls",
489
- global_replace=True,
490
- test_cases=[
491
- ("subprocess.run(cmd, shell=True)", "subprocess.run(cmd.split())"),
492
- (
493
- "subprocess.run('ls -la', shell=True)",
494
- "subprocess.run('ls -la'.split())",
495
- ),
496
- (
497
- "subprocess.run(command, shell=False)",
498
- "subprocess.run(command, shell=False)",
499
- ),
500
- ],
501
- ),
502
- "fix_subprocess_call_shell": ValidatedPattern(
503
- name="fix_subprocess_call_shell",
504
- pattern=r"subprocess\.call\(([^,]+),\s*shell=True\)",
505
- replacement=r"subprocess.call(\1.split())",
506
- description="Remove shell=True from subprocess.call calls",
507
- global_replace=True,
508
- test_cases=[
509
- ("subprocess.call(cmd, shell=True)", "subprocess.call(cmd.split())"),
510
- (
511
- "subprocess.call('ls -la', shell=True)",
512
- "subprocess.call('ls -la'.split())",
513
- ),
514
- (
515
- "subprocess.call(command, shell=False)",
516
- "subprocess.call(command, shell=False)",
517
- ),
518
- ],
519
- ),
520
- "fix_subprocess_popen_shell": ValidatedPattern(
521
- name="fix_subprocess_popen_shell",
522
- pattern=r"subprocess\.Popen\(([^,]+), \s*shell=True\)",
523
- replacement=r"subprocess.Popen(\1.split())",
524
- description="Remove shell=True from subprocess.Popen calls",
525
- global_replace=True,
526
- test_cases=[
527
- ("subprocess.Popen(cmd, shell=True)", "subprocess.Popen(cmd.split())"),
528
- (
529
- "subprocess.Popen('ls -la', shell=True)",
530
- "subprocess.Popen('ls -la'.split())",
531
- ),
532
- (
533
- "subprocess.Popen(command, shell=False)",
534
- "subprocess.Popen(command, shell=False)",
535
- ),
536
- ],
537
- ),
538
- "fix_unsafe_yaml_load": ValidatedPattern(
539
- name="fix_unsafe_yaml_load",
540
- pattern=r"\byaml\.load\(",
541
- replacement="yaml.safe_load(",
542
- description="Replace unsafe yaml.load with yaml.safe_load",
543
- global_replace=True,
544
- test_cases=[
545
- ("yaml.load(file)", "yaml.safe_load(file)"),
546
- ("data = yaml.load(content)", "data = yaml.safe_load(content)"),
547
- ("yaml.safe_load(content)", "yaml.safe_load(content)"),
548
- (
549
- "my_yaml.load(content)",
550
- "my_yaml.load(content)",
551
- ),
552
- ],
553
- ),
554
- "fix_weak_md5_hash": ValidatedPattern(
555
- name="fix_weak_md5_hash",
556
- pattern=r"\bhashlib\.md5\(",
557
- replacement="hashlib.sha256(",
558
- description="Replace weak MD5 hashing with SHA256",
559
- global_replace=True,
560
- test_cases=[
561
- ("hashlib.md5(data)", "hashlib.sha256(data)"),
562
- ("hash = hashlib.md5(content)", "hash = hashlib.sha256(content)"),
563
- ("hashlib.sha256(data)", "hashlib.sha256(data)"),
564
- ],
565
- ),
566
- "fix_weak_sha1_hash": ValidatedPattern(
567
- name="fix_weak_sha1_hash",
568
- pattern=r"\bhashlib\.sha1\(",
569
- replacement="hashlib.sha256(",
570
- description="Replace weak SHA1 hashing with SHA256",
571
- global_replace=True,
572
- test_cases=[
573
- ("hashlib.sha1(data)", "hashlib.sha256(data)"),
574
- ("hash = hashlib.sha1(content)", "hash = hashlib.sha256(content)"),
575
- ("hashlib.sha256(data)", "hashlib.sha256(data)"),
576
- ],
577
- ),
578
- "fix_insecure_random_choice": ValidatedPattern(
579
- name="fix_insecure_random_choice",
580
- pattern=r"random\.choice\(([^)]+)\)",
581
- replacement=r"secrets.choice(\1)",
582
- description="Replace insecure random.choice with secrets.choice",
583
- global_replace=True,
584
- test_cases=[
585
- ("random.choice(options)", "secrets.choice(options)"),
586
- ("item = random.choice(items)", "item = secrets.choice(items)"),
587
- ("secrets.choice(options)", "secrets.choice(options)"),
588
- ],
589
- ),
590
- "remove_debug_prints_with_secrets": ValidatedPattern(
591
- name="remove_debug_prints_with_secrets",
592
- pattern=r"print\s*\([^)]*(?: password|secret|key|token)[^)]*\)",
593
- replacement="",
594
- description="Remove debug print statements that contain sensitive information",
595
- global_replace=True,
596
- test_cases=[
597
- ('print("password: ", password)', ""),
598
- ("print(f'Token: {token}')", ""),
599
- ("print('Debug secret value')", ""),
600
- (
601
- "print('Normal debug message')",
602
- "print('Normal debug message')",
603
- ),
604
- ('print("API key is", key)', ""),
605
- ],
606
- ),
607
- "normalize_assert_statements": ValidatedPattern(
608
- name="normalize_assert_statements",
609
- pattern=r"assert (.+?)\s*==\s*(.+)",
610
- replacement=r"assert \1 == \2",
611
- description="Normalize spacing around == in assert statements",
612
- global_replace=True,
613
- test_cases=[
614
- ("assert result==expected", "assert result == expected"),
615
- ("assert value == other", "assert value == other"),
616
- ("assert result== expected", "assert result == expected"),
617
- ("assert result ==expected", "assert result == expected"),
618
- (
619
- "assert result == expected",
620
- "assert result == expected",
621
- ),
622
- ],
623
- ),
624
- "validate_job_id_alphanumeric": ValidatedPattern(
625
- name="validate_job_id_alphanumeric",
626
- pattern=r"^[a-zA-Z0-9_-]+$",
627
- replacement="VALID",
628
- description="Validate job ID contains only alphanumeric characters, "
629
- "underscores, and hyphens",
630
- test_cases=[
631
- ("valid_job-123", "VALID"),
632
- ("another_valid-job_456", "VALID"),
633
- ("job_123", "VALID"),
634
- ],
635
- ),
636
- "remove_coverage_fail_under": ValidatedPattern(
637
- name="remove_coverage_fail_under",
638
- pattern=r"--cov-fail-under=\d+\.?\d*\s*",
639
- replacement="",
640
- description="Remove coverage fail-under flags from pytest addopts",
641
- global_replace=True,
642
- test_cases=[
643
- ("--cov-fail-under=85 --verbose", "--verbose"),
644
- ("--cov-fail-under=90.5 -x", "-x"),
645
- ("--verbose --cov-fail-under=80 ", "--verbose "),
646
- ("--no-cov", "--no-cov"),
647
- ],
648
- ),
649
- "update_coverage_requirement": ValidatedPattern(
650
- name="update_coverage_requirement",
651
- pattern=r"(--cov-fail-under=)\d+\.?\d*",
652
- replacement=r"\1NEW_COVERAGE",
653
- description="Update coverage fail-under requirement (NEW_COVERAGE placeholder"
654
- " replaced dynamically)",
655
- test_cases=[
656
- ("--cov-fail-under=85", "--cov-fail-under=NEW_COVERAGE"),
657
- ("--cov-fail-under=90.5", "--cov-fail-under=NEW_COVERAGE"),
658
- ("--verbose", "--verbose"),
659
- ],
660
- ),
661
- "detect_directory_traversal_basic": ValidatedPattern(
662
- name="detect_directory_traversal_basic",
663
- pattern=r"\.\./",
664
- replacement="[TRAVERSAL]",
665
- description="Detect basic directory traversal patterns (../)",
666
- global_replace=True,
667
- test_cases=[
668
- ("../config.txt", "[TRAVERSAL]config.txt"),
669
- ("normal/path", "normal/path"),
670
- ("../../etc/passwd", "[TRAVERSAL][TRAVERSAL]etc/passwd"),
671
- ],
672
- ),
673
- "detect_directory_traversal_backslash": ValidatedPattern(
674
- name="detect_directory_traversal_backslash",
675
- pattern=r"\.\.[/\\]",
676
- replacement="[TRAVERSAL]",
677
- description="Detect directory traversal with forward/back slashes",
678
- global_replace=True,
679
- test_cases=[
680
- ("..\\config.txt", "[TRAVERSAL]config.txt"),
681
- ("../config.txt", "[TRAVERSAL]config.txt"),
682
- ("normal/path", "normal/path"),
683
- ],
684
- ),
685
- "detect_url_encoded_traversal": ValidatedPattern(
686
- name="detect_url_encoded_traversal",
687
- pattern=r"%2e%2e%2f",
688
- replacement="[TRAVERSAL]",
689
- description="Detect URL encoded directory traversal (%2e%2e%2f = ../)",
690
- global_replace=True,
691
- test_cases=[
692
- ("path/%2e%2e%2f/config", "path/[TRAVERSAL]/config"),
693
- ("normal/path", "normal/path"),
694
- ("%2e%2e%2fpasswd", "[TRAVERSAL]passwd"),
695
- ],
696
- ),
697
- "detect_double_url_encoded_traversal": ValidatedPattern(
698
- name="detect_double_url_encoded_traversal",
699
- pattern=r"%252e%252e%252f",
700
- replacement="[TRAVERSAL]",
701
- description="Detect double URL encoded directory traversal",
702
- global_replace=True,
703
- test_cases=[
704
- ("path/%252e%252e%252f/config", "path/[TRAVERSAL]/config"),
705
- ("normal/path", "normal/path"),
706
- ],
707
- ),
708
- "detect_null_bytes_url": ValidatedPattern(
709
- name="detect_null_bytes_url",
710
- pattern=r"%00",
711
- replacement="[NULL]",
712
- description="Detect URL encoded null bytes",
713
- global_replace=True,
714
- test_cases=[
715
- ("file.txt%00.jpg", "file.txt[NULL].jpg"),
716
- ("normal.txt", "normal.txt"),
717
- ],
718
- ),
719
- "detect_null_bytes_literal": ValidatedPattern(
720
- name="detect_null_bytes_literal",
721
- pattern=r"\\x00",
722
- replacement="[NULL]",
723
- description="Detect literal null byte patterns",
724
- global_replace=True,
725
- test_cases=[
726
- ("file.txt\\x00", "file.txt[NULL]"),
727
- ("normal.txt", "normal.txt"),
728
- ],
729
- ),
730
- "detect_utf8_overlong_null": ValidatedPattern(
731
- name="detect_utf8_overlong_null",
732
- pattern=r"%c0%80",
733
- replacement="[NULL]",
734
- description="Detect UTF-8 overlong null byte encoding",
735
- global_replace=True,
736
- test_cases=[
737
- ("file.txt%c0%80", "file.txt[NULL]"),
738
- ("normal.txt", "normal.txt"),
739
- ],
740
- ),
741
- "detect_sys_directory_pattern": ValidatedPattern(
742
- name="detect_sys_directory_pattern",
743
- pattern=r"^/sys/?.*",
744
- replacement="[DANGER]",
745
- description="Detect access to /sys directory",
746
- test_cases=[
747
- ("/sys/", "[DANGER]"),
748
- ("/sys/devices", "[DANGER]"),
749
- ("/usr/sys", "/usr/sys"),
750
- ],
751
- ),
752
- "detect_proc_directory_pattern": ValidatedPattern(
753
- name="detect_proc_directory_pattern",
754
- pattern=r"^/proc/?.*",
755
- replacement="[DANGER]",
756
- description="Detect access to /proc directory",
757
- test_cases=[
758
- ("/proc/", "[DANGER]"),
759
- ("/proc/self", "[DANGER]"),
760
- ("/usr/proc", "/usr/proc"),
761
- ],
762
- ),
763
- "detect_etc_directory_pattern": ValidatedPattern(
764
- name="detect_etc_directory_pattern",
765
- pattern=r"^/etc/?.*",
766
- replacement="[DANGER]",
767
- description="Detect access to /etc directory",
768
- test_cases=[
769
- ("/etc/", "[DANGER]"),
770
- ("/etc/passwd", "[DANGER]"),
771
- ("/usr/etc", "/usr/etc"),
772
- ],
773
- ),
774
- "detect_boot_directory_pattern": ValidatedPattern(
775
- name="detect_boot_directory_pattern",
776
- pattern=r"^/boot/?.*",
777
- replacement="[DANGER]",
778
- description="Detect access to /boot directory",
779
- test_cases=[
780
- ("/boot/", "[DANGER]"),
781
- ("/boot/grub", "[DANGER]"),
782
- ("/usr/boot", "/usr/boot"),
783
- ],
784
- ),
785
- "detect_dev_directory_pattern": ValidatedPattern(
786
- name="detect_dev_directory_pattern",
787
- pattern=r"^/dev/?.*",
788
- replacement="[DANGER]",
789
- description="Detect access to /dev directory",
790
- test_cases=[
791
- ("/dev/", "[DANGER]"),
792
- ("/dev/null", "[DANGER]"),
793
- ("/usr/dev", "/usr/dev"),
794
- ],
795
- ),
796
- "detect_root_directory_pattern": ValidatedPattern(
797
- name="detect_root_directory_pattern",
798
- pattern=r"^/root/?.*",
799
- replacement="[DANGER]",
800
- description="Detect access to /root directory",
801
- test_cases=[
802
- ("/root/", "[DANGER]"),
803
- ("/root/.ssh", "[DANGER]"),
804
- ("/usr/root", "/usr/root"),
805
- ],
806
- ),
807
- "detect_var_log_directory_pattern": ValidatedPattern(
808
- name="detect_var_log_directory_pattern",
809
- pattern=r"^/var/log/?.*",
810
- replacement="[DANGER]",
811
- description="Detect access to /var/log directory",
812
- test_cases=[
813
- ("/var/log/", "[DANGER]"),
814
- ("/var/log/messages", "[DANGER]"),
815
- ("/usr/var/log", "/usr/var/log"),
816
- ],
817
- ),
818
- "detect_bin_directory_pattern": ValidatedPattern(
819
- name="detect_bin_directory_pattern",
820
- pattern=r"^/(usr/)?bin/?.*",
821
- replacement="[DANGER]",
822
- description="Detect access to /bin or /usr/bin directories",
823
- test_cases=[
824
- ("/bin/", "[DANGER]"),
825
- ("/usr/bin/", "[DANGER]"),
826
- ("/usr/local/bin", "/usr/local/bin"),
827
- ],
828
- ),
829
- "detect_sbin_directory_pattern": ValidatedPattern(
830
- name="detect_sbin_directory_pattern",
831
- pattern=r"^/(usr/)?sbin/?.*",
832
- replacement="[DANGER]",
833
- description="Detect access to /sbin or /usr/sbin directories",
834
- test_cases=[
835
- ("/sbin/", "[DANGER]"),
836
- ("/usr/sbin/", "[DANGER]"),
837
- ("/usr/local/sbin", "/usr/local/sbin"),
838
- ],
839
- ),
840
- "detect_parent_directory_in_path": ValidatedPattern(
841
- name="detect_parent_directory_in_path",
842
- pattern=r"\.\.",
843
- replacement="[PARENT]",
844
- description="Detect parent directory references anywhere in path",
845
- global_replace=True,
846
- test_cases=[
847
- ("../config", "[PARENT]/config"),
848
- ("safe/path", "safe/path"),
849
- ("path/../other", "path/[PARENT]/other"),
850
- ],
851
- ),
852
- "detect_suspicious_temp_traversal": ValidatedPattern(
853
- name="detect_suspicious_temp_traversal",
854
- pattern=r"/tmp/.*\.\./", # nosec B108
855
- replacement="[SUSPICIOUS]",
856
- description="Detect traversal attempts in temp directories",
857
- test_cases=[
858
- ("/tmp/safe/../etc/passwd", "[SUSPICIOUS]etc/passwd"), # nosec B108
859
- ("/tmp/normal/file.txt", "/tmp/normal/file.txt"), # nosec B108
860
- ],
861
- ),
862
- "detect_suspicious_var_traversal": ValidatedPattern(
863
- name="detect_suspicious_var_traversal",
864
- pattern=r"/var/.*\.\./",
865
- replacement="[SUSPICIOUS]",
866
- description="Detect traversal attempts in var directories",
867
- test_cases=[
868
- ("/var/lib/../etc/passwd", "[SUSPICIOUS]etc/passwd"),
869
- ("/var/lib/normal.txt", "/var/lib/normal.txt"),
870
- ],
871
- ),
872
- "ruff_check_error": ValidatedPattern(
873
- name="ruff_check_error",
874
- pattern=r"^(.+?): (\d+): (\d+): ([A-Z]\d+) (.+)$",
875
- replacement=r"File: \1, Line: \2, Col: \3, Code: \4, Message: \5",
876
- description="Parse ruff-check error output: file: line: col: code message",
877
- test_cases=[
878
- (
879
- "crackerjack/core.py: 123: 45: E501 line too long",
880
- "File: crackerjack/core.py, Line: 123, Col: 45, Code: E501, Message: "
881
- "line too long",
882
- ),
883
- (
884
- "./test.py: 1: 1: F401 unused import",
885
- "File: ./test.py, Line: 1, Col: 1, Code: F401, Message: unused import",
886
- ),
887
- (
888
- "src/main.py: 999: 80: W291 trailing whitespace",
889
- "File: src/main.py, Line: 999, Col: 80, Code: W291, Message: trailing "
890
- "whitespace",
891
- ),
892
- ],
893
- ),
894
- "ruff_check_summary": ValidatedPattern(
895
- name="ruff_check_summary",
896
- pattern=r"Found (\d+) error",
897
- replacement=r"Found \1 error(s)",
898
- description="Parse ruff-check summary line for error count",
899
- test_cases=[
900
- ("Found 5 error", "Found 5 error(s)"),
901
- ("Found 1 error in 3 files", "Found 1 error(s) in 3 files"),
902
- ("Found 42 error detected", "Found 42 error(s) detected"),
903
- ],
904
- ),
905
- "pyright_error": ValidatedPattern(
906
- name="pyright_error",
907
- pattern=r"^(.+?): (\d+): (\d+) - error: (.+)$",
908
- replacement=r"File: \1, Line: \2, Col: \3, Error: \4",
909
- description="Parse pyright error output: file: line: col - error: message",
910
- test_cases=[
911
- (
912
- "src/app.py: 45: 12 - error: Undefined variable",
913
- "File: src/app.py, Line: 45, Col: 12, Error: Undefined variable",
914
- ),
915
- (
916
- "test.py: 1: 1 - error: Type mismatch",
917
- "File: test.py, Line: 1, Col: 1, Error: Type mismatch",
918
- ),
919
- (
920
- "./main.py: 999: 50 - error: Missing return statement",
921
- "File: ./main.py, Line: 999, Col: 50, Error: Missing return statement",
922
- ),
923
- ],
924
- ),
925
- "pyright_warning": ValidatedPattern(
926
- name="pyright_warning",
927
- pattern=r"^(.+?): (\d+): (\d+) - warning: (.+)$",
928
- replacement=r"File: \1, Line: \2, Col: \3, Warning: \4",
929
- description="Parse pyright warning output: file: line: col - warning: message",
930
- test_cases=[
931
- (
932
- "src/app.py: 45: 12 - warning: Unused variable",
933
- "File: src/app.py, Line: 45, Col: 12, Warning: Unused variable",
934
- ),
935
- (
936
- "test.py: 1: 1 - warning: Deprecated API",
937
- "File: test.py, Line: 1, Col: 1, Warning: Deprecated API",
938
- ),
939
- (
940
- "./main.py: 999: 50 - warning: Type could be more specific",
941
- "File: ./main.py, Line: 999, Col: 50, Warning: Type could be more"
942
- " specific",
943
- ),
944
- ],
945
- ),
946
- "pyright_summary": ValidatedPattern(
947
- name="pyright_summary",
948
- pattern=r"(\d+) error[s]?, (\d+) warning[s]?",
949
- replacement=r"\1 errors, \2 warnings",
950
- description="Parse pyright summary with error and warning counts",
951
- test_cases=[
952
- ("5 errors, 3 warnings", "5 errors, 3 warnings"),
953
- ("1 error, 1 warning", "1 errors, 1 warnings"),
954
- ("0 errors, 10 warnings found", "0 errors, 10 warnings found"),
955
- ],
956
- ),
957
- "bandit_issue": ValidatedPattern(
958
- name="bandit_issue",
959
- pattern=r">> Issue: \[([A-Z]\d+): \w+\] (.+)",
960
- replacement=r"Security Issue [\1]: \2",
961
- description="Parse bandit security issue output with code and message",
962
- test_cases=[
963
- (
964
- ">> Issue: [B602: subprocess_popen_with_shell_equals_true] Use of "
965
- "shell=True",
966
- "Security Issue [B602]: Use of shell=True",
967
- ),
968
- (
969
- ">> Issue: [B101: assert_used] Use of assert detected",
970
- "Security Issue [B101]: Use of assert detected",
971
- ),
972
- (
973
- ">> Issue: [B301: pickle] Pickle library detected",
974
- "Security Issue [B301]: Pickle library detected",
975
- ),
976
- ],
977
- ),
978
- "bandit_location": ValidatedPattern(
979
- name="bandit_location",
980
- pattern=r"Location: (.+?): (\d+): (\d+)",
981
- replacement=r"Location: File \1, Line \2, Column \3",
982
- description="Parse bandit location information for security issues",
983
- test_cases=[
984
- (
985
- "Location: src/security.py: 123: 45",
986
- "Location: File src/security.py, Line 123, Column 45",
987
- ),
988
- ("Location: ./test.py: 1: 1", "Location: File ./test.py, Line 1, Column 1"),
989
- (
990
- "Location: crackerjack/core.py: 999: 80",
991
- "Location: File crackerjack/core.py, Line 999, Column 80",
992
- ),
993
- ],
994
- ),
995
- "bandit_confidence": ValidatedPattern(
996
- name="bandit_confidence",
997
- pattern=r"Confidence: (\w+)",
998
- replacement=r"Confidence Level: \1",
999
- description="Parse bandit confidence level for security issues",
1000
- test_cases=[
1001
- ("Confidence: HIGH", "Confidence Level: HIGH"),
1002
- ("Confidence: MEDIUM", "Confidence Level: MEDIUM"),
1003
- ("Confidence: LOW", "Confidence Level: LOW"),
1004
- ],
1005
- ),
1006
- "bandit_severity": ValidatedPattern(
1007
- name="bandit_severity",
1008
- pattern=r"Severity: (\w+)",
1009
- replacement=r"Severity Level: \1",
1010
- description="Parse bandit severity level for security issues",
1011
- test_cases=[
1012
- ("Severity: HIGH", "Severity Level: HIGH"),
1013
- ("Severity: MEDIUM", "Severity Level: MEDIUM"),
1014
- ("Severity: LOW", "Severity Level: LOW"),
1015
- ],
1016
- ),
1017
- "mypy_error": ValidatedPattern(
1018
- name="mypy_error",
1019
- pattern=r"^(.+?): (\d+): error: (.+)$",
1020
- replacement=r"File: \1, Line: \2, Error: \3",
1021
- description="Parse mypy error output: file: line: error: message",
1022
- test_cases=[
1023
- (
1024
- "src/app.py: 45: error: Name 'undefined_var' is not defined",
1025
- "File: src/app.py, Line: 45, Error: Name 'undefined_var' is not "
1026
- "defined",
1027
- ),
1028
- (
1029
- "test.py: 1: error: Incompatible return value type",
1030
- "File: test.py, Line: 1, Error: Incompatible return value type",
1031
- ),
1032
- (
1033
- "./main.py: 999: error: Argument has incompatible type",
1034
- "File: ./main.py, Line: 999, Error: Argument has incompatible type",
1035
- ),
1036
- ],
1037
- ),
1038
- "mypy_note": ValidatedPattern(
1039
- name="mypy_note",
1040
- pattern=r"^(.+?): (\d+): note: (.+)$",
1041
- replacement=r"File: \1, Line: \2, Note: \3",
1042
- description="Parse mypy note output: file: line: note: message",
1043
- test_cases=[
1044
- (
1045
- "src/app.py: 45: note: Expected type Union[int, str]",
1046
- "File: src/app.py, Line: 45, Note: Expected type Union[int, str]",
1047
- ),
1048
- (
1049
- "test.py: 1: note: See https: //mypy.readthedocs.io/",
1050
- "File: test.py, Line: 1, Note: See https: //mypy.readthedocs.io/",
1051
- ),
1052
- (
1053
- "./main.py: 999: note: Consider using Optional[...]",
1054
- "File: ./main.py, Line: 999, Note: Consider using Optional[...]",
1055
- ),
1056
- ],
1057
- ),
1058
- "vulture_unused": ValidatedPattern(
1059
- name="vulture_unused",
1060
- pattern=r"^(.+?): (\d+): unused (.+) '(.+)'",
1061
- replacement=r"File: \1, Line: \2, Unused \3: '\4'",
1062
- description="Parse vulture unused code detection: file: line: unused type"
1063
- " 'name'",
1064
- test_cases=[
1065
- (
1066
- "src/app.py: 45: unused variable 'temp_var'",
1067
- "File: src/app.py, Line: 45, Unused variable: 'temp_var'",
1068
- ),
1069
- (
1070
- "test.py: 1: unused function 'helper'",
1071
- "File: test.py, Line: 1, Unused function: 'helper'",
1072
- ),
1073
- (
1074
- "./main.py: 999: unused import 'os'",
1075
- "File: ./main.py, Line: 999, Unused import: 'os'",
1076
- ),
1077
- ],
1078
- ),
1079
- "complexipy_complex": ValidatedPattern(
1080
- name="complexipy_complex",
1081
- pattern=r"^(.+?): (\d+): (\d+) - (.+) is too complex \((\d+)\)",
1082
- replacement=r"File: \1, Line: \2, Col: \3, Function: \4, Complexity: \5",
1083
- description="Parse complexipy complexity detection: file: line: col - function "
1084
- "is too complex (score)",
1085
- test_cases=[
1086
- (
1087
- "src/app.py: 45: 1 - complex_function is too complex (15)",
1088
- "File: src/app.py, Line: 45, Col: 1, Function: complex_function, "
1089
- "Complexity: 15",
1090
- ),
1091
- (
1092
- "test.py: 1: 1 - nested_loops is too complex (20)",
1093
- "File: test.py, Line: 1, Col: 1, Function: nested_loops, "
1094
- "Complexity: 20",
1095
- ),
1096
- (
1097
- "./main.py: 999: 5 - process_data is too complex (18)",
1098
- "File: ./main.py, Line: 999, Col: 5, Function: process_data, "
1099
- "Complexity: 18",
1100
- ),
1101
- ],
1102
- ),
1103
- "pytest_test_start": ValidatedPattern(
1104
- name="pytest_test_start",
1105
- pattern=r"^(.+?):: ?(.+?):: ?(.+?) (PASSED|FAILED|SKIPPED|ERROR)$",
1106
- replacement=r"\1::\2::\3",
1107
- description="Parse pytest test start line with file, class, and method "
1108
- "(3-part format)",
1109
- test_cases=[
1110
- (
1111
- "test_file.py::TestClass::test_method PASSED",
1112
- "test_file.py::TestClass::test_method",
1113
- ),
1114
- (
1115
- "tests/test_core.py::TestCore::test_function FAILED",
1116
- "tests/test_core.py::TestCore::test_function",
1117
- ),
1118
- (
1119
- "src/test.py::MyTest::test_case SKIPPED",
1120
- "src/test.py::MyTest::test_case",
1121
- ),
1122
- ],
1123
- ),
1124
- "pytest_test_result": ValidatedPattern(
1125
- name="pytest_test_result",
1126
- pattern=r"^(.+?) (PASSED|FAILED|SKIPPED|ERROR)(?: \[.*?\])?\s*$",
1127
- replacement=r"\1",
1128
- description="Parse pytest test result line with test identifier",
1129
- test_cases=[
1130
- ("test_file.py::test_method PASSED", "test_file.py::test_method"),
1131
- (
1132
- "tests/test_core.py::test_func FAILED [100%]",
1133
- "tests/test_core.py::test_func",
1134
- ),
1135
- ("src/test.py::test_case SKIPPED ", "src/test.py::test_case"),
1136
- ],
1137
- ),
1138
- "pytest_collection_count": ValidatedPattern(
1139
- name="pytest_collection_count",
1140
- pattern=r"collected (\d+) items?",
1141
- replacement=r"\1",
1142
- description="Parse pytest test collection count",
1143
- test_cases=[
1144
- ("collected 5 items", "5"),
1145
- ("collected 1 item", "1"),
1146
- (
1147
- "collected 42 items for execution",
1148
- "42 for execution",
1149
- ),
1150
- ],
1151
- ),
1152
- "pytest_session_start": ValidatedPattern(
1153
- name="pytest_session_start",
1154
- pattern=r"test session starts",
1155
- replacement=r"test session starts",
1156
- description="Match pytest session start indicator",
1157
- test_cases=[
1158
- ("test session starts", "test session starts"),
1159
- ("pytest test session starts", "pytest test session starts"),
1160
- ],
1161
- ),
1162
- "pytest_coverage_total": ValidatedPattern(
1163
- name="pytest_coverage_total",
1164
- pattern=r"TOTAL\s+\d+\s+\d+\s+(\d+)%",
1165
- replacement=r"\1",
1166
- description="Parse pytest coverage total percentage",
1167
- test_cases=[
1168
- ("TOTAL 123 45 85%", "85"),
1169
- ("TOTAL 1000 250 75%", "75"),
1170
- ("TOTAL 50 0 100%", "100"),
1171
- ],
1172
- ),
1173
- "pytest_detailed_test": ValidatedPattern(
1174
- name="pytest_detailed_test",
1175
- pattern=r"^(.+\.py)::(.+) (PASSED|FAILED|SKIPPED|ERROR)",
1176
- replacement=r"\1::\2",
1177
- description="Parse detailed pytest test output with file, test name, and "
1178
- "status",
1179
- test_cases=[
1180
- (
1181
- "test_file.py::test_method PASSED [50%]",
1182
- "test_file.py::test_method [50%]",
1183
- ),
1184
- (
1185
- "tests/core.py::TestClass::test_func FAILED [75%] [0.1s]",
1186
- "tests/core.py::TestClass::test_func [75%] [0.1s]",
1187
- ),
1188
- (
1189
- "src/test.py::test_case SKIPPED",
1190
- "src/test.py::test_case",
1191
- ),
1192
- ],
1193
- ),
1194
- "docstring_triple_double": ValidatedPattern(
1195
- name="docstring_triple_double",
1196
- pattern=r'^\s*""".*?"""\s*$',
1197
- replacement=r"",
1198
- flags=re.MULTILINE | re.DOTALL,
1199
- description="Remove triple-quoted docstrings with double quotes",
1200
- test_cases=[
1201
- (' """This is a docstring""" ', ""),
1202
- ('"""Module docstring"""', ""),
1203
- (' """\n Multi-line\n docstring\n """', ""),
1204
- (
1205
- 'regular_code = "not a docstring"',
1206
- 'regular_code = "not a docstring"',
1207
- ),
1208
- ],
1209
- ),
1210
- "docstring_triple_single": ValidatedPattern(
1211
- name="docstring_triple_single",
1212
- pattern=r"^\s*'''.*?'''\s*$",
1213
- replacement=r"",
1214
- flags=re.MULTILINE | re.DOTALL,
1215
- description="Remove triple-quoted docstrings with single quotes",
1216
- test_cases=[
1217
- (" '''This is a docstring''' ", ""),
1218
- ("'''Module docstring'''", ""),
1219
- (" '''\n Multi-line\n docstring\n '''", ""),
1220
- (
1221
- "regular_code = 'not a docstring'",
1222
- "regular_code = 'not a docstring'",
1223
- ),
1224
- ],
1225
- ),
1226
- "spacing_after_comma": ValidatedPattern(
1227
- name="spacing_after_comma",
1228
- pattern=r", ([^ \n])",
1229
- replacement=r", \1",
1230
- global_replace=True,
1231
- description="Add space after comma if missing",
1232
- test_cases=[
1233
- ("def func(a, b, c): ", "def func(a, b, c): "),
1234
- ("items = [1, 2, 3, 4]", "items = [1, 2, 3, 4]"),
1235
- ("already, spaced, properly", "already, spaced, properly"),
1236
- ("mixed, spacing, here", "mixed, spacing, here"),
1237
- ],
1238
- ),
1239
- "spacing_after_colon": ValidatedPattern(
1240
- name="spacing_after_colon",
1241
- pattern=r"(?<!https)(?<!http)(?<!ftp)(?<!file)(?<!: )(\b[a-zA-Z_][a-zA-Z0-9_]*):([a-zA-Z0-9_][^ \n:]*)",
1242
- replacement=r"\1: \2",
1243
- global_replace=True,
1244
- description="Add space after colon if missing (avoid double colons, URLs, and protocols)",
1245
- test_cases=[
1246
- ("def func(x: int, y: str): ", "def func(x: int, y: str): "),
1247
- ("dict_item = {'key': 'value'}", "dict_item = {'key': 'value'}"),
1248
- ("already: spaced: properly", "already: spaced: properly"),
1249
- ("class::method", "class::method"),
1250
- ("https://github.com", "https://github.com"),
1251
- ("http://example.com", "http://example.com"),
1252
- ("ftp://server.com", "ftp://server.com"),
1253
- ("repo:local", "repo: local"),
1254
- ],
1255
- ),
1256
- "multiple_spaces": ValidatedPattern(
1257
- name="multiple_spaces",
1258
- pattern=r" {2,}",
1259
- replacement=r" ",
1260
- description="Replace multiple spaces with single space",
1261
- global_replace=True,
1262
- test_cases=[
1263
- ("def func( x, y ): ", "def func( x, y ): "),
1264
- ("single space only", "single space only"),
1265
- ("lots of spaces", "lots of spaces"),
1266
- ("\tkeep\ttabs\tbut fix spaces", "\tkeep\ttabs\tbut fix spaces"),
1267
- ],
1268
- ),
1269
- "preserved_comments": ValidatedPattern(
1270
- name="preserved_comments",
1271
- pattern=r"(#.*?(?: coding: | encoding: | type: | noqa | pragma).*)",
1272
- replacement=r"\1",
1273
- description="Match preserved code comments (encoding, type hints, etc.)",
1274
- test_cases=[
1275
- ("# coding: utf-8", "# coding: utf-8"),
1276
- (
1277
- "# encoding: latin-1",
1278
- "# encoding: latin-1",
1279
- ),
1280
- ("# type: ignore", "# type: ignore"),
1281
- ("# noqa: E501", "# noqa: E501"),
1282
- (
1283
- "# pragma: no cover",
1284
- "# pragma: no cover",
1285
- ),
1286
- ("# regular comment", "# regular comment"),
1287
- ],
1288
- ),
1289
- "todo_pattern": ValidatedPattern(
1290
- name="todo_pattern",
1291
- pattern=r"(#.*?TODO.*)",
1292
- replacement=r"\1",
1293
- flags=re.IGNORECASE,
1294
- description="Match TODO comments for validation",
1295
- test_cases=[
1296
- (
1297
- "# TODO: Fix this bug",
1298
- "# TODO: Fix this bug",
1299
- ),
1300
- (
1301
- "# todo: implement later",
1302
- "# todo: implement later",
1303
- ),
1304
- (
1305
- "# TODO refactor this method",
1306
- "# TODO refactor this method",
1307
- ),
1308
- (
1309
- "# FIXME: another issue",
1310
- "# FIXME: another issue",
1311
- ),
1312
- ("# regular comment", "# regular comment"),
1313
- ],
1314
- ),
1315
- "detect_error_response_patterns": ValidatedPattern(
1316
- name="detect_error_response_patterns",
1317
- pattern=r'return\s+.*[\'\"]\{.*[\'\""]error[\'\""].*\}.*[\'\""]',
1318
- replacement=r"MATCH",
1319
- description="Detect error response patterns in Python code for DRY violations",
1320
- test_cases=[
1321
- ('return \'{"error": "msg"}\'', "MATCH"),
1322
- ('return f\'{"error": "msg"}\'', "MATCH"),
1323
- ('return {"success": True}', 'return {"success": True}'),
1324
- ('return \'{"error": "test message", "code": 500}\'', "MATCH"),
1325
- ],
1326
- ),
1327
- "detect_path_conversion_patterns": ValidatedPattern(
1328
- name="detect_path_conversion_patterns",
1329
- pattern=r"Path\([^)]+\)\s+if\s+isinstance\([^)]+, \s*str\)\s+else\s+[^)]+",
1330
- replacement=r"MATCH",
1331
- description="Detect path conversion patterns in Python code for DRY violations",
1332
- test_cases=[
1333
- ("Path(value) if isinstance(value, str) else value", "MATCH"),
1334
- ("Path(path) if isinstance(path, str) else path", "MATCH"),
1335
- ("Path('/tmp/file')", "Path('/tmp/file')"),
1336
- (
1337
- "Path(input_path) if isinstance(input_path, str) else input_path",
1338
- "MATCH",
1339
- ),
1340
- ],
1341
- ),
1342
- "detect_file_existence_patterns": ValidatedPattern(
1343
- name="detect_file_existence_patterns",
1344
- pattern=r"if\s+not\s+\w+\.exists\(\): ",
1345
- replacement=r"MATCH",
1346
- description="Detect file existence check patterns in Python code for DRY"
1347
- " violations",
1348
- test_cases=[
1349
- ("if not file.exists(): ", "MATCH"),
1350
- ("if not path.exists(): ", "MATCH"),
1351
- ("if not file_path.exists(): ", "MATCH"),
1352
- ("if file.exists(): ", "if file.exists(): "),
1353
- ],
1354
- ),
1355
- "detect_exception_patterns": ValidatedPattern(
1356
- name="detect_exception_patterns",
1357
- pattern=r"except\s+\w*Exception\s+as\s+\w+: ",
1358
- replacement=r"MATCH",
1359
- description="Detect exception handling patterns for base Exception class in Python code for DRY violations",
1360
- test_cases=[
1361
- ("except Exception as e: ", "MATCH"),
1362
- ("except BaseException as error: ", "MATCH"),
1363
- (
1364
- "except ValueError as error: ",
1365
- "except ValueError as error: ",
1366
- ),
1367
- ("try: ", "try: "),
1368
- ],
1369
- ),
1370
- "fix_path_conversion_with_ensure_path": ValidatedPattern(
1371
- name="fix_path_conversion_with_ensure_path",
1372
- pattern=r"Path\([^)]+\)\s+if\s+isinstance\([^)]+, \s*str\)\s+else\s+([^)]+)",
1373
- replacement=r"_ensure_path(\1)",
1374
- description="Replace path conversion patterns with _ensure_path utility "
1375
- "function",
1376
- test_cases=[
1377
- ("Path(value) if isinstance(value, str) else value", "_ensure_path(value)"),
1378
- ("Path(path) if isinstance(path, str) else path", "_ensure_path(path)"),
1379
- (
1380
- "Path(input_path) if isinstance(input_path, str) else input_path",
1381
- "_ensure_path(input_path)",
1382
- ),
1383
- ],
1384
- ),
1385
- "fix_path_conversion_simple": ValidatedPattern(
1386
- name="fix_path_conversion_simple",
1387
- pattern=r"Path\(([^)]+)\)\s+if\s+isinstance\(\1, \s*str\)\s+else\s+\1",
1388
- replacement=r"_ensure_path(\1)",
1389
- description="Replace simple path conversion patterns with _ensure_path utility "
1390
- "function",
1391
- test_cases=[
1392
- ("Path(value) if isinstance(value, str) else value", "_ensure_path(value)"),
1393
- ("Path(path) if isinstance(path, str) else path", "_ensure_path(path)"),
1394
- (
1395
- "Path(file_path) if isinstance(file_path, str) else file_path",
1396
- "_ensure_path(file_path)",
1397
- ),
1398
- ],
1399
- ),
1400
- "detect_security_keywords": ValidatedPattern(
1401
- name="detect_security_keywords",
1402
- pattern=r"(?i)(bandit|security|vulnerability|hardcoded|"
1403
- r"shell=true|b108|b602|b301|b506|unsafe|injection)",
1404
- replacement=r"MATCH",
1405
- description="Detect security-related keywords in issue messages "
1406
- "(case insensitive)",
1407
- flags=re.IGNORECASE,
1408
- test_cases=[
1409
- ("Bandit security issue found", "MATCH security issue found"),
1410
- ("VULNERABILITY detected", "MATCH detected"),
1411
- ("hardcoded path found", "MATCH path found"),
1412
- ("shell=True usage", "MATCH usage"),
1413
- ("B108 violation", "MATCH violation"),
1414
- ("normal message", "normal message"),
1415
- ],
1416
- ),
1417
- "detect_hardcoded_temp_paths_basic": ValidatedPattern(
1418
- name="detect_hardcoded_temp_paths_basic",
1419
- pattern=r"(?:/tmp/|/temp/|C:\\temp\\|C:\\tmp\\)", # nosec B108
1420
- replacement="[TEMP_PATH]/",
1421
- description="Detect hardcoded temporary directory paths",
1422
- global_replace=True,
1423
- test_cases=[
1424
- ("/tmp/myfile.txt", "[TEMP_PATH]/myfile.txt"), # nosec B108
1425
- (r"C:\tmp\data.log", "[TEMP_PATH]/data.log"),
1426
- ("/temp/cache", "[TEMP_PATH]/cache"),
1427
- (r"C:\temp\work", "[TEMP_PATH]/work"),
1428
- ("/regular/path", "/regular/path"),
1429
- ],
1430
- ),
1431
- "replace_hardcoded_temp_paths": ValidatedPattern(
1432
- name="replace_hardcoded_temp_paths",
1433
- pattern=r'Path\("/tmp/([^"]+)"\)',
1434
- replacement=r'Path(tempfile.gettempdir()) / "\1"',
1435
- description="Replace hardcoded /tmp paths with tempfile.gettempdir()",
1436
- global_replace=True,
1437
- test_cases=[
1438
- ('Path("/tmp/myfile.txt")', 'Path(tempfile.gettempdir()) / "myfile.txt"'),
1439
- ('Path("/tmp/data.log")', 'Path(tempfile.gettempdir()) / "data.log"'),
1440
- ('Path("/regular/path")', 'Path("/regular/path")'),
1441
- ],
1442
- ),
1443
- "replace_hardcoded_temp_strings": ValidatedPattern(
1444
- name="replace_hardcoded_temp_strings",
1445
- pattern=r'"/tmp/([^"]+)"',
1446
- replacement=r'str(Path(tempfile.gettempdir()) / "\1")',
1447
- description="Replace hardcoded /tmp string paths with tempfile equivalent",
1448
- global_replace=True,
1449
- test_cases=[
1450
- ('"/tmp/myfile.txt"', 'str(Path(tempfile.gettempdir()) / "myfile.txt")'),
1451
- ('"/tmp/data.log"', 'str(Path(tempfile.gettempdir()) / "data.log")'),
1452
- ('"/regular/path"', '"/regular/path"'),
1453
- ],
1454
- ),
1455
- "replace_hardcoded_temp_single_quotes": ValidatedPattern(
1456
- name="replace_hardcoded_temp_single_quotes",
1457
- pattern=r"'/tmp/([^']+)'",
1458
- replacement=r"str(Path(tempfile.gettempdir()) / '\1')",
1459
- description="Replace hardcoded /tmp paths (single quotes) with tempfile"
1460
- " equivalent",
1461
- global_replace=True,
1462
- test_cases=[
1463
- ("'/tmp/myfile.txt'", "str(Path(tempfile.gettempdir()) / 'myfile.txt')"),
1464
- ("'/tmp/data.log'", "str(Path(tempfile.gettempdir()) / 'data.log')"),
1465
- ("'/regular/path'", "'/regular/path'"),
1466
- ],
1467
- ),
1468
- "replace_test_path_patterns": ValidatedPattern(
1469
- name="replace_test_path_patterns",
1470
- pattern=r'Path\("/test/path"\)',
1471
- replacement=r"Path(tempfile.gettempdir()) / 'test-path'",
1472
- description="Replace hardcoded /test/path patterns with tempfile equivalent",
1473
- test_cases=[
1474
- ('Path("/test/path")', "Path(tempfile.gettempdir()) / 'test-path'"),
1475
- ('Path("/other/path")', 'Path("/other/path")'),
1476
- ],
1477
- ),
1478
- "detect_hardcoded_secrets": ValidatedPattern(
1479
- name="detect_hardcoded_secrets",
1480
- pattern=r'\b\w*(password|secret|key|token)\w*\s*=\s*[\'"][^\'"]+[\'"]',
1481
- replacement="[SECRET_DETECTED]",
1482
- description="Detect hardcoded secrets in assignments (case insensitive)",
1483
- flags=re.IGNORECASE,
1484
- global_replace=True,
1485
- test_cases=[
1486
- ('password = "secret123"', "[SECRET_DETECTED]"),
1487
- ("api_key = 'abc123def'", "[SECRET_DETECTED]"),
1488
- ('TOKEN = "my-token-here"', "[SECRET_DETECTED]"),
1489
- ("username = 'user123'", "username = 'user123'"),
1490
- ],
1491
- ),
1492
- "extract_variable_name_from_assignment": ValidatedPattern(
1493
- name="extract_variable_name_from_assignment",
1494
- pattern=r"\s*(\w+)\s*=.*",
1495
- replacement=r"\1",
1496
- description="Extract variable name from assignment statement",
1497
- test_cases=[
1498
- ("password = 'secret'", "password"),
1499
- ("api_key = 'value'", "api_key"),
1500
- (" token =", "token"),
1501
- ("complex_variable_name = value", "complex_variable_name"),
1502
- ],
1503
- ),
1504
- "detect_insecure_random_usage": ValidatedPattern(
1505
- name="detect_insecure_random_usage",
1506
- pattern=r"\brandom\.(?:random|choice)\([^)]*\)",
1507
- replacement="[INSECURE_RANDOM]()",
1508
- description="Detect insecure random module usage",
1509
- global_replace=True,
1510
- test_cases=[
1511
- ("random.random()", "[INSECURE_RANDOM]()"),
1512
- ("random.choice(options)", "[INSECURE_RANDOM]()"),
1513
- ("secrets.choice(options)", "secrets.choice(options)"),
1514
- ("my_random.choice()", "my_random.choice()"),
1515
- ],
1516
- ),
1517
- "validate_sql_injection_patterns": ValidatedPattern(
1518
- name="validate_sql_injection_patterns",
1519
- pattern=r"\b(union|select|insert|update|delete|drop|create|alter|"
1520
- r"exec|execute)\b",
1521
- replacement="[SQL_INJECTION]",
1522
- flags=re.IGNORECASE,
1523
- description="Detect SQL injection patterns in input validation "
1524
- "(case insensitive)",
1525
- global_replace=True,
1526
- test_cases=[
1527
- ("UNION SELECT", "[SQL_INJECTION] [SQL_INJECTION]"),
1528
- ("drop table", "[SQL_INJECTION] table"),
1529
- ("normal text", "normal text"),
1530
- ("exec command", "[SQL_INJECTION] command"),
1531
- ("execute procedure", "[SQL_INJECTION] procedure"),
1532
- ],
1533
- ),
1534
- "validate_sql_comment_patterns": ValidatedPattern(
1535
- name="validate_sql_comment_patterns",
1536
- pattern=r"(-{2,}|\/\*|\*\/)",
1537
- replacement="[SQL_COMMENT]",
1538
- description="Detect SQL comment patterns in input validation",
1539
- global_replace=True,
1540
- test_cases=[
1541
- ("--comment", "[SQL_COMMENT]comment"),
1542
- ("/* comment */", "[SQL_COMMENT] comment [SQL_COMMENT]"),
1543
- ("normal-text", "normal-text"),
1544
- ("---triple", "[SQL_COMMENT]triple"),
1545
- ],
1546
- ),
1547
- "validate_sql_boolean_injection": ValidatedPattern(
1548
- name="validate_sql_boolean_injection",
1549
- pattern=r"\b(or|and)\b.*=",
1550
- replacement="[BOOLEAN_INJECTION]",
1551
- flags=re.IGNORECASE,
1552
- description="Detect boolean-based SQL injection patterns (case insensitive)",
1553
- global_replace=True,
1554
- test_cases=[
1555
- ("or 1=1", "[BOOLEAN_INJECTION]1"),
1556
- ("AND password=", "[BOOLEAN_INJECTION]"),
1557
- ("normal or text", "normal or text"),
1558
- ("value=test", "value=test"),
1559
- ],
1560
- ),
1561
- "validate_sql_server_specific": ValidatedPattern(
1562
- name="validate_sql_server_specific",
1563
- pattern=r"\b(xp_cmdshell|sp_executesql)\b",
1564
- replacement="[SQLSERVER_EXPLOIT]",
1565
- flags=re.IGNORECASE,
1566
- description="Detect SQL Server specific attack patterns (case insensitive)",
1567
- global_replace=True,
1568
- test_cases=[
1569
- ("xp_cmdshell", "[SQLSERVER_EXPLOIT]"),
1570
- ("SP_EXECUTESQL", "[SQLSERVER_EXPLOIT]"),
1571
- ("normal text", "normal text"),
1572
- ],
1573
- ),
1574
- "validate_code_eval_injection": ValidatedPattern(
1575
- name="validate_code_eval_injection",
1576
- pattern=r"\b(eval|exec|execfile)\s*\(",
1577
- replacement="[CODE_EVAL](",
1578
- description="Detect Python code evaluation injection patterns",
1579
- global_replace=True,
1580
- test_cases=[
1581
- ("eval(code)", "[CODE_EVAL](code)"),
1582
- ("exec(command)", "[CODE_EVAL](command)"),
1583
- ("execfile(script)", "[CODE_EVAL](script)"),
1584
- ("evaluate()", "evaluate()"),
1585
- ],
1586
- ),
1587
- "validate_code_dynamic_access": ValidatedPattern(
1588
- name="validate_code_dynamic_access",
1589
- pattern=r"\b(__import__|getattr|setattr|delattr)\b",
1590
- replacement="[DYNAMIC_ACCESS]",
1591
- description="Detect dynamic attribute access patterns for code injection",
1592
- global_replace=True,
1593
- test_cases=[
1594
- ("__import__", "[DYNAMIC_ACCESS]"),
1595
- ("getattr(obj, name)", "[DYNAMIC_ACCESS](obj, name)"),
1596
- ("setattr(obj, name)", "[DYNAMIC_ACCESS](obj, name)"),
1597
- ("delattr(obj, name)", "[DYNAMIC_ACCESS](obj, name)"),
1598
- ("mygetattr", "mygetattr"),
1599
- ],
1600
- ),
1601
- "validate_code_system_commands": ValidatedPattern(
1602
- name="validate_code_system_commands",
1603
- pattern=r"\b(subprocess|os\.system|os\.popen|commands\.)",
1604
- replacement="[SYSTEM_COMMAND]",
1605
- description="Detect system command execution patterns for code injection",
1606
- global_replace=True,
1607
- test_cases=[
1608
- ("subprocess.run", "[SYSTEM_COMMAND].run"),
1609
- ("os.system(cmd)", "[SYSTEM_COMMAND](cmd)"),
1610
- ("os.popen(cmd)", "[SYSTEM_COMMAND](cmd)"),
1611
- ("commands.getoutput", "[SYSTEM_COMMAND]getoutput"),
1612
- ("mysubprocess", "mysubprocess"),
1613
- ],
1614
- ),
1615
- "validate_code_compilation": ValidatedPattern(
1616
- name="validate_code_compilation",
1617
- pattern=r"\bcompile\s*\(|code\.compile",
1618
- replacement="[CODE_COMPILE]",
1619
- description="Detect code compilation patterns for injection",
1620
- global_replace=True,
1621
- test_cases=[
1622
- ("compile(source)", "[CODE_COMPILE]source)"),
1623
- ("code.compile(source)", "[CODE_COMPILE](source)"),
1624
- ("compiled", "compiled"),
1625
- ],
1626
- ),
1627
- "validate_job_id_format": ValidatedPattern(
1628
- name="validate_job_id_format",
1629
- pattern=r"^[a-zA-Z0-9\-_]+$",
1630
- replacement="VALID_JOB_ID",
1631
- description="Validate job ID format - alphanumeric with hyphens and"
1632
- " underscores only",
1633
- test_cases=[
1634
- ("valid_job-123", "VALID_JOB_ID"),
1635
- ("another-valid_job_456", "VALID_JOB_ID"),
1636
- ("job_123", "VALID_JOB_ID"),
1637
- ("UPPERCASE_JOB-ID", "VALID_JOB_ID"),
1638
- ("hyphen-underscore_combo", "VALID_JOB_ID"),
1639
- ],
1640
- ),
1641
- "validate_env_var_name_format": ValidatedPattern(
1642
- name="validate_env_var_name_format",
1643
- pattern=r"^[A-Z_][A-Z0-9_]*$",
1644
- replacement="VALID_ENV_VAR_NAME",
1645
- description="Validate environment variable name format - uppercase letters, "
1646
- " numbers, underscores only, must start with letter or underscore",
1647
- test_cases=[
1648
- ("VALID_VAR", "VALID_ENV_VAR_NAME"),
1649
- ("_VALID_VAR", "VALID_ENV_VAR_NAME"),
1650
- ("API_KEY_123", "VALID_ENV_VAR_NAME"),
1651
- ("DATABASE_URL", "VALID_ENV_VAR_NAME"),
1652
- ("_PRIVATE_VAR", "VALID_ENV_VAR_NAME"),
1653
- ],
1654
- ),
1655
- "update_repo_revision": ValidatedPattern(
1656
- name="update_repo_revision",
1657
- pattern=r'("repo": "[^"]+?".*?"rev": )"([^"]+)"',
1658
- replacement=r'\1"NEW_REVISION"',
1659
- description="Update repository revision in config files (NEW_REVISION"
1660
- " placeholder replaced dynamically)",
1661
- flags=re.DOTALL,
1662
- test_cases=[
1663
- (
1664
- '"repo": "https: //github.com/user/repo".*"rev": "old_rev"',
1665
- '"repo": "https: //github.com/user/repo".*"rev": "NEW_REVISION"',
1666
- ),
1667
- (
1668
- '"repo": "git@github.com: user/repo.git", "branch": "main", "rev": '
1669
- '"abc123"',
1670
- '"repo": "git@github.com: user/repo.git", "branch": "main", "rev": '
1671
- '"NEW_REVISION"',
1672
- ),
1673
- (
1674
- '{"repo": "https: //example.com/repo", "description": "test", "rev": '
1675
- '"456def"}',
1676
- '{"repo": "https: //example.com/repo", "description": "test", "rev": '
1677
- '"NEW_REVISION"}',
1678
- ),
1679
- ],
1680
- ),
1681
- "sanitize_localhost_urls": ValidatedPattern(
1682
- name="sanitize_localhost_urls",
1683
- pattern=r"https?: //localhost: \d+[^\s]*",
1684
- replacement="[INTERNAL_URL]",
1685
- description="Sanitize localhost URLs with ports for security",
1686
- global_replace=True,
1687
- test_cases=[
1688
- ("http: //localhost: 8000/api/test", "[INTERNAL_URL]"),
1689
- ("https: //localhost: 3000/dashboard", "[INTERNAL_URL]"),
1690
- (
1691
- "Visit http: //localhost: 8080/admin for details",
1692
- "Visit [INTERNAL_URL] for details",
1693
- ),
1694
- ("https: //example.com/test", "https: //example.com/test"),
1695
- ],
1696
- ),
1697
- "sanitize_127_urls": ValidatedPattern(
1698
- name="sanitize_127_urls",
1699
- pattern=r"https?: //127\.0\.0\.1: \d+[^\s]*",
1700
- replacement="[INTERNAL_URL]",
1701
- description="Sanitize 127.0.0.1 URLs with ports for security",
1702
- global_replace=True,
1703
- test_cases=[
1704
- ("http: //127.0.0.1: 8000/api", "[INTERNAL_URL]"),
1705
- ("https: //127.0.0.1: 3000/test", "[INTERNAL_URL]"),
1706
- ("Connect to http: //127.0.0.1: 5000/status", "Connect to [INTERNAL_URL]"),
1707
- (
1708
- "https: //192.168.1.1: 8080/test",
1709
- "https: //192.168.1.1: 8080/test",
1710
- ),
1711
- ],
1712
- ),
1713
- "sanitize_any_localhost_urls": ValidatedPattern(
1714
- name="sanitize_any_localhost_urls",
1715
- pattern=r"https?: //0\.0\.0\.0: \d+[^\s]*",
1716
- replacement="[INTERNAL_URL]",
1717
- description="Sanitize 0.0.0.0 URLs with ports for security",
1718
- global_replace=True,
1719
- test_cases=[
1720
- ("http: //0.0.0.0: 8000/api", "[INTERNAL_URL]"),
1721
- ("https: //0.0.0.0: 3000/test", "[INTERNAL_URL]"),
1722
- ("https: //1.1.1.1: 8080/test", "https: //1.1.1.1: 8080/test"),
1723
- ],
1724
- ),
1725
- "sanitize_ws_localhost_urls": ValidatedPattern(
1726
- name="sanitize_ws_localhost_urls",
1727
- pattern=r"ws: //localhost: \d+[^\s]*",
1728
- replacement="[INTERNAL_URL]",
1729
- description="Sanitize WebSocket localhost URLs with ports for security",
1730
- global_replace=True,
1731
- test_cases=[
1732
- ("ws: //localhost: 8675/websocket", "[INTERNAL_URL]"),
1733
- ("ws: //localhost: 3000/socket", "[INTERNAL_URL]"),
1734
- ("Connect to ws: //localhost: 8000/ws", "Connect to [INTERNAL_URL]"),
1735
- (
1736
- "wss: //example.com: 443/socket",
1737
- "wss: //example.com: 443/socket",
1738
- ),
1739
- ],
1740
- ),
1741
- "sanitize_ws_127_urls": ValidatedPattern(
1742
- name="sanitize_ws_127_urls",
1743
- pattern=r"ws: //127\.0\.0\.1: \d+[^\s]*",
1744
- replacement="[INTERNAL_URL]",
1745
- description="Sanitize WebSocket 127.0.0.1 URLs with ports for security",
1746
- global_replace=True,
1747
- test_cases=[
1748
- ("ws: //127.0.0.1: 8675/websocket", "[INTERNAL_URL]"),
1749
- ("ws: //127.0.0.1: 3000/socket", "[INTERNAL_URL]"),
1750
- (
1751
- "ws: //192.168.1.1: 8080/socket",
1752
- "ws: //192.168.1.1: 8080/socket",
1753
- ),
1754
- ],
1755
- ),
1756
- "sanitize_simple_localhost_urls": ValidatedPattern(
1757
- name="sanitize_simple_localhost_urls",
1758
- pattern=r"http: //localhost[^\s]*",
1759
- replacement="[INTERNAL_URL]",
1760
- description="Sanitize simple localhost URLs without explicit ports for security",
1761
- global_replace=True,
1762
- test_cases=[
1763
- ("http: //localhost/api/test", "[INTERNAL_URL]"),
1764
- ("http: //localhost/dashboard", "[INTERNAL_URL]"),
1765
- ("Visit http: //localhost/admin", "Visit [INTERNAL_URL]"),
1766
- (
1767
- "https: //localhost: 443/test",
1768
- "https: //localhost: 443/test",
1769
- ),
1770
- ],
1771
- ),
1772
- "sanitize_simple_ws_localhost_urls": ValidatedPattern(
1773
- name="sanitize_simple_ws_localhost_urls",
1774
- pattern=r"ws: //localhost[^\s]*",
1775
- replacement="[INTERNAL_URL]",
1776
- description="Sanitize simple WebSocket localhost URLs without explicit ports"
1777
- " for security",
1778
- global_replace=True,
1779
- test_cases=[
1780
- ("ws: //localhost/websocket", "[INTERNAL_URL]"),
1781
- ("ws: //localhost/socket", "[INTERNAL_URL]"),
1782
- ("Connect to ws: //localhost/ws", "Connect to [INTERNAL_URL]"),
1783
- (
1784
- "wss: //localhost: 443/socket",
1785
- "wss: //localhost: 443/socket",
1786
- ),
1787
- ],
1788
- ),
1789
- "detect_tempfile_usage": ValidatedPattern(
1790
- name="detect_tempfile_usage",
1791
- pattern=r"tempfile\.(mkdtemp|NamedTemporaryFile|TemporaryDirectory)",
1792
- replacement="MATCH",
1793
- test_cases=[
1794
- ("tempfile.mkdtemp()", "MATCH()"),
1795
- ("tempfile.NamedTemporaryFile()", "MATCH()"),
1796
- ("tempfile.TemporaryDirectory()", "MATCH()"),
1797
- (
1798
- "not_tempfile.other()",
1799
- "not_tempfile.other()",
1800
- ),
1801
- ],
1802
- description="Detect tempfile module usage for resource management integration",
1803
- ),
1804
- "detect_subprocess_usage": ValidatedPattern(
1805
- name="detect_subprocess_usage",
1806
- pattern=r"subprocess\.(Popen|run)",
1807
- replacement="MATCH",
1808
- test_cases=[
1809
- ("subprocess.Popen(cmd)", "MATCH(cmd)"),
1810
- ("subprocess.run(['cmd'])", "MATCH(['cmd'])"),
1811
- ("not_subprocess.other()", "not_subprocess.other()"),
1812
- ],
1813
- description="Detect subprocess module usage for resource management integration",
1814
- ),
1815
- "detect_asyncio_create_task": ValidatedPattern(
1816
- name="detect_asyncio_create_task",
1817
- pattern=r"asyncio\.create_task",
1818
- replacement="MATCH",
1819
- test_cases=[
1820
- ("asyncio.create_task(coro)", "MATCH(coro)"),
1821
- ("not_asyncio.other()", "not_asyncio.other()"),
1822
- ],
1823
- description="Detect asyncio.create_task usage for resource management"
1824
- " integration",
1825
- ),
1826
- "detect_file_open_operations": ValidatedPattern(
1827
- name="detect_file_open_operations",
1828
- pattern=r"(\.open\(|with open\()",
1829
- replacement=r"MATCH",
1830
- test_cases=[
1831
- ("file.open()", "fileMATCH)"),
1832
- ("with open('file.txt'): ", "MATCH'file.txt'): "),
1833
- ("other_method()", "other_method()"),
1834
- ],
1835
- description="Detect file open operations for resource management integration",
1836
- ),
1837
- "match_async_function_definition": ValidatedPattern(
1838
- name="match_async_function_definition",
1839
- pattern=r"(async def \w+\([^)]*\)[^: ]*: )",
1840
- replacement=r"\1",
1841
- test_cases=[
1842
- ("async def foo(): ", "async def foo(): "),
1843
- ("async def bar(a, b) -> None: ", "async def bar(a, b) -> None: "),
1844
- ("def sync_func(): ", "def sync_func(): "),
1845
- ],
1846
- description="Match async function definitions for resource management"
1847
- " integration",
1848
- ),
1849
- "match_class_definition": ValidatedPattern(
1850
- name="match_class_definition",
1851
- pattern=r"class (\w+).*: ",
1852
- replacement=r"\1",
1853
- test_cases=[
1854
- ("class MyClass: ", "MyClass"),
1855
- ("class MyClass(BaseClass): ", "MyClass"),
1856
- ("class MyClass(Base, Mixin): ", "MyClass"),
1857
- ("def not_class(): ", "def not_class(): "),
1858
- ],
1859
- description="Match class definitions for resource management integration",
1860
- ),
1861
- "replace_subprocess_popen_basic": ValidatedPattern(
1862
- name="replace_subprocess_popen_basic",
1863
- pattern=r"subprocess\.Popen\(",
1864
- replacement="managed_proc = resource_ctx.managed_process(subprocess.Popen(",
1865
- test_cases=[
1866
- (
1867
- "subprocess.Popen(cmd)",
1868
- "managed_proc = resource_ctx.managed_process(subprocess.Popen(cmd)",
1869
- ),
1870
- (
1871
- "result = subprocess.Popen(['ls'])",
1872
- "result = managed_proc = resource_ctx.managed_process("
1873
- "subprocess.Popen(['ls'])",
1874
- ),
1875
- ],
1876
- description="Replace subprocess.Popen with managed version",
1877
- ),
1878
- "replace_subprocess_popen_assignment": ValidatedPattern(
1879
- name="replace_subprocess_popen_assignment",
1880
- pattern=r"(\w+)\s*=\s*subprocess\.Popen\(",
1881
- replacement=r"process = subprocess.Popen(",
1882
- test_cases=[
1883
- ("proc = subprocess.Popen(cmd)", "process = subprocess.Popen(cmd)"),
1884
- (
1885
- "my_process = subprocess.Popen(['ls'])",
1886
- "process = subprocess.Popen(['ls'])",
1887
- ),
1888
- ],
1889
- description="Replace subprocess.Popen assignment with standard variable name",
1890
- ),
1891
- "replace_path_open_write": ValidatedPattern(
1892
- name="replace_path_open_write",
1893
- pattern=r'(\w+)\.open\(["\']wb?["\'][^)]*\)',
1894
- replacement=r"atomic_file_write(\1)",
1895
- test_cases=[
1896
- ("path.open('w')", "atomic_file_write(path)"),
1897
- ("file.open('wb')", "atomic_file_write(file)"),
1898
- ],
1899
- description="Replace file.open() with atomic_file_write",
1900
- ),
1901
- "replace_path_write_text": ValidatedPattern(
1902
- name="replace_path_write_text",
1903
- pattern=r"(\w+)\.write_text\(([^)]+)\)",
1904
- replacement=r"await SafeFileOperations.safe_write_text(\1, \2, atomic=True)",
1905
- test_cases=[
1906
- (
1907
- "path.write_text(content)",
1908
- "await SafeFileOperations.safe_write_text(path, content, atomic=True)",
1909
- ),
1910
- (
1911
- "file.write_text(data, encoding='utf-8')",
1912
- "await SafeFileOperations.safe_write_text(file, data, encoding='utf-8', "
1913
- "atomic=True)",
1914
- ),
1915
- ],
1916
- description="Replace path.write_text with SafeFileOperations.safe_write_text",
1917
- ),
1918
- "agent_count_pattern": ValidatedPattern(
1919
- name="agent_count_pattern",
1920
- pattern=r"(\d+)\s+agents",
1921
- replacement=r"\1 agents",
1922
- test_cases=[
1923
- ("9 agents", "9 agents"),
1924
- ("12 agents", "12 agents"),
1925
- ("5 agents", "5 agents"),
1926
- ],
1927
- description="Match agent count patterns for documentation consistency",
1928
- flags=re.IGNORECASE,
1929
- ),
1930
- "specialized_agent_count_pattern": ValidatedPattern(
1931
- name="specialized_agent_count_pattern",
1932
- pattern=r"(\d+)\s+specialized\s+agents",
1933
- replacement=r"\1 specialized agents",
1934
- test_cases=[
1935
- ("9 specialized agents", "9 specialized agents"),
1936
- ("12 specialized agents", "12 specialized agents"),
1937
- ("5 specialized agents", "5 specialized agents"),
1938
- ],
1939
- description="Match specialized agent count patterns for documentation "
1940
- "consistency",
1941
- flags=re.IGNORECASE,
1942
- ),
1943
- "total_agents_config_pattern": ValidatedPattern(
1944
- name="total_agents_config_pattern",
1945
- pattern=r'total_agents["\'][\s]*: \s*(\d+)',
1946
- replacement=r'total_agents": \1',
1947
- test_cases=[
1948
- ('total_agents": 9', 'total_agents": 9'),
1949
- ("total_agents': 12", 'total_agents": 12'),
1950
- ('total_agents" : 5', 'total_agents": 5'),
1951
- ],
1952
- description="Match total agents configuration patterns",
1953
- flags=re.IGNORECASE,
1954
- ),
1955
- "sub_agent_count_pattern": ValidatedPattern(
1956
- name="sub_agent_count_pattern",
1957
- pattern=r"(\d+)\s+sub-agents",
1958
- replacement=r"\1 sub-agents",
1959
- test_cases=[
1960
- ("9 sub-agents", "9 sub-agents"),
1961
- ("12 sub-agents", "12 sub-agents"),
1962
- ("5 sub-agents", "5 sub-agents"),
1963
- ],
1964
- description="Match sub-agent count patterns for documentation consistency",
1965
- flags=re.IGNORECASE,
1966
- ),
1967
- "update_agent_count": ValidatedPattern(
1968
- name="update_agent_count",
1969
- pattern=r"\b(\d+)\s+agents\b",
1970
- replacement=r"NEW_COUNT agents",
1971
- test_cases=[
1972
- ("9 agents working", "NEW_COUNT agents working"),
1973
- ("We have 12 agents ready", "We have NEW_COUNT agents ready"),
1974
- ("All 5 agents are active", "All NEW_COUNT agents are active"),
1975
- ],
1976
- description="Update agent count references (NEW_COUNT replaced dynamically)",
1977
- ),
1978
- "update_specialized_agent_count": ValidatedPattern(
1979
- name="update_specialized_agent_count",
1980
- pattern=r"\b(\d+)\s+specialized\s+agents\b",
1981
- replacement=r"NEW_COUNT specialized agents",
1982
- test_cases=[
1983
- (
1984
- "9 specialized agents available",
1985
- "NEW_COUNT specialized agents available",
1986
- ),
1987
- ("We have 12 specialized agents", "We have NEW_COUNT specialized agents"),
1988
- ("All 5 specialized agents work", "All NEW_COUNT specialized agents work"),
1989
- ],
1990
- description="Update specialized agent count references (NEW_COUNT replaced"
1991
- " dynamically)",
1992
- ),
1993
- "update_total_agents_config": ValidatedPattern(
1994
- name="update_total_agents_config",
1995
- pattern=r'total_agents["\'][\s]*: \s*\d+',
1996
- replacement=r'total_agents": NEW_COUNT',
1997
- test_cases=[
1998
- ('total_agents": 9', 'total_agents": NEW_COUNT'),
1999
- ("total_agents': 12", 'total_agents": NEW_COUNT'),
2000
- ('total_agents" : 5', 'total_agents": NEW_COUNT'),
2001
- ],
2002
- description="Update total agents configuration (NEW_COUNT replaced"
2003
- " dynamically)",
2004
- ),
2005
- "update_sub_agent_count": ValidatedPattern(
2006
- name="update_sub_agent_count",
2007
- pattern=r"\b(\d+)\s+sub-agents\b",
2008
- replacement=r"NEW_COUNT sub-agents",
2009
- test_cases=[
2010
- ("9 sub-agents working", "NEW_COUNT sub-agents working"),
2011
- ("We have 12 sub-agents ready", "We have NEW_COUNT sub-agents ready"),
2012
- ("All 5 sub-agents are active", "All NEW_COUNT sub-agents are active"),
2013
- ],
2014
- description="Update sub-agent count references (NEW_COUNT replaced"
2015
- " dynamically)",
2016
- ),
2017
- "fixture_not_found_pattern": ValidatedPattern(
2018
- name="fixture_not_found_pattern",
2019
- pattern=r"fixture '(\w+)' not found",
2020
- replacement=r"fixture '\1' not found",
2021
- test_cases=[
2022
- ("fixture 'temp_pkg_path' not found", "fixture 'temp_pkg_path' not found"),
2023
- ("fixture 'console' not found", "fixture 'console' not found"),
2024
- ("fixture 'tmp_path' not found", "fixture 'tmp_path' not found"),
2025
- ],
2026
- description="Match pytest fixture not found error patterns",
2027
- ),
2028
- "import_error_pattern": ValidatedPattern(
2029
- name="import_error_pattern",
2030
- pattern=r"ImportError|ModuleNotFoundError",
2031
- replacement=r"ImportError",
2032
- test_cases=[
2033
- ("ImportError: No module named", "ImportError: No module named"),
2034
- ("ModuleNotFoundError: No module", "ImportError: No module"),
2035
- ("Other error types", "Other error types"),
2036
- ],
2037
- description="Match import error patterns in test failures",
2038
- ),
2039
- "assertion_error_pattern": ValidatedPattern(
2040
- name="assertion_error_pattern",
2041
- pattern=r"assert .+ ==",
2042
- replacement=r"AssertionError",
2043
- test_cases=[
2044
- (
2045
- "AssertionError: Values differ",
2046
- "AssertionError: Values differ",
2047
- ),
2048
- ("assert result == expected", "AssertionError expected"),
2049
- ("Normal code", "Normal code"),
2050
- ],
2051
- description="Match assertion error patterns in test failures",
2052
- ),
2053
- "attribute_error_pattern": ValidatedPattern(
2054
- name="attribute_error_pattern",
2055
- pattern=r"AttributeError: .+ has no attribute",
2056
- replacement=r"AttributeError: has no attribute",
2057
- test_cases=[
2058
- (
2059
- "AttributeError: 'Mock' has no attribute 'test'",
2060
- "AttributeError: has no attribute 'test'",
2061
- ),
2062
- (
2063
- "AttributeError: 'NoneType' has no attribute 'value'",
2064
- "AttributeError: has no attribute 'value'",
2065
- ),
2066
- ("Normal error", "Normal error"),
2067
- ],
2068
- description="Match attribute error patterns in test failures",
2069
- ),
2070
- "mock_spec_error_pattern": ValidatedPattern(
2071
- name="mock_spec_error_pattern",
2072
- pattern=r"MockSpec|spec.*Mock",
2073
- replacement=r"MockSpec",
2074
- test_cases=[
2075
- ("MockSpec error occurred", "MockSpec error occurred"),
2076
- ("spec for Mock failed", "MockSpec failed"),
2077
- ("Normal mock usage", "Normal mock usage"),
2078
- ],
2079
- description="Match mock specification error patterns in test failures",
2080
- ),
2081
- "hardcoded_path_pattern": ValidatedPattern(
2082
- name="hardcoded_path_pattern",
2083
- pattern=r"'/test/path'|/test/path",
2084
- replacement=r"str(tmp_path)",
2085
- test_cases=[
2086
- ("'/test/path'", "str(tmp_path)"),
2087
- ("/test/path", "str(tmp_path)"),
2088
- ("'/other/path'", "'/other/path'"),
2089
- ],
2090
- description="Match hardcoded test path patterns that should use tmp_path",
2091
- ),
2092
- "missing_name_pattern": ValidatedPattern(
2093
- name="missing_name_pattern",
2094
- pattern=r"name '(\w+)' is not defined",
2095
- replacement=r"name '\1' is not defined",
2096
- test_cases=[
2097
- ("name 'pytest' is not defined", "name 'pytest' is not defined"),
2098
- ("name 'Mock' is not defined", "name 'Mock' is not defined"),
2099
- ("name 'Path' is not defined", "name 'Path' is not defined"),
2100
- ],
2101
- description="Match undefined name patterns in test failures",
2102
- ),
2103
- "pydantic_validation_pattern": ValidatedPattern(
2104
- name="pydantic_validation_pattern",
2105
- pattern=r"ValidationError|validation error",
2106
- replacement=r"ValidationError",
2107
- test_cases=[
2108
- ("ValidationError: field required", "ValidationError: field required"),
2109
- ("validation error in field", "ValidationError in field"),
2110
- ("Normal validation", "Normal validation"),
2111
- ],
2112
- description="Match Pydantic validation error patterns in test failures",
2113
- ),
2114
- "list_append_inefficiency_pattern": ValidatedPattern(
2115
- name="list_append_inefficiency_pattern",
2116
- pattern=r"(\s*)(\w+)\s*\+=\s*\[([^]]+)\]",
2117
- replacement=r"\1\2.append(\3)",
2118
- test_cases=[
2119
- (" items += [new_item]", " items.append(new_item)"),
2120
- ("results += [result]", "results.append(result)"),
2121
- (" data += [value, other]", " data.append(value, other)"),
2122
- ],
2123
- description="Replace inefficient list[t.Any] concatenation with append for"
2124
- " performance",
2125
- ),
2126
- "string_concatenation_pattern": ValidatedPattern(
2127
- name="string_concatenation_pattern",
2128
- pattern=r"(\s*)(\w+)\s*\+=\s*(.+)",
2129
- replacement=r"\1\2_parts.append(\3)",
2130
- test_cases=[
2131
- (" text += new_text", " text_parts.append(new_text)"),
2132
- ("result += line", "result_parts.append(line)"),
2133
- (" output += data", " output_parts.append(data)"),
2134
- ],
2135
- description="Replace string concatenation with list[t.Any] append for performance "
2136
- "optimization",
2137
- ),
2138
- "nested_loop_detection_pattern": ValidatedPattern(
2139
- name="nested_loop_detection_pattern",
2140
- pattern=r"(\s*)(for\s+\w+\s+in\s+.*: )",
2141
- replacement=r"\1# Performance: Potential nested loop - check complexity\n\1\2",
2142
- test_cases=[
2143
- (
2144
- " for j in other: ",
2145
- " # Performance: Potential nested loop - check complexity\n "
2146
- "for j in other: ",
2147
- ),
2148
- (
2149
- "for i in items: ",
2150
- "# Performance: Potential nested loop - check complexity\nfor i"
2151
- " in items: ",
2152
- ),
2153
- ],
2154
- description="Detect loop patterns that might be nested creating O(n²)"
2155
- " complexity",
2156
- flags=re.MULTILINE,
2157
- ),
2158
- "list_extend_optimization_pattern": ValidatedPattern(
2159
- name="list_extend_optimization_pattern",
2160
- pattern=r"(\s*)(\w+)\s*\+=\s*\[([^]]+(?: , \s*[^]]+)*)\]",
2161
- replacement=r"\1\2.extend([\3])",
2162
- test_cases=[
2163
- (" items += [a, b, c]", " items.extend([a, b, c])"),
2164
- ("results += [x, y]", "results.extend([x, y])"),
2165
- (" data += [single_item]", " data.extend([single_item])"),
2166
- ],
2167
- description="Replace list[t.Any] concatenation with extend for better performance with multiple items",
2168
- ),
2169
- "inefficient_string_join_pattern": ValidatedPattern(
2170
- name="inefficient_string_join_pattern",
2171
- pattern=r"(\s*)(\w+)\s*=\s*([\"'])([\"'])\s*\.\s*join\(\s*\[\s*\]\s*\)",
2172
- replacement=r"\1\2 = \3\4 # Performance: Use empty string directly instead"
2173
- r" of join",
2174
- test_cases=[
2175
- (
2176
- ' text = "".join([])',
2177
- ' text = "" # Performance: Use empty string directly instead of join',
2178
- ),
2179
- (
2180
- "result = ''.join([])",
2181
- "result = '' # Performance: Use empty string directly instead of join",
2182
- ),
2183
- ],
2184
- description="Replace inefficient empty list[t.Any] join with direct empty string"
2185
- " assignment",
2186
- ),
2187
- "repeated_len_in_loop_pattern": ValidatedPattern(
2188
- name="repeated_len_in_loop_pattern",
2189
- pattern=r"(\s*)(len\(\s*(\w+)\s*\))",
2190
- replacement=r"\1# Performance: Consider caching len(\3) if used "
2191
- r"repeatedly\n\1\2",
2192
- test_cases=[
2193
- (
2194
- " len(items)",
2195
- " # Performance: Consider caching len(items) if used repeatedly\n"
2196
- " len(items)",
2197
- ),
2198
- (
2199
- "len(data)",
2200
- "# Performance: Consider caching len(data) if used "
2201
- "repeatedly\nlen(data)",
2202
- ),
2203
- ],
2204
- description="Suggest caching len() calls that might be repeated",
2205
- ),
2206
- "list_comprehension_optimization_pattern": ValidatedPattern(
2207
- name="list_comprehension_optimization_pattern",
2208
- pattern=r"(\s*)(\w+)\.append\(([^)]+)\)",
2209
- replacement=r"\1# Performance: Consider list[t.Any] comprehension if this is in a "
2210
- r"simple loop\n\1\2.append(\3)",
2211
- test_cases=[
2212
- (
2213
- " results.append(item * 2)",
2214
- " # Performance: Consider list[t.Any] comprehension if this is in a "
2215
- "simple loop\n results.append(item * 2)",
2216
- ),
2217
- (
2218
- "data.append(value)",
2219
- "# Performance: Consider list[t.Any] comprehension if this is in a simple"
2220
- " loop\ndata.append(value)",
2221
- ),
2222
- ],
2223
- description="Suggest list[t.Any] comprehensions for simple append patterns",
2224
- ),
2225
- "detect_crypto_weak_algorithms": ValidatedPattern(
2226
- name="detect_crypto_weak_algorithms",
2227
- pattern=r"\b(?:md4|md5|sha1|des|3des|rc4)\b",
2228
- replacement="[WEAK_CRYPTO_ALGORITHM]",
2229
- description="Detect weak cryptographic algorithms",
2230
- flags=re.IGNORECASE,
2231
- global_replace=True,
2232
- test_cases=[
2233
- ("hashlib.md5()", "hashlib.[WEAK_CRYPTO_ALGORITHM]()"),
2234
- ("using DES encryption", "using [WEAK_CRYPTO_ALGORITHM] encryption"),
2235
- ("SHA256 is good", "SHA256 is good"),
2236
- ("MD4 hashing", "[WEAK_CRYPTO_ALGORITHM] hashing"),
2237
- ],
2238
- ),
2239
- "detect_hardcoded_credentials_advanced": ValidatedPattern(
2240
- name="detect_hardcoded_credentials_advanced",
2241
- pattern=r"(?i)\b(?:password|passwd|pwd|secret|key|token|api_key|"
2242
- r'apikey)\s*[:=]\s*["\'][^"\']{3,}["\']',
2243
- replacement="[HARDCODED_CREDENTIAL_DETECTED]",
2244
- description="Detect hardcoded credentials in various formats "
2245
- "(case insensitive)",
2246
- flags=re.IGNORECASE,
2247
- global_replace=True,
2248
- test_cases=[
2249
- ('password="secret123"', "[HARDCODED_CREDENTIAL_DETECTED]"),
2250
- ("API_KEY = 'abc-123-def'", "[HARDCODED_CREDENTIAL_DETECTED]"),
2251
- ('token: "my-secret-token"', "[HARDCODED_CREDENTIAL_DETECTED]"),
2252
- (
2253
- 'username = "user"',
2254
- 'username = "user"',
2255
- ),
2256
- ],
2257
- ),
2258
- "detect_subprocess_shell_injection": ValidatedPattern(
2259
- name="detect_subprocess_shell_injection",
2260
- pattern=r"\bsubprocess\.\w+\([^)]*shell\s*=\s*True[^)]*\)",
2261
- replacement="[SHELL_INJECTION_RISK]",
2262
- description="Detect subprocess calls with shell=True",
2263
- global_replace=True,
2264
- test_cases=[
2265
- ("subprocess.run(cmd, shell=True)", "[SHELL_INJECTION_RISK]"),
2266
- ("subprocess.call(command, shell=True)", "[SHELL_INJECTION_RISK]"),
2267
- (
2268
- "subprocess.run(cmd, shell=False)",
2269
- "subprocess.run(cmd, shell=False)",
2270
- ),
2271
- ],
2272
- ),
2273
- "detect_regex_redos_vulnerable": ValidatedPattern(
2274
- name="detect_regex_redos_vulnerable",
2275
- pattern=r"\([^)]+\)[\*\+]",
2276
- replacement="[REDOS_VULNERABLE_PATTERN]",
2277
- description="Detect regex patterns vulnerable to ReDoS attacks (simplified"
2278
- " detection)",
2279
- global_replace=True,
2280
- test_cases=[
2281
- ("(a+)*", "[REDOS_VULNERABLE_PATTERN]"),
2282
- ("(a*)+", "[REDOS_VULNERABLE_PATTERN]"),
2283
- ("(abc)+", "[REDOS_VULNERABLE_PATTERN]"),
2284
- ("simple+", "simple+"),
2285
- ],
2286
- ),
2287
- "fix_hardcoded_jwt_secret": ValidatedPattern(
2288
- name="fix_hardcoded_jwt_secret",
2289
- pattern=r'(JWT_SECRET|jwt_secret)\s*=\s*["\'][^"\']+["\']',
2290
- replacement=r'\1 = os.getenv("JWT_SECRET", "")',
2291
- description="Replace hardcoded JWT secrets with environment variables",
2292
- global_replace=True,
2293
- test_cases=[
2294
- (
2295
- 'JWT_SECRET = "hardcoded-secret"',
2296
- 'JWT_SECRET = os.getenv("JWT_SECRET", "")',
2297
- ),
2298
- ('jwt_secret = "my-secret"', 'jwt_secret = os.getenv("JWT_SECRET", "")'),
2299
- ('other_var = "value"', 'other_var = "value"'),
2300
- ],
2301
- ),
2302
- "detect_unsafe_pickle_usage": ValidatedPattern(
2303
- name="detect_unsafe_pickle_usage",
2304
- pattern=r"\bpickle\.(loads?)\s*\(",
2305
- replacement=r"[UNSAFE_PICKLE_USAGE].\1(",
2306
- description="Detect potentially unsafe pickle usage",
2307
- global_replace=True,
2308
- test_cases=[
2309
- ("pickle.load(file)", "[UNSAFE_PICKLE_USAGE].load(file)"),
2310
- ("pickle.loads(data)", "[UNSAFE_PICKLE_USAGE].loads(data)"),
2311
- ("my_pickle.load(file)", "my_pickle.load(file)"),
2312
- ],
2313
- ),
2314
- "extract_range_size": ValidatedPattern(
2315
- name="extract_range_size",
2316
- pattern=r"range\((\d+)\)",
2317
- replacement=r"\1",
2318
- description="Extract numeric size from range() calls",
2319
- test_cases=[
2320
- ("range(1000)", "1000"),
2321
- ("range(50)", "50"),
2322
- ("for i in range(100): ", "for i in 100: "),
2323
- ("other_func(10)", "other_func(10)"),
2324
- ],
2325
- ),
2326
- "match_error_code_patterns": ValidatedPattern(
2327
- name="match_error_code_patterns",
2328
- pattern=r"F\d{3}|I\d{3}|E\d{3}|W\d{3}",
2329
- replacement=r"\g<0>",
2330
- description="Match standard error codes like F403, I001, etc.",
2331
- test_cases=[
2332
- ("F403", "F403"),
2333
- ("I001", "I001"),
2334
- ("E302", "E302"),
2335
- ("W291", "W291"),
2336
- ("ABC123", "ABC123"),
2337
- ],
2338
- ),
2339
- "match_validation_patterns": ValidatedPattern(
2340
- name="match_validation_patterns",
2341
- pattern=r"if\s+not\s+\w+\s*: |if\s+\w+\s+is\s+None\s*: |if\s+len\(\w+\)\s*[<>=]",
2342
- replacement=r"\g<0>",
2343
- description="Match common validation patterns for extraction",
2344
- test_cases=[
2345
- ("if not var: ", "if not var: "),
2346
- ("if item is None: ", "if item is None: "),
2347
- ("if len(items) >", "if len(items) >"),
2348
- ("other code", "other code"),
2349
- ],
2350
- ),
2351
- "match_loop_patterns": ValidatedPattern(
2352
- name="match_loop_patterns",
2353
- pattern=r"\s*for\s+.*: \s*$|\s*while\s+.*: \s*$",
2354
- replacement=r"\g<0>",
2355
- description="Match for/while loop patterns",
2356
- test_cases=[
2357
- (" for i in items: ", " for i in items: "),
2358
- (" while condition: ", " while condition: "),
2359
- ("regular line", "regular line"),
2360
- ],
2361
- ),
2362
- "match_star_import": ValidatedPattern(
2363
- name="match_star_import",
2364
- pattern=r"from\s+\w+\s+import\s+\*",
2365
- replacement=r"\g<0>",
2366
- description="Match star import statements",
2367
- test_cases=[
2368
- ("from module import *", "from module import *"),
2369
- ("from my_pkg import *", "from my_pkg import *"),
2370
- ("from module import specific", "from module import specific"),
2371
- ],
2372
- ),
2373
- "clean_unused_import": ValidatedPattern(
2374
- name="clean_unused_import",
2375
- pattern=r"^\s*import\s+unused_module\s*$",
2376
- replacement=r"",
2377
- description="Remove unused import statements (example with unused_module)",
2378
- test_cases=[
2379
- (" import unused_module", ""),
2380
- (
2381
- "import other_module",
2382
- "import other_module",
2383
- ),
2384
- ],
2385
- ),
2386
- "clean_unused_from_import": ValidatedPattern(
2387
- name="clean_unused_from_import",
2388
- pattern=r"^\s*from\s+\w+\s+import\s+.*\bunused_item\b",
2389
- replacement=r"\g<0>",
2390
- description="Match from import statements with unused items (example with "
2391
- "unused_item)",
2392
- test_cases=[
2393
- (
2394
- "from module import used, unused_item",
2395
- "from module import used, unused_item",
2396
- ),
2397
- ("from other import needed", "from other import needed"),
2398
- ],
2399
- ),
2400
- "clean_import_commas": ValidatedPattern(
2401
- name="clean_import_commas",
2402
- pattern=r", \s*, ",
2403
- replacement=r", ",
2404
- description="Clean double commas in import statements",
2405
- test_cases=[
2406
- ("from module import a, , b", "from module import a, b"),
2407
- ("items = [a, , b]", "items = [a, b]"),
2408
- ("normal, list[t.Any]", "normal, list[t.Any]"),
2409
- ],
2410
- ),
2411
- "clean_trailing_import_comma": ValidatedPattern(
2412
- name="clean_trailing_import_comma",
2413
- pattern=r", \s*$",
2414
- replacement=r"",
2415
- description="Remove trailing commas from lines",
2416
- test_cases=[
2417
- ("from module import a, b, ", "from module import a, b"),
2418
- ("import item, ", "import item"),
2419
- ("normal line", "normal line"),
2420
- ],
2421
- ),
2422
- "clean_import_prefix": ValidatedPattern(
2423
- name="clean_import_prefix",
2424
- pattern=r"import\s*, \s*",
2425
- replacement=r"import ",
2426
- description="Clean malformed import statements with leading comma",
2427
- test_cases=[
2428
- ("import , module", "import module"),
2429
- ("from pkg import , item", "from pkg import item"),
2430
- ("import normal", "import normal"),
2431
- ],
2432
- ),
2433
- "extract_unused_import_name": ValidatedPattern(
2434
- name="extract_unused_import_name",
2435
- pattern=r"unused import ['\"]([^'\"]+)['\"]",
2436
- replacement=r"\1",
2437
- description="Extract import name from vulture unused import messages",
2438
- test_cases=[
2439
- ("unused import 'module_name'", "module_name"),
2440
- ('unused import "other_module"', "other_module"),
2441
- ("some other text", "some other text"),
2442
- ],
2443
- ),
2444
- "normalize_whitespace": ValidatedPattern(
2445
- name="normalize_whitespace",
2446
- pattern=r"\s+",
2447
- replacement=r" ",
2448
- description="Normalize multiple whitespace to single space",
2449
- global_replace=True,
2450
- test_cases=[
2451
- ("import module", "import module"),
2452
- ("from pkg import item", "from pkg import item"),
2453
- ("normal text", "normal text"),
2454
- ],
2455
- ),
2456
- # Template processing patterns
2457
- "extract_template_variables": ValidatedPattern(
2458
- name="extract_template_variables",
2459
- pattern=r"\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\}\}",
2460
- replacement=r"\1",
2461
- description="Extract template variables from {{variable}} patterns",
2462
- test_cases=[
2463
- ("Hello {{name}}", "Hello name"),
2464
- ("{{user_name}}", "user_name"),
2465
- ("{{ spaced_var }}", "spaced_var"),
2466
- ("text {{var1}} and {{var2}}", "text var1 and {{var2}}"),
2467
- ],
2468
- ),
2469
- "extract_template_sections": ValidatedPattern(
2470
- name="extract_template_sections",
2471
- pattern=r"\{\%\s*section\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\%\}",
2472
- replacement=r"\1",
2473
- description="Extract section names from {% section name %} patterns",
2474
- test_cases=[
2475
- ("{% section intro %}", "intro"),
2476
- ("{% section main_content %}", "main_content"),
2477
- ("text {% section footer %} more", "text footer more"),
2478
- ("{% section header_1 %}", "header_1"),
2479
- ],
2480
- ),
2481
- "extract_template_blocks": ValidatedPattern(
2482
- name="extract_template_blocks",
2483
- pattern=r"\{\%\s*block\s+(\w+)\s*\%\}(.*?)\{\%\s*endblock\s*\%\}",
2484
- replacement=r"\1",
2485
- description="Extract block names and content from template blocks",
2486
- flags=re.DOTALL,
2487
- test_cases=[
2488
- ("{% block title %}Hello{% endblock %}", "title"),
2489
- ("{% block content %}Text content{% endblock %}", "content"),
2490
- ("{% block main %}Multi\nline{% endblock %}", "main"),
2491
- (
2492
- "prefix {% block nav %}nav content{% endblock %} suffix",
2493
- "prefix nav suffix",
2494
- ),
2495
- ],
2496
- ),
2497
- "replace_template_block": ValidatedPattern(
2498
- name="replace_template_block",
2499
- pattern=r"\{\%\s*block\s+BLOCK_NAME\s*\%\}.*?\{\%\s*endblock\s*\%\}",
2500
- replacement="REPLACEMENT_CONTENT",
2501
- description="Replace a specific template block (use with dynamic pattern substitution)",
2502
- flags=re.DOTALL,
2503
- test_cases=[
2504
- ("{% block BLOCK_NAME %}old{% endblock %}", "REPLACEMENT_CONTENT"),
2505
- (
2506
- "{% block BLOCK_NAME %}old content{% endblock %}",
2507
- "REPLACEMENT_CONTENT",
2508
- ),
2509
- ],
2510
- ),
2511
- # Documentation parsing patterns
2512
- "extract_markdown_links": ValidatedPattern(
2513
- name="extract_markdown_links",
2514
- pattern=r"\[([^\]]+)\]\(([^)]+)\)",
2515
- replacement=r"\1 -> \2",
2516
- description="Extract markdown link text and URLs from [text](url) patterns",
2517
- test_cases=[
2518
- ("[Click here](http://example.com)", "Click here -> http://example.com"),
2519
- ("[Local file](./docs/readme.md)", "Local file -> ./docs/readme.md"),
2520
- (
2521
- "See [the docs](../reference.md) for more",
2522
- "See the docs -> ../reference.md for more",
2523
- ),
2524
- ("[Multi word link](path/to/file)", "Multi word link -> path/to/file"),
2525
- ],
2526
- ),
2527
- "extract_version_numbers": ValidatedPattern(
2528
- name="extract_version_numbers",
2529
- pattern=r"version\s+(\d+\.\d+\.\d+)",
2530
- replacement=r"\1",
2531
- description="Extract semantic version numbers from 'version X.Y.Z' patterns",
2532
- flags=re.IGNORECASE,
2533
- test_cases=[
2534
- ("version 1.2.3", "1.2.3"),
2535
- ("Version 10.0.1", "10.0.1"),
2536
- ("current version 0.5.0", "current 0.5.0"),
2537
- ("VERSION 2.11.4", "2.11.4"),
2538
- ],
2539
- ),
2540
- # Docstring parsing patterns
2541
- "extract_google_docstring_params": ValidatedPattern(
2542
- name="extract_google_docstring_params",
2543
- pattern=r"^\s*(\w+)(?:\s*\([^)]+\))?\s*:\s*(.+)$",
2544
- replacement=r"\1: \2",
2545
- description="Extract parameter names and descriptions from Google-style docstrings",
2546
- flags=re.MULTILINE,
2547
- test_cases=[
2548
- (" param1: Description here", "param1: Description here"),
2549
- ("param2 (str): String parameter", "param2: String parameter"),
2550
- (
2551
- " complex_param (Optional[int]): Complex type",
2552
- "complex_param: Complex type",
2553
- ),
2554
- ("simple: Simple desc", "simple: Simple desc"),
2555
- ],
2556
- ),
2557
- "extract_sphinx_docstring_params": ValidatedPattern(
2558
- name="extract_sphinx_docstring_params",
2559
- pattern=r":param\s+(\w+)\s*:\s*(.+)$",
2560
- replacement=r"\1: \2",
2561
- description="Extract parameter names and descriptions from Sphinx-style docstrings",
2562
- flags=re.MULTILINE,
2563
- test_cases=[
2564
- (":param name: The name parameter", "name: The name parameter"),
2565
- (":param user_id: User identifier", "user_id: User identifier"),
2566
- (
2567
- ":param spaced : Description with spaces",
2568
- "spaced: Description with spaces",
2569
- ),
2570
- (
2571
- ":param complex_var: Multi-word description here",
2572
- "complex_var: Multi-word description here",
2573
- ),
2574
- ],
2575
- ),
2576
- "extract_docstring_returns": ValidatedPattern(
2577
- name="extract_docstring_returns",
2578
- pattern=r"(?:Returns?|Return):\s*(.+?)(?=\n\n|\n\w+:|\Z)",
2579
- replacement=r"\1",
2580
- description="Extract return descriptions from docstrings",
2581
- flags=re.MULTILINE | re.DOTALL,
2582
- test_cases=[
2583
- ("Returns: A string value", "A string value"),
2584
- ("Return: Boolean indicating success", "Boolean indicating success"),
2585
- ("Returns: Multi-line\n description", "Multi-line\n description"),
2586
- ("Returns: Simple value\n\nArgs:", "Simple value\n\nArgs:"),
2587
- ],
2588
- ),
2589
- # Command processing patterns
2590
- "enhance_command_blocks": ValidatedPattern(
2591
- name="enhance_command_blocks",
2592
- pattern=r"```(?:bash|shell|sh)?\n([^`]+)\n```",
2593
- replacement=r"```bash\n\1\n```",
2594
- description="Enhance command blocks with proper bash syntax highlighting",
2595
- test_cases=[
2596
- ("```\npython -m test\n```", "```bash\npython -m test\n```"),
2597
- ("```bash\necho hello\n```", "```bash\necho hello\n```"),
2598
- ("```sh\nls -la\n```", "```bash\nls -la\n```"),
2599
- ("```shell\ncd /tmp\n```", "```bash\ncd /tmp\n```"),
2600
- ],
2601
- ),
2602
- "extract_step_numbers": ValidatedPattern(
2603
- name="extract_step_numbers",
2604
- pattern=r"^(\s*)(\d+)\.\s*(.+)$",
2605
- replacement=r"\1**Step \2**: \3",
2606
- description="Extract and enhance numbered steps in documentation",
2607
- flags=re.MULTILINE,
2608
- test_cases=[
2609
- ("1. First step", "**Step 1**: First step"),
2610
- (" 2. Indented step", " **Step 2**: Indented step"),
2611
- ("10. Double digit step", "**Step 10**: Double digit step"),
2612
- ("normal text", "normal text"),
2613
- ],
2614
- ),
2615
- "extract_bash_command_blocks": ValidatedPattern(
2616
- name="extract_bash_command_blocks",
2617
- pattern=r"```bash\n([^`]+)\n```",
2618
- replacement=r"\1",
2619
- description="Extract content from bash command blocks",
2620
- test_cases=[
2621
- ("```bash\necho hello\n```", "echo hello"),
2622
- ("```bash\npython -m test\n```", "python -m test"),
2623
- ("text\n```bash\nls -la\n```\nmore", "text\nls -la\nmore"),
2624
- ("```bash\nmulti\nline\ncommand\n```", "multi\nline\ncommand"),
2625
- ],
2626
- ),
2627
- "detect_coverage_badge": ValidatedPattern(
2628
- name="detect_coverage_badge",
2629
- pattern=r"!\[Coverage.*?\]\(.*?coverage.*?\)|!\[.*coverage.*?\]\(.*?shields\.io.*?coverage.*?\)|https://img\.shields\.io/badge/coverage-[\d\.]+%25-\w+",
2630
- replacement="",
2631
- description="Detect existing coverage badges in README content",
2632
- flags=re.IGNORECASE,
2633
- test_cases=[
2634
- (
2635
- "![Coverage](https://img.shields.io/badge/coverage-85.0%25-brightgreen)",
2636
- "",
2637
- ),
2638
- (
2639
- "![Code Coverage](https://img.shields.io/badge/coverage-75.5%25-yellow)",
2640
- "",
2641
- ),
2642
- (
2643
- "![coverage badge](https://shields.io/badge/coverage-90.0%25-brightgreen)",
2644
- "",
2645
- ),
2646
- ("Some text without badge", "Some text without badge"),
2647
- ],
2648
- ),
2649
- "update_coverage_badge_url": ValidatedPattern(
2650
- name="update_coverage_badge_url",
2651
- pattern=r"(!\[Coverage.*?\]\()([^)]+)(\))",
2652
- replacement=r"\1NEW_BADGE_URL\3",
2653
- description="Update coverage badge URL in markdown links",
2654
- test_cases=[
2655
- ("![Coverage](old_url)", "![Coverage](NEW_BADGE_URL)"),
2656
- ("![Coverage Badge](old_badge_url)", "![Coverage Badge](NEW_BADGE_URL)"),
2657
- ("text ![Coverage](url) more", "text ![Coverage](NEW_BADGE_URL) more"),
2658
- ("no badge here", "no badge here"),
2659
- ],
2660
- ),
2661
- "update_coverage_badge_any": ValidatedPattern(
2662
- name="update_coverage_badge_any",
2663
- pattern=r"(!\[.*coverage.*?\]\()([^)]+)(\))",
2664
- replacement=r"\1NEW_BADGE_URL\3",
2665
- description="Update any coverage-related badge URL",
2666
- flags=re.IGNORECASE,
2667
- test_cases=[
2668
- ("![Code Coverage](old_url)", "![Code Coverage](NEW_BADGE_URL)"),
2669
- ("![coverage badge](old_url)", "![coverage badge](NEW_BADGE_URL)"),
2670
- ("![test coverage](url)", "![test coverage](NEW_BADGE_URL)"),
2671
- ("![Other Badge](url)", "![Other Badge](url)"),
2672
- ],
2673
- ),
2674
- "update_shields_coverage_url": ValidatedPattern(
2675
- name="update_shields_coverage_url",
2676
- pattern=r"(https://img\.shields\.io/badge/coverage-[\d\.]+%25-\w+)",
2677
- replacement="NEW_BADGE_URL",
2678
- description="Update shields.io coverage badge URLs directly",
2679
- test_cases=[
2680
- (
2681
- "https://img.shields.io/badge/coverage-85.0%25-brightgreen",
2682
- "NEW_BADGE_URL",
2683
- ),
2684
- ("https://img.shields.io/badge/coverage-75.5%25-yellow", "NEW_BADGE_URL"),
2685
- (
2686
- "https://img.shields.io/badge/coverage-90.1%25-brightgreen",
2687
- "NEW_BADGE_URL",
2688
- ),
2689
- (
2690
- "https://img.shields.io/badge/build-passing-green",
2691
- "https://img.shields.io/badge/build-passing-green",
2692
- ),
2693
- ],
2694
- ),
2695
- "extract_coverage_percentage": ValidatedPattern(
2696
- name="extract_coverage_percentage",
2697
- pattern=r"coverage-([\d\.]+)%25",
2698
- replacement="", # Not used for extraction, just validation
2699
- description="Search for coverage percentage in badge URL",
2700
- test_cases=[
2701
- ("coverage-85.0%25", ""), # Will use search() to get group(1)
2702
- ("coverage-75.5%25", ""),
2703
- ("coverage-100.0%25", ""),
2704
- ("no coverage here", "no coverage here"), # No match
2705
- ],
2706
- ),
2707
- "detect_typing_usage": ValidatedPattern(
2708
- name="detect_typing_usage",
2709
- pattern=r"\bt\.[A-Z]",
2710
- replacement="",
2711
- description="Detect usage of typing module aliases like t.Any, t.Dict, etc.",
2712
- global_replace=True,
2713
- test_cases=[
2714
- (
2715
- "def func(x: t.Any) -> t.Dict:",
2716
- "def func(x: ny) -> ict:",
2717
- ), # Removes t.A and t.D
2718
- (
2719
- "value: t.Optional[str] = None",
2720
- "value: ptional[str] = None",
2721
- ), # Removes t.O
2722
- ("from typing import Dict", "from typing import Dict"), # No match
2723
- ("data = dict()", "data = dict()"), # No match
2724
- ],
2725
- ),
2726
- }
2727
-
2728
-
2729
- def validate_all_patterns() -> dict[str, bool]:
2730
- validate_results: dict[str, bool] = {}
2731
- for name, pattern in SAFE_PATTERNS.items():
2732
- try:
2733
- pattern._validate()
2734
- results[name] = True
2735
- except ValueError as e:
2736
- results[name] = False
2737
- print(f"Pattern '{name}' failed validation: {e}")
2738
- return validate_results
2739
-
2740
-
2741
- def find_pattern_for_text(text: str) -> list[str]:
2742
- return [name for name, pattern in SAFE_PATTERNS.items() if pattern.test(text)]
2743
-
2744
-
2745
- def apply_safe_replacement(text: str, pattern_name: str) -> str:
2746
- if pattern_name not in SAFE_PATTERNS:
2747
- raise ValueError(f"Unknown pattern: {pattern_name}")
2748
-
2749
- return SAFE_PATTERNS[pattern_name].apply(text)
2750
-
2751
-
2752
- def get_pattern_description(pattern_name: str) -> str:
2753
- if pattern_name not in SAFE_PATTERNS:
2754
- return "Unknown pattern"
2755
-
2756
- return SAFE_PATTERNS[pattern_name].description
2757
-
2758
-
2759
- def fix_multi_word_hyphenation(text: str) -> str:
2760
- return SAFE_PATTERNS["fix_spaced_hyphens"].apply_iteratively(text)
2761
-
2762
-
2763
- def update_pyproject_version(content: str, new_version: str) -> str:
2764
- import re
2765
-
2766
- pattern_obj = SAFE_PATTERNS["update_pyproject_version"]
2767
-
2768
- temp_pattern = ValidatedPattern(
2769
- name="temp_version_update",
2770
- pattern=pattern_obj.pattern,
2771
- replacement=f"\\g<1>{new_version}\\g<3>",
2772
- description=f"Update version to {new_version}",
2773
- test_cases=[
2774
- ('version = "1.2.3"', f'version = "{new_version}"'),
2775
- ],
2776
- )
2777
-
2778
- return re.compile(pattern_obj.pattern, re.MULTILINE).sub(
2779
- temp_pattern.replacement, content
2780
- )
2781
-
2782
-
2783
- def apply_formatting_fixes(content: str) -> str:
2784
- import re
2785
-
2786
- pattern = SAFE_PATTERNS["remove_trailing_whitespace"]
2787
- content = re.compile(pattern.pattern, re.MULTILINE).sub(
2788
- pattern.replacement, content
2789
- )
2790
-
2791
- content = SAFE_PATTERNS["normalize_multiple_newlines"].apply(content)
2792
-
2793
- return content
2794
-
2795
-
2796
- def apply_security_fixes(content: str) -> str:
2797
- content = SAFE_PATTERNS["fix_subprocess_run_shell"].apply(content)
2798
- content = SAFE_PATTERNS["fix_subprocess_call_shell"].apply(content)
2799
- content = SAFE_PATTERNS["fix_subprocess_popen_shell"].apply(content)
2800
-
2801
- content = SAFE_PATTERNS["fix_unsafe_yaml_load"].apply(content)
2802
- content = SAFE_PATTERNS["fix_weak_md5_hash"].apply(content)
2803
- content = SAFE_PATTERNS["fix_weak_sha1_hash"].apply(content)
2804
- content = SAFE_PATTERNS["fix_insecure_random_choice"].apply(content)
2805
-
2806
- content = SAFE_PATTERNS["remove_debug_prints_with_secrets"].apply(content)
2807
-
2808
- return content
2809
-
2810
-
2811
- def apply_test_fixes(content: str) -> str:
2812
- return SAFE_PATTERNS["normalize_assert_statements"].apply(content)
2813
-
2814
-
2815
- def is_valid_job_id(job_id: str) -> bool:
2816
- return SAFE_PATTERNS["validate_job_id_alphanumeric"].test(job_id)
2817
-
2818
-
2819
- def remove_coverage_fail_under(addopts: str) -> str:
2820
- return SAFE_PATTERNS["remove_coverage_fail_under"].apply(addopts)
2821
-
2822
-
2823
- def update_coverage_requirement(content: str, new_coverage: float) -> str:
2824
- import re
2825
-
2826
- pattern_obj = SAFE_PATTERNS["update_coverage_requirement"]
2827
-
2828
- temp_pattern = ValidatedPattern(
2829
- name="temp_coverage_update",
2830
- pattern=pattern_obj.pattern,
2831
- replacement=f"\\1{new_coverage: .0f}",
2832
- description=f"Update coverage to {new_coverage}",
2833
- test_cases=[
2834
- ("--cov-fail-under=85", f"--cov-fail-under={new_coverage: .0f}"),
2835
- ],
2836
- )
2837
-
2838
- return re.compile(pattern_obj.pattern).sub(temp_pattern.replacement, content)
2839
-
2840
-
2841
- def update_repo_revision(content: str, repo_url: str, new_revision: str) -> str:
2842
- import re
2843
-
2844
- escaped_url = re.escape(repo_url)
2845
- pattern = rf'("repo": "{escaped_url}".*?"rev": )"([^"]+)"'
2846
- replacement = rf'\1"{new_revision}"'
2847
-
2848
- return re.compile(pattern, re.DOTALL).sub(replacement, content)
2849
-
2850
-
2851
- def sanitize_internal_urls(text: str) -> str:
2852
- url_patterns = [
2853
- "sanitize_localhost_urls",
2854
- "sanitize_127_urls",
2855
- "sanitize_any_localhost_urls",
2856
- "sanitize_ws_localhost_urls",
2857
- "sanitize_ws_127_urls",
2858
- "sanitize_simple_localhost_urls",
2859
- "sanitize_simple_ws_localhost_urls",
2860
- ]
2861
-
2862
- result = text
2863
- for pattern_name in url_patterns:
2864
- result = SAFE_PATTERNS[pattern_name].apply(result)
2865
-
2866
- return result
2867
-
2868
-
2869
- def apply_pattern_iteratively(
2870
- text: str, pattern_name: str, max_iterations: int = MAX_ITERATIONS
2871
- ) -> str:
2872
- if pattern_name not in SAFE_PATTERNS:
2873
- raise ValueError(f"Unknown pattern: {pattern_name}")
2874
-
2875
- return SAFE_PATTERNS[pattern_name].apply_iteratively(text, max_iterations)
2876
-
2877
-
2878
- def get_all_pattern_stats() -> dict[str, dict[str, float] | dict[str, str]]:
2879
- test_text = "python - m crackerjack - t with pytest - hypothesis - specialist"
2880
- stats: dict[str, dict[str, float] | dict[str, str]] = {}
2881
-
2882
- for name, pattern in SAFE_PATTERNS.items():
2883
- try:
2884
- pattern_stats = pattern.get_performance_stats(test_text, iterations=10)
2885
- stats[name] = pattern_stats
2886
- except Exception as e:
2887
- stats[name] = {"error": str(e)}
2888
-
2889
- return stats
2890
-
2891
-
2892
- def clear_all_caches() -> None:
2893
- CompiledPatternCache.clear_cache()
2894
-
2895
-
2896
- def get_cache_info() -> dict[str, int | list[str]]:
2897
- return CompiledPatternCache.get_cache_stats()
2898
-
2899
-
2900
- def detect_path_traversal_patterns(path_str: str) -> list[str]:
2901
- detected = []
2902
- traversal_patterns = [
2903
- "detect_directory_traversal_basic",
2904
- "detect_directory_traversal_backslash",
2905
- "detect_url_encoded_traversal",
2906
- "detect_double_url_encoded_traversal",
2907
- ]
2908
-
2909
- for pattern_name in traversal_patterns:
2910
- pattern = SAFE_PATTERNS[pattern_name]
2911
- if pattern.test(path_str):
2912
- detected.append(pattern_name)
2913
-
2914
- return detected
2915
-
2916
-
2917
- def detect_null_byte_patterns(path_str: str) -> list[str]:
2918
- detected = []
2919
- null_patterns = [
2920
- "detect_null_bytes_url",
2921
- "detect_null_bytes_literal",
2922
- "detect_utf8_overlong_null",
2923
- ]
2924
-
2925
- for pattern_name in null_patterns:
2926
- pattern = SAFE_PATTERNS[pattern_name]
2927
- if pattern.test(path_str):
2928
- detected.append(pattern_name)
2929
-
2930
- return detected
2931
-
2932
-
2933
- def detect_dangerous_directory_patterns(path_str: str) -> list[str]:
2934
- detected = []
2935
- dangerous_patterns = [
2936
- "detect_sys_directory_pattern",
2937
- "detect_proc_directory_pattern",
2938
- "detect_etc_directory_pattern",
2939
- "detect_boot_directory_pattern",
2940
- "detect_dev_directory_pattern",
2941
- "detect_root_directory_pattern",
2942
- "detect_var_log_directory_pattern",
2943
- "detect_bin_directory_pattern",
2944
- "detect_sbin_directory_pattern",
2945
- ]
2946
-
2947
- for pattern_name in dangerous_patterns:
2948
- pattern = SAFE_PATTERNS[pattern_name]
2949
- if pattern.test(path_str):
2950
- detected.append(pattern_name)
2951
-
2952
- return detected
2953
-
2954
-
2955
- def detect_suspicious_path_patterns(path_str: str) -> list[str]:
2956
- detected = []
2957
- suspicious_patterns = [
2958
- "detect_parent_directory_in_path",
2959
- "detect_suspicious_temp_traversal",
2960
- "detect_suspicious_var_traversal",
2961
- ]
2962
-
2963
- for pattern_name in suspicious_patterns:
2964
- pattern = SAFE_PATTERNS[pattern_name]
2965
- if pattern.test(path_str):
2966
- detected.append(pattern_name)
2967
-
2968
- return detected
2969
-
2970
-
2971
- def validate_path_security(path_str: str) -> dict[str, list[str]]:
2972
- return {
2973
- "traversal_patterns": detect_path_traversal_patterns(path_str),
2974
- "null_bytes": detect_null_byte_patterns(path_str),
2975
- "dangerous_directories": detect_dangerous_directory_patterns(path_str),
2976
- "suspicious_patterns": detect_suspicious_path_patterns(path_str),
2977
- }
2978
-
2979
-
2980
- if __name__ == "__main__":
2981
- results = validate_all_patterns()
2982
- if all(results.values()):
2983
- print("✅ All regex patterns validated successfully!")
2984
- else:
2985
- failed = [name for name, success in results.items() if not success]
2986
- print(f"❌ Pattern validation failed for: {failed}")
2987
- exit(1)
1
+ """Backward compatibility wrapper for refactored patterns module.
2
+
3
+ This module re-exports everything from the new patterns/ directory structure
4
+ to maintain backward compatibility with existing code that imports from
5
+ crackerjack.services.regex_patterns.
6
+
7
+ The actual implementation has been split into domain-specific modules:
8
+ - patterns/core.py - Base classes and utilities
9
+ - patterns/formatting.py - Formatting patterns
10
+ - patterns/security/ - Security-related patterns
11
+ - patterns/testing/ - Test-related patterns
12
+ - patterns/code/ - Code analysis patterns
13
+ - patterns/documentation/ - Documentation patterns
14
+ - patterns/tool_output/ - Tool output parsing patterns
15
+ - And more...
16
+
17
+ All imports should work exactly as before.
18
+ """
19
+
20
+ # Re-export everything from the new patterns module
21
+ from .patterns import * # noqa: F401, F403
22
+
23
+ __all__ = [
24
+ # Core classes
25
+ "ValidatedPattern",
26
+ "CompiledPatternCache",
27
+ "validate_pattern_safety",
28
+ "MAX_INPUT_SIZE",
29
+ "MAX_ITERATIONS",
30
+ "PATTERN_CACHE_SIZE",
31
+ # Main registry
32
+ "SAFE_PATTERNS",
33
+ # Utility functions
34
+ "validate_all_patterns",
35
+ "find_pattern_for_text",
36
+ "apply_safe_replacement",
37
+ "get_pattern_description",
38
+ "fix_multi_word_hyphenation",
39
+ "update_pyproject_version",
40
+ "apply_formatting_fixes",
41
+ "apply_security_fixes",
42
+ "apply_test_fixes",
43
+ "is_valid_job_id",
44
+ "remove_coverage_fail_under",
45
+ "update_coverage_requirement",
46
+ "update_repo_revision",
47
+ "sanitize_internal_urls",
48
+ "apply_pattern_iteratively",
49
+ "get_all_pattern_stats",
50
+ "clear_all_caches",
51
+ "get_cache_info",
52
+ "detect_path_traversal_patterns",
53
+ "detect_null_byte_patterns",
54
+ "detect_dangerous_directory_patterns",
55
+ "detect_suspicious_path_patterns",
56
+ "validate_path_security",
57
+ "RegexPatternsService",
58
+ ]