patchi 0.7.5__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 (496) hide show
  1. patchi/__init__.py +1 -0
  2. patchi/__main__.py +13 -0
  3. patchi/cli/__init__.py +0 -0
  4. patchi/cli/command_families.py +235 -0
  5. patchi/cli/commands/__init__.py +0 -0
  6. patchi/cli/commands/access_cmd.py +65 -0
  7. patchi/cli/commands/agent_stats_cmd.py +273 -0
  8. patchi/cli/commands/agents_cmd.py +187 -0
  9. patchi/cli/commands/ai_cmd.py +308 -0
  10. patchi/cli/commands/assure_cmd.py +538 -0
  11. patchi/cli/commands/audit_cmd.py +286 -0
  12. patchi/cli/commands/auto_cmd.py +110 -0
  13. patchi/cli/commands/blame_cmd.py +53 -0
  14. patchi/cli/commands/chains_cmd.py +327 -0
  15. patchi/cli/commands/charter_cmd.py +173 -0
  16. patchi/cli/commands/chat_cmd.py +597 -0
  17. patchi/cli/commands/check_cmd.py +232 -0
  18. patchi/cli/commands/cleanup_cmd.py +328 -0
  19. patchi/cli/commands/cockpit_cmd.py +208 -0
  20. patchi/cli/commands/commands_cmd.py +160 -0
  21. patchi/cli/commands/cross_repo_cmd.py +113 -0
  22. patchi/cli/commands/deps_cmd.py +110 -0
  23. patchi/cli/commands/dev_check_cmd.py +342 -0
  24. patchi/cli/commands/dev_cmd.py +445 -0
  25. patchi/cli/commands/doctor_cmd.py +457 -0
  26. patchi/cli/commands/findings_cmd.py +187 -0
  27. patchi/cli/commands/fix_cmd.py +648 -0
  28. patchi/cli/commands/fix_review_cmd.py +273 -0
  29. patchi/cli/commands/heatmap_cmd.py +62 -0
  30. patchi/cli/commands/help_cmd.py +159 -0
  31. patchi/cli/commands/hosted_cmd.py +980 -0
  32. patchi/cli/commands/init.py +456 -0
  33. patchi/cli/commands/key_cmd.py +571 -0
  34. patchi/cli/commands/learn_cmd.py +140 -0
  35. patchi/cli/commands/link_cmd.py +104 -0
  36. patchi/cli/commands/log_cmd.py +57 -0
  37. patchi/cli/commands/memory_cmd.py +215 -0
  38. patchi/cli/commands/model_cmd.py +207 -0
  39. patchi/cli/commands/notify_cmd.py +213 -0
  40. patchi/cli/commands/notify_prompts.py +29 -0
  41. patchi/cli/commands/patch_cmd.py +231 -0
  42. patchi/cli/commands/plan_cmd.py +128 -0
  43. patchi/cli/commands/plugins_cmd.py +307 -0
  44. patchi/cli/commands/queue_cmd.py +152 -0
  45. patchi/cli/commands/quick_cmd.py +159 -0
  46. patchi/cli/commands/ready_cmd.py +292 -0
  47. patchi/cli/commands/report_cmd.py +416 -0
  48. patchi/cli/commands/restrict_cmd.py +127 -0
  49. patchi/cli/commands/review_cmd.py +225 -0
  50. patchi/cli/commands/rules_cmd.py +233 -0
  51. patchi/cli/commands/scan_cmd.py +1908 -0
  52. patchi/cli/commands/settings_cmd.py +45 -0
  53. patchi/cli/commands/status_cmd.py +679 -0
  54. patchi/cli/commands/test_cmd.py +1052 -0
  55. patchi/cli/commands/trend_cmd.py +296 -0
  56. patchi/cli/commands/undo_cmd.py +235 -0
  57. patchi/cli/commands/update_cmd.py +345 -0
  58. patchi/cli/commands/verify_cmd.py +56 -0
  59. patchi/cli/commands/vr_cmd.py +440 -0
  60. patchi/cli/commands/watch_cmd.py +304 -0
  61. patchi/cli/commands/web_cmd.py +114 -0
  62. patchi/cli/console.py +101 -0
  63. patchi/cli/display/__init__.py +1 -0
  64. patchi/cli/display/live_audit.py +269 -0
  65. patchi/cli/display/live_progress.py +169 -0
  66. patchi/cli/framework.py +241 -0
  67. patchi/cli/logo.py +128 -0
  68. patchi/cli/main.py +222 -0
  69. patchi/cli/registry.py +1171 -0
  70. patchi/cli/style.py +68 -0
  71. patchi/cli/themes.py +187 -0
  72. patchi/cli/ux.py +444 -0
  73. patchi/core/__init__.py +0 -0
  74. patchi/core/agents/__init__.py +0 -0
  75. patchi/core/agents/api_fuzzer_agent.py +141 -0
  76. patchi/core/agents/attack_agent.py +242 -0
  77. patchi/core/agents/base.py +1012 -0
  78. patchi/core/agents/bug_predictor_agent.py +58 -0
  79. patchi/core/agents/build_tool_validator.py +150 -0
  80. patchi/core/agents/cache.py +192 -0
  81. patchi/core/agents/chaos_agent.py +133 -0
  82. patchi/core/agents/cicd_generator.py +240 -0
  83. patchi/core/agents/comment_scanner.py +280 -0
  84. patchi/core/agents/console_logging_agent.py +156 -0
  85. patchi/core/agents/coordinator.py +858 -0
  86. patchi/core/agents/core_scanner.py +269 -0
  87. patchi/core/agents/coverage_prioritizer.py +181 -0
  88. patchi/core/agents/dead_code_hygiene.py +296 -0
  89. patchi/core/agents/dead_code_scanner.py +692 -0
  90. patchi/core/agents/dependency_scanner.py +725 -0
  91. patchi/core/agents/doc_claim_agent.py +573 -0
  92. patchi/core/agents/duplicate_scanner.py +568 -0
  93. patchi/core/agents/env_scanner.py +410 -0
  94. patchi/core/agents/feature_flag_agent.py +89 -0
  95. patchi/core/agents/frontend_framework_agent.py +133 -0
  96. patchi/core/agents/goroutine_agent.py +82 -0
  97. patchi/core/agents/governor.py +1972 -0
  98. patchi/core/agents/i18n_agent.py +95 -0
  99. patchi/core/agents/license_compliance.py +247 -0
  100. patchi/core/agents/linking_agent.py +251 -0
  101. patchi/core/agents/memory_profiler_agent.py +102 -0
  102. patchi/core/agents/modernization_agent.py +84 -0
  103. patchi/core/agents/mutation_agent.py +174 -0
  104. patchi/core/agents/nl_query_agent.py +74 -0
  105. patchi/core/agents/promise_rejection_agent.py +100 -0
  106. patchi/core/agents/refactoring_agent.py +286 -0
  107. patchi/core/agents/resource_leak_agent.py +91 -0
  108. patchi/core/agents/route_graph_scanner.py +951 -0
  109. patchi/core/agents/run_agent.py +106 -0
  110. patchi/core/agents/rust_unwrap_agent.py +100 -0
  111. patchi/core/agents/sbom_generator.py +284 -0
  112. patchi/core/agents/scanners.py +67 -0
  113. patchi/core/agents/side/__init__.py +1 -0
  114. patchi/core/agents/side/build_agent.py +65 -0
  115. patchi/core/agents/side/format_agent.py +50 -0
  116. patchi/core/agents/side/install_agent.py +63 -0
  117. patchi/core/agents/side_file_scanner.py +549 -0
  118. patchi/core/agents/snapshot_drift_detector.py +146 -0
  119. patchi/core/agents/spa_route_inventory.py +203 -0
  120. patchi/core/agents/test_scanner.py +442 -0
  121. patchi/core/agents/tool_health.py +109 -0
  122. patchi/core/agents/tool_runner.py +162 -0
  123. patchi/core/agents/type_scanner.py +168 -0
  124. patchi/core/agents/ui_scanner.py +558 -0
  125. patchi/core/ai/__init__.py +20 -0
  126. patchi/core/ai/agent_audit.py +153 -0
  127. patchi/core/ai/agent_profiler.py +277 -0
  128. patchi/core/ai/client.py +499 -0
  129. patchi/core/ai/cost_tracker.py +107 -0
  130. patchi/core/ai/model_router.py +318 -0
  131. patchi/core/ai/orchestrator.py +625 -0
  132. patchi/core/ai/prompts.py +681 -0
  133. patchi/core/ai/smart.py +302 -0
  134. patchi/core/ai/tool_executor.py +24 -0
  135. patchi/core/ai/tools/__init__.py +10 -0
  136. patchi/core/ai/tools/executor.py +621 -0
  137. patchi/core/ai/tools/realize.py +1160 -0
  138. patchi/core/ai/tools/registry.py +1577 -0
  139. patchi/core/assurance/__init__.py +33 -0
  140. patchi/core/assurance/builder.py +211 -0
  141. patchi/core/assurance/graph.py +342 -0
  142. patchi/core/assurance/invariants.py +168 -0
  143. patchi/core/atomic.py +61 -0
  144. patchi/core/attackers/__init__.py +17 -0
  145. patchi/core/attackers/api_abuse.py +89 -0
  146. patchi/core/attackers/auth_bypass.py +73 -0
  147. patchi/core/attackers/base.py +91 -0
  148. patchi/core/attackers/business_logic.py +88 -0
  149. patchi/core/attackers/planner.py +108 -0
  150. patchi/core/attackers/priv_escalation.py +88 -0
  151. patchi/core/attackers/recon.py +87 -0
  152. patchi/core/brain/__init__.py +0 -0
  153. patchi/core/brain/ast_utils/__init__.py +81 -0
  154. patchi/core/brain/ast_utils/assignments.py +219 -0
  155. patchi/core/brain/ast_utils/calls.py +139 -0
  156. patchi/core/brain/ast_utils/config.py +132 -0
  157. patchi/core/brain/ast_utils/control_flow.py +74 -0
  158. patchi/core/brain/ast_utils/dead_symbols.py +205 -0
  159. patchi/core/brain/ast_utils/decorators.py +108 -0
  160. patchi/core/brain/ast_utils/helpers.py +144 -0
  161. patchi/core/brain/ast_utils/imports.py +169 -0
  162. patchi/core/brain/ast_utils/scan.py +143 -0
  163. patchi/core/brain/ast_utils/taint.py +310 -0
  164. patchi/core/brain/audit.py +379 -0
  165. patchi/core/brain/baseline.py +195 -0
  166. patchi/core/brain/blast_radius.py +312 -0
  167. patchi/core/brain/body_tags.py +144 -0
  168. patchi/core/brain/brain.py +1161 -0
  169. patchi/core/brain/brain_context.py +393 -0
  170. patchi/core/brain/brain_watcher.py +340 -0
  171. patchi/core/brain/charter.py +473 -0
  172. patchi/core/brain/classifier.py +214 -0
  173. patchi/core/brain/cockpit.py +350 -0
  174. patchi/core/brain/code_query.py +710 -0
  175. patchi/core/brain/contract.py +889 -0
  176. patchi/core/brain/contract_diff.py +155 -0
  177. patchi/core/brain/council.py +800 -0
  178. patchi/core/brain/cross_repo.py +104 -0
  179. patchi/core/brain/dead_code_tools.py +157 -0
  180. patchi/core/brain/doc_validator.py +197 -0
  181. patchi/core/brain/domain_activator.py +2183 -0
  182. patchi/core/brain/enriched_context.py +220 -0
  183. patchi/core/brain/file_corpus.py +220 -0
  184. patchi/core/brain/framework.py +953 -0
  185. patchi/core/brain/freshness.py +273 -0
  186. patchi/core/brain/git_aware.py +206 -0
  187. patchi/core/brain/heatmap.py +104 -0
  188. patchi/core/brain/ignore_parser.py +153 -0
  189. patchi/core/brain/import_graph.py +486 -0
  190. patchi/core/brain/languages.py +397 -0
  191. patchi/core/brain/layered_brain.py +419 -0
  192. patchi/core/brain/learning.py +229 -0
  193. patchi/core/brain/orphaned_endpoints.py +313 -0
  194. patchi/core/brain/personas/__init__.py +648 -0
  195. patchi/core/brain/personas/bad_user.py +165 -0
  196. patchi/core/brain/personas/base.py +349 -0
  197. patchi/core/brain/proactive.py +807 -0
  198. patchi/core/brain/project_context.py +121 -0
  199. patchi/core/brain/project_reader.py +521 -0
  200. patchi/core/brain/reasoning.py +269 -0
  201. patchi/core/brain/route_detector/__init__.py +15 -0
  202. patchi/core/brain/route_detector/base.py +31 -0
  203. patchi/core/brain/route_detector/csharp.py +209 -0
  204. patchi/core/brain/route_detector/file_based.py +81 -0
  205. patchi/core/brain/route_detector/go.py +152 -0
  206. patchi/core/brain/route_detector/java.py +195 -0
  207. patchi/core/brain/route_detector/javascript.py +197 -0
  208. patchi/core/brain/route_detector/php.py +158 -0
  209. patchi/core/brain/route_detector/python.py +193 -0
  210. patchi/core/brain/route_detector/registry.py +25 -0
  211. patchi/core/brain/route_detector/ruby.py +134 -0
  212. patchi/core/brain/route_detector/rust.py +216 -0
  213. patchi/core/brain/route_detector/swift.py +118 -0
  214. patchi/core/brain/route_mapper.py +265 -0
  215. patchi/core/brain/scanner.py +2136 -0
  216. patchi/core/brain/secrets.py +280 -0
  217. patchi/core/brain/symbol_graph.py +1955 -0
  218. patchi/core/brain/team.py +28 -0
  219. patchi/core/brain/trace_log.py +132 -0
  220. patchi/core/brain/type_checker/__init__.py +65 -0
  221. patchi/core/brain/type_checker/base.py +60 -0
  222. patchi/core/brain/type_checker/csharp.py +59 -0
  223. patchi/core/brain/type_checker/dart.py +59 -0
  224. patchi/core/brain/type_checker/go.py +81 -0
  225. patchi/core/brain/type_checker/java.py +75 -0
  226. patchi/core/brain/type_checker/kotlin.py +75 -0
  227. patchi/core/brain/type_checker/php.py +88 -0
  228. patchi/core/brain/type_checker/python.py +87 -0
  229. patchi/core/brain/type_checker/rust.py +81 -0
  230. patchi/core/brain/type_checker/swift.py +59 -0
  231. patchi/core/brain/type_checker/typescript.py +298 -0
  232. patchi/core/brain/understander.py +130 -0
  233. patchi/core/brain/verify.py +379 -0
  234. patchi/core/campaigns/__init__.py +14 -0
  235. patchi/core/campaigns/base.py +62 -0
  236. patchi/core/campaigns/data_flow_abuse.py +153 -0
  237. patchi/core/campaigns/orchestrator.py +140 -0
  238. patchi/core/campaigns/state_transitions.py +178 -0
  239. patchi/core/ci_bundle.py +104 -0
  240. patchi/core/config.py +311 -0
  241. patchi/core/constants.py +245 -0
  242. patchi/core/debug/__init__.py +25 -0
  243. patchi/core/debug/adapters/__init__.py +11 -0
  244. patchi/core/debug/adapters/codelldb.py +379 -0
  245. patchi/core/debug/adapters/node.py +222 -0
  246. patchi/core/debug/adapters/powershell.py +210 -0
  247. patchi/core/debug/adapters/python.py +244 -0
  248. patchi/core/debug/capture.py +210 -0
  249. patchi/core/debug/dap_client.py +268 -0
  250. patchi/core/detector/__init__.py +0 -0
  251. patchi/core/detector/audit.py +194 -0
  252. patchi/core/detector/bus.py +226 -0
  253. patchi/core/detector/dispatcher.py +275 -0
  254. patchi/core/detector/event.py +254 -0
  255. patchi/core/detector/sigma_engine.py +378 -0
  256. patchi/core/detector/triage.py +289 -0
  257. patchi/core/export/sarif.py +113 -0
  258. patchi/core/fix/__init__.py +17 -0
  259. patchi/core/fix/applier.py +574 -0
  260. patchi/core/fix/base.py +510 -0
  261. patchi/core/fix/code_fixer.py +169 -0
  262. patchi/core/fix/dead_code_remover.py +131 -0
  263. patchi/core/fix/fix_agents.py +775 -0
  264. patchi/core/fix/patch.py +444 -0
  265. patchi/core/fix/risk_gate.py +383 -0
  266. patchi/core/fix/security_fixer.py +93 -0
  267. patchi/core/fix/verify_loop.py +204 -0
  268. patchi/core/fuzz/__init__.py +8 -0
  269. patchi/core/fuzz/corpus.py +137 -0
  270. patchi/core/fuzz/input_fuzzer.py +256 -0
  271. patchi/core/fuzz/sequence_fuzzer.py +162 -0
  272. patchi/core/fuzz/state_fuzzer.py +236 -0
  273. patchi/core/health.py +407 -0
  274. patchi/core/hosted/__init__.py +1 -0
  275. patchi/core/hosted/anomaly.py +298 -0
  276. patchi/core/hosted/audit_log.py +102 -0
  277. patchi/core/hosted/compliance_report.py +148 -0
  278. patchi/core/hosted/ip_reputation.py +169 -0
  279. patchi/core/hosted/log_parsers.py +251 -0
  280. patchi/core/hosted/tokens.py +127 -0
  281. patchi/core/hosted/watchlist.py +164 -0
  282. patchi/core/hosted/webhooks.py +156 -0
  283. patchi/core/memory.py +437 -0
  284. patchi/core/notifications/__init__.py +18 -0
  285. patchi/core/notifications/channels.py +192 -0
  286. patchi/core/notifications/digest.py +141 -0
  287. patchi/core/notifications/escalation.py +158 -0
  288. patchi/core/notifications/notifier.py +182 -0
  289. patchi/core/notifications/quiet_hours.py +69 -0
  290. patchi/core/plugins/__init__.py +48 -0
  291. patchi/core/plugins/analyzer.py +326 -0
  292. patchi/core/plugins/analyzers/__init__.py +6 -0
  293. patchi/core/plugins/analyzers/dead_code_analyzer.py +120 -0
  294. patchi/core/plugins/analyzers/secret_scanner.py +158 -0
  295. patchi/core/plugins/analyzers/todo_scanner.py +115 -0
  296. patchi/core/plugins/registry.py +335 -0
  297. patchi/core/queue.py +375 -0
  298. patchi/core/reliability/__init__.py +13 -0
  299. patchi/core/reliability/chaos.py +163 -0
  300. patchi/core/reliability/fault_injector.py +172 -0
  301. patchi/core/reliability/idempotency.py +128 -0
  302. patchi/core/reliability/recovery.py +147 -0
  303. patchi/core/runtime/__init__.py +5 -0
  304. patchi/core/runtime/tracer.py +230 -0
  305. patchi/core/scan_bus.py +156 -0
  306. patchi/core/security/__init__.py +1 -0
  307. patchi/core/security/ai_validator.py +462 -0
  308. patchi/core/security/app_mapper.py +216 -0
  309. patchi/core/security/app_profile.py +464 -0
  310. patchi/core/security/attack_feedback.py +141 -0
  311. patchi/core/security/attack_scenarios/__init__.py +1 -0
  312. patchi/core/security/attack_tree.py +176 -0
  313. patchi/core/security/auth_audit_agent.py +344 -0
  314. patchi/core/security/authz_agent.py +602 -0
  315. patchi/core/security/auto_fix_proactive.py +620 -0
  316. patchi/core/security/auto_fixer.py +660 -0
  317. patchi/core/security/auto_ticket.py +53 -0
  318. patchi/core/security/bandit_agent.py +152 -0
  319. patchi/core/security/blast_radius.py +226 -0
  320. patchi/core/security/browser_tester.py +333 -0
  321. patchi/core/security/business_logic_agent.py +263 -0
  322. patchi/core/security/catch_block_auditor.py +395 -0
  323. patchi/core/security/cdn_cache_agent.py +357 -0
  324. patchi/core/security/chain_analyzer.py +321 -0
  325. patchi/core/security/chain_to_assurance.py +168 -0
  326. patchi/core/security/charter.py +510 -0
  327. patchi/core/security/cloud_waf_detector.py +295 -0
  328. patchi/core/security/codeql_agent.py +233 -0
  329. patchi/core/security/compliance_agent.py +348 -0
  330. patchi/core/security/confidence_gate.py +423 -0
  331. patchi/core/security/container_scanner.py +282 -0
  332. patchi/core/security/crypto_agent.py +378 -0
  333. patchi/core/security/cve_monitor.py +262 -0
  334. patchi/core/security/dast_agent.py +865 -0
  335. patchi/core/security/defectdojo.py +238 -0
  336. patchi/core/security/defenders/__init__.py +75 -0
  337. patchi/core/security/defenders/auth_middleware.py +115 -0
  338. patchi/core/security/defenders/base.py +97 -0
  339. patchi/core/security/defenders/block_ip.py +71 -0
  340. patchi/core/security/defenders/block_ws_origin.py +36 -0
  341. patchi/core/security/defenders/crypto_fix.py +118 -0
  342. patchi/core/security/defenders/enforce_rate_limit.py +108 -0
  343. patchi/core/security/defenders/fix_code.py +93 -0
  344. patchi/core/security/defenders/invalidate_session.py +40 -0
  345. patchi/core/security/defenders/patch_config.py +70 -0
  346. patchi/core/security/defenders/rotate_secret.py +54 -0
  347. patchi/core/security/defenders/suspend_account.py +41 -0
  348. patchi/core/security/defenders/update_dependency.py +160 -0
  349. patchi/core/security/defense_layer.py +217 -0
  350. patchi/core/security/dependency_vulnerability_agent.py +616 -0
  351. patchi/core/security/detection_pipeline.py +289 -0
  352. patchi/core/security/dns_security_agent.py +497 -0
  353. patchi/core/security/domain_activator_v2.py +913 -0
  354. patchi/core/security/domain_loader.py +1004 -0
  355. patchi/core/security/domains/__init__.py +0 -0
  356. patchi/core/security/email_authentication_agent.py +417 -0
  357. patchi/core/security/env_var_validator.py +304 -0
  358. patchi/core/security/evidence.py +100 -0
  359. patchi/core/security/falco_runtime_agent.py +387 -0
  360. patchi/core/security/fix-playbooks/__init__.py +0 -0
  361. patchi/core/security/gated_finding.py +85 -0
  362. patchi/core/security/git_diff_activator.py +367 -0
  363. patchi/core/security/governance.py +208 -0
  364. patchi/core/security/gradual_gate.py +34 -0
  365. patchi/core/security/history.py +315 -0
  366. patchi/core/security/iac_scanner.py +250 -0
  367. patchi/core/security/ignore_expiry.py +111 -0
  368. patchi/core/security/ignore_learner.py +416 -0
  369. patchi/core/security/injection_agent.py +319 -0
  370. patchi/core/security/insecure_randomness_agent.py +288 -0
  371. patchi/core/security/intent_analyzer.py +326 -0
  372. patchi/core/security/jwt_agent.py +453 -0
  373. patchi/core/security/kubernetes_agent.py +638 -0
  374. patchi/core/security/layer2_orchestrator.py +341 -0
  375. patchi/core/security/llm_security_agent.py +252 -0
  376. patchi/core/security/misconfig_agent.py +600 -0
  377. patchi/core/security/mobile_security_agent.py +474 -0
  378. patchi/core/security/network_agent.py +376 -0
  379. patchi/core/security/noise_filter.py +274 -0
  380. patchi/core/security/orchestrator.py +313 -0
  381. patchi/core/security/pattern_context.py +125 -0
  382. patchi/core/security/pattern_loader.py +101 -0
  383. patchi/core/security/pentest/__init__.py +4 -0
  384. patchi/core/security/pentest/base.py +58 -0
  385. patchi/core/security/pentest/dalfox_adapter.py +40 -0
  386. patchi/core/security/pentest/ffuf_adapter.py +48 -0
  387. patchi/core/security/pentest/nuclei_adapter.py +50 -0
  388. patchi/core/security/pentest/registry.py +80 -0
  389. patchi/core/security/pentest/shannon_adapter.py +107 -0
  390. patchi/core/security/pentest/sqlmap_adapter.py +56 -0
  391. patchi/core/security/pentest/zap_adapter.py +52 -0
  392. patchi/core/security/plan_auditor.py +276 -0
  393. patchi/core/security/policy_engine.py +272 -0
  394. patchi/core/security/prechecks.py +172 -0
  395. patchi/core/security/precommit_hook.py +247 -0
  396. patchi/core/security/privacy_agent.py +361 -0
  397. patchi/core/security/push_notification_agent.py +417 -0
  398. patchi/core/security/pysa_agent.py +143 -0
  399. patchi/core/security/reasoning.py +442 -0
  400. patchi/core/security/red_team_agent.py +184 -0
  401. patchi/core/security/red_team_engine.py +1142 -0
  402. patchi/core/security/remediation.py +360 -0
  403. patchi/core/security/request_interceptor.py +294 -0
  404. patchi/core/security/runtime_validator.py +245 -0
  405. patchi/core/security/saml_sso_agent.py +401 -0
  406. patchi/core/security/sast_agent.py +213 -0
  407. patchi/core/security/sast_gate.py +95 -0
  408. patchi/core/security/scheduler.py +287 -0
  409. patchi/core/security/secrets_runtime_agent.py +476 -0
  410. patchi/core/security/security_agents.py +175 -0
  411. patchi/core/security/security_config.py +551 -0
  412. patchi/core/security/security_probe.py +762 -0
  413. patchi/core/security/security_taint.py +1005 -0
  414. patchi/core/security/sensitive_data_agent.py +271 -0
  415. patchi/core/security/service_mesh_agent.py +412 -0
  416. patchi/core/security/session_management_agent.py +245 -0
  417. patchi/core/security/ssrf_agent.py +422 -0
  418. patchi/core/security/supply_chain.py +449 -0
  419. patchi/core/security/threat_model_generator.py +378 -0
  420. patchi/core/security/threat_model_updater.py +196 -0
  421. patchi/core/security/tool_adapters.py +135 -0
  422. patchi/core/security/tool_verify.py +173 -0
  423. patchi/core/security/websocket_security_agent.py +188 -0
  424. patchi/core/snapshot.py +242 -0
  425. patchi/core/tenant.py +386 -0
  426. patchi/core/testing/__init__.py +17 -0
  427. patchi/core/testing/_browser.py +147 -0
  428. patchi/core/testing/ads_agent.py +141 -0
  429. patchi/core/testing/api_contract_agent.py +450 -0
  430. patchi/core/testing/app_discovery_agent.py +641 -0
  431. patchi/core/testing/app_launcher.py +184 -0
  432. patchi/core/testing/browser_test_agent.py +516 -0
  433. patchi/core/testing/e2e_flow_agent.py +354 -0
  434. patchi/core/testing/flake_detector_agent.py +292 -0
  435. patchi/core/testing/gate.py +31 -0
  436. patchi/core/testing/live_test_runner.py +304 -0
  437. patchi/core/testing/live_v2/__init__.py +1 -0
  438. patchi/core/testing/live_v2/browser_pool.py +349 -0
  439. patchi/core/testing/live_v2/browser_test_runner.py +313 -0
  440. patchi/core/testing/live_v2/runner.py +626 -0
  441. patchi/core/testing/live_v2/screenshot_manager.py +474 -0
  442. patchi/core/testing/live_v2/stress_orchestrator.py +523 -0
  443. patchi/core/testing/live_v2/video_recorder.py +303 -0
  444. patchi/core/testing/regression_agent.py +302 -0
  445. patchi/core/testing/security_test_agent.py +253 -0
  446. patchi/core/testing/stress_test_agent.py +203 -0
  447. patchi/core/testing/test_agents.py +50 -0
  448. patchi/core/testing/test_config.py +194 -0
  449. patchi/core/testing/ui_accessibility_agent.py +324 -0
  450. patchi/core/testing/ui_button_agent.py +294 -0
  451. patchi/core/testing/ui_layout_agent.py +330 -0
  452. patchi/core/testing/unit_test_agent.py +1149 -0
  453. patchi/core/testing/visual_regression_agent.py +260 -0
  454. patchi/web/__init__.py +1 -0
  455. patchi/web/api/__init__.py +1 -0
  456. patchi/web/api/brain_map.py +114 -0
  457. patchi/web/api/charts.py +47 -0
  458. patchi/web/api/chat.py +224 -0
  459. patchi/web/api/cicd.py +576 -0
  460. patchi/web/api/dast_evidence.py +49 -0
  461. patchi/web/api/dev_check.py +320 -0
  462. patchi/web/api/fix.py +134 -0
  463. patchi/web/api/guard.py +65 -0
  464. patchi/web/api/hosted.py +128 -0
  465. patchi/web/api/hosted_v2.py +234 -0
  466. patchi/web/api/live_testing.py +699 -0
  467. patchi/web/api/scan.py +582 -0
  468. patchi/web/api/smart.py +111 -0
  469. patchi/web/api/tenant.py +117 -0
  470. patchi/web/api/tools.py +85 -0
  471. patchi/web/api_legacy.py +2401 -0
  472. patchi/web/app.py +310 -0
  473. patchi/web/events.py +483 -0
  474. patchi/web/routes/__init__.py +1 -0
  475. patchi/web/routes/assurance.py +541 -0
  476. patchi/web/routes/brain.py +36 -0
  477. patchi/web/routes/charter.py +227 -0
  478. patchi/web/routes/chat.py +23 -0
  479. patchi/web/routes/dashboard.py +558 -0
  480. patchi/web/routes/dashboard_v2.py +402 -0
  481. patchi/web/routes/findings.py +330 -0
  482. patchi/web/routes/guard.py +35 -0
  483. patchi/web/routes/history.py +126 -0
  484. patchi/web/routes/landing.py +52 -0
  485. patchi/web/routes/live_testing.py +24 -0
  486. patchi/web/routes/review.py +57 -0
  487. patchi/web/routes/self_improvement.py +110 -0
  488. patchi/web/routes/settings.py +49 -0
  489. patchi/web/routes/tokens.py +29 -0
  490. patchi/web/spawn.py +139 -0
  491. patchi/web/ws.py +141 -0
  492. patchi-0.7.5.dist-info/METADATA +315 -0
  493. patchi-0.7.5.dist-info/RECORD +496 -0
  494. patchi-0.7.5.dist-info/WHEEL +5 -0
  495. patchi-0.7.5.dist-info/licenses/LICENSE +60 -0
  496. patchi-0.7.5.dist-info/top_level.txt +1 -0
@@ -0,0 +1,135 @@
1
+ """
2
+ Tool Adapters — shared normalization for external SAST tools (bandit, pysa, codeql).
3
+
4
+ Every wrapper agent used to hand-build Finding objects and crash on the
5
+ first finding (Severity("MEDIUM") vs lowercase enum; nonexistent
6
+ ``confidence=`` kwarg). This module is the single honest conversion layer:
7
+
8
+ normalize_severity(raw) -> Severity (every known convention)
9
+ make_tool_finding(...) -> Finding (valid fields, provenance kept)
10
+
11
+ Max-potential details preserved instead of discarded:
12
+ - the TOOL's own confidence tier (bandit HIGH/MEDIUM/LOW) lands in
13
+ ``extra["tool_confidence"]`` so the ConfidenceGate can weigh it;
14
+ - a code snippet rides along in ``code_snippet`` — the gate's
15
+ method-precision scorer needs it, and AI review gets real context;
16
+ - numeric SARIF security-severity (0.0-10.0) maps to severity bands.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ from typing import Any
22
+
23
+ from patchi.core.agents.base import Finding, Severity
24
+
25
+ # ── Severity normalization ───────────────────────────────────────────────────
26
+
27
+ _WORD_MAP: dict[str, str] = {
28
+ # bandit / generic uppercase
29
+ "LOW": "low",
30
+ "MEDIUM": "medium",
31
+ "HIGH": "high",
32
+ "CRITICAL": "critical",
33
+ # already-lowercase passthrough
34
+ "low": "low",
35
+ "medium": "medium",
36
+ "high": "high",
37
+ "critical": "critical",
38
+ "info": "info",
39
+ "informational": "info",
40
+ # compiler-style levels (pysa/codeql text output)
41
+ "ERROR": "high",
42
+ "WARNING": "medium",
43
+ "WARN": "medium",
44
+ "NOTE": "info",
45
+ "NOTICE": "info",
46
+ "SEGMENT": "info",
47
+ }
48
+
49
+ _DEFAULT = "medium"
50
+
51
+
52
+ def normalize_severity(raw: Any, default: str = _DEFAULT) -> Severity:
53
+ """Map any tool's severity spelling to our Severity enum. Never raises."""
54
+ if isinstance(raw, (int, float)):
55
+ return Severity(_numeric_band(float(raw)))
56
+ value = str(raw).strip() if raw is not None else ""
57
+ mapped = _WORD_MAP.get(value)
58
+ if mapped is None:
59
+ # last resort: case-insensitive lookup
60
+ mapped = _WORD_MAP.get(value.upper(), default)
61
+ if mapped not in ("low", "medium", "high", "critical", "info"):
62
+ mapped = default
63
+ try:
64
+ return Severity(mapped)
65
+ except ValueError:
66
+ return Severity(default)
67
+
68
+
69
+ def _numeric_band(score_0_to_10: float) -> str:
70
+ """CodeQL SARIF security-severity banding."""
71
+ if score_0_to_10 >= 9.0:
72
+ return "critical"
73
+ if score_0_to_10 >= 7.0:
74
+ return "high"
75
+ if score_0_to_10 >= 4.0:
76
+ return "medium"
77
+ return "low"
78
+
79
+
80
+ # ── Tool confidence tiers ────────────────────────────────────────────────────
81
+
82
+ _TIER_MAP = {"HIGH": 0.9, "MEDIUM": 0.6, "LOW": 0.3}
83
+
84
+
85
+ def tool_confidence(raw: Any, default: float = 0.5) -> float:
86
+ """Bandit-style HIGH/MEDIUM/LOW tier -> float; passthrough numbers."""
87
+ if isinstance(raw, (int, float)):
88
+ return max(0.0, min(1.0, float(raw)))
89
+ return _TIER_MAP.get(str(raw).strip().upper(), default)
90
+
91
+
92
+ # ── Finding construction ─────────────────────────────────────────────────────
93
+
94
+
95
+ def make_tool_finding(
96
+ agent: str,
97
+ ftype: str,
98
+ raw_severity: Any,
99
+ file: str,
100
+ line: int,
101
+ message: str,
102
+ cwe: str = "",
103
+ snippet: str = "",
104
+ confidence_raw: Any = None,
105
+ extra: dict | None = None,
106
+ ) -> Finding:
107
+ """Build one valid Finding from a raw tool record. Never raises for data."""
108
+ payload_extra: dict = {"tool": agent}
109
+ conf = None
110
+ if confidence_raw is not None:
111
+ conf = tool_confidence(confidence_raw)
112
+ payload_extra["tool_confidence"] = conf
113
+ if extra:
114
+ payload_extra.update(extra)
115
+
116
+ # Normalize bare-number / int CWEs ("89", 89) to "CWE-89"
117
+ cwe_str = str(cwe or "").strip()
118
+ if cwe_str and cwe_str.isdigit():
119
+ cwe_str = f"CWE-{int(cwe_str)}"
120
+
121
+ kwargs: dict[str, Any] = {}
122
+ if snippet:
123
+ kwargs["code_snippet"] = str(snippet)[:500]
124
+
125
+ return Finding(
126
+ agent=agent,
127
+ type=(ftype or f"{agent.lower()}_finding")[:80],
128
+ severity=normalize_severity(raw_severity),
129
+ file=file or "",
130
+ line=int(line or 0),
131
+ message=str(message or "")[:400],
132
+ cwe=cwe_str,
133
+ extra=payload_extra,
134
+ **kwargs,
135
+ )
@@ -0,0 +1,173 @@
1
+ """
2
+ Tool verification — close the fix loop with the same SAST tools that found the bug.
3
+
4
+ After the AutoFixer writes a patch, we want real evidence the vulnerability is
5
+ gone, not a simulated "True". This module runs Bandit or Semgrep against the
6
+ *patched* file and reports whether the original finding still appears.
7
+
8
+ Both tools are invoked through their existing Patchi agents so we inherit the
9
+ exact same severity/confidence/CWE normalization the scanner uses elsewhere.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import logging
15
+ import shutil
16
+ import tempfile
17
+ from pathlib import Path
18
+
19
+ from patchi.core.agents.base import AgentInput, Finding, Severity
20
+
21
+ _log = logging.getLogger("patchi.security.tool_verify")
22
+
23
+ # Map the playbook `deterministic_tool` name -> registered agent name.
24
+ _TOOL_AGENT = {
25
+ "bandit": "BanditAgent",
26
+ "semgrep": "SemgrepAgent",
27
+ }
28
+
29
+
30
+ def _extract_identity(finding: Finding | object) -> tuple[str, str]:
31
+ """Return (cwe, type) for a Finding or CorrelatedFinding-like object."""
32
+ cwe = ""
33
+ ftype = ""
34
+ for attr in ("cwe", "type"):
35
+ if hasattr(finding, attr):
36
+ val = getattr(finding, attr)
37
+ if attr == "cwe":
38
+ cwe = str(val or "")
39
+ else:
40
+ ftype = str(val or "")
41
+ elif hasattr(finding, "finding"):
42
+ val = getattr(finding.finding, attr, "")
43
+ if attr == "cwe":
44
+ cwe = str(val or "")
45
+ else:
46
+ ftype = str(val or "")
47
+ return cwe, ftype
48
+
49
+
50
+ def run_tool_on_file(tool: str, file_path: Path) -> list[Finding]:
51
+ """Run one SAST tool against a single file via an isolated temp copy.
52
+
53
+ Copying the file into a temp dir keeps the scan scoped to just that file
54
+ (fast, no project-wide noise) while still exercising the real tool binary
55
+ through its Patchi agent wrapper.
56
+ """
57
+ import patchi.core.security.security_agents # noqa: F401 (registers agents)
58
+ from patchi.core.agents.base import list_agents
59
+
60
+ agent_name = _TOOL_AGENT.get(tool.lower())
61
+ if not agent_name:
62
+ raise ValueError(f"tool_verify: unsupported tool {tool!r}")
63
+
64
+ agents = {a.name: a for a in list_agents("security")}
65
+ cls = agents.get(agent_name)
66
+ if not cls:
67
+ _log.warning("tool_verify: agent %s not available", agent_name)
68
+ return []
69
+
70
+ src = Path(file_path)
71
+ if not src.exists():
72
+ return []
73
+
74
+ with tempfile.TemporaryDirectory() as tmp:
75
+ tmp_path = Path(tmp)
76
+ dest = tmp_path / src.name
77
+ shutil.copy2(src, dest)
78
+ res = cls().run(AgentInput(root=tmp_path, scope=[], brain={}, config={}, extra={}))
79
+ return list(res.findings or [])
80
+
81
+
82
+ def finding_resolved(tool: str, file_path: Path, original: Finding | object) -> bool:
83
+ """True if `original` no longer appears in the tool's output for the file.
84
+
85
+ Matching prefers CWE (most specific). When the tool reports no findings at
86
+ all we treat the issue as resolved.
87
+ """
88
+ findings = run_tool_on_file(tool, file_path)
89
+ if not findings:
90
+ return True
91
+
92
+ orig_cwe, orig_type = _extract_identity(original)
93
+ orig_short = orig_type.split(".")[-1] if orig_type else ""
94
+
95
+ for f in findings:
96
+ if orig_cwe and f.cwe and f.cwe.upper() == orig_cwe.upper():
97
+ return False
98
+ if orig_short and f.type and orig_short in f.type:
99
+ return False
100
+ return True
101
+
102
+
103
+ def high_findings_on_file(file_path: Path) -> list[Finding]:
104
+ """Run Bandit + Semgrep on a single file; return only HIGH/CRITICAL findings.
105
+
106
+ Used to verify that an applied proactive fix did not introduce or leave a
107
+ serious vulnerability. Returns an empty list on tool failure (fail-open) so
108
+ a slow/missing tool never blocks the watch loop. The returned findings carry
109
+ the real (non-temp) file path.
110
+ """
111
+ out: list[Finding] = []
112
+ real = str(file_path)
113
+ for tool in ("bandit", "semgrep"):
114
+ try:
115
+ findings = run_tool_on_file(tool, file_path)
116
+ except Exception as e:
117
+ _log.warning("high_findings_on_file: %s failed on %s: %s", tool, file_path, e)
118
+ continue
119
+ for f in findings:
120
+ if f.severity in (Severity.HIGH, Severity.CRITICAL):
121
+ try:
122
+ f.file = real
123
+ except Exception as _exc:
124
+ _log.warning('high_findings_on_file failed: %s', _exc)
125
+ out.append(f)
126
+ return out
127
+
128
+
129
+ def verify_proactive_fixes(
130
+ root: Path,
131
+ applied_files: list[str],
132
+ pre_highs: dict[str, set],
133
+ ) -> list[dict]:
134
+ """Diff SAST HIGH/CRITICAL findings before vs after applied proactive fixes.
135
+
136
+ For each file a proactive fix touched, re-runs Bandit + Semgrep and reports
137
+ only findings that are NEW relative to ``pre_highs`` (so pre-existing
138
+ findings are never re-flagged on every save). Returns a list of issue dicts
139
+ ready for ``mem.save_issue``. Fail-open: any tool failure yields no issue.
140
+ """
141
+ issues: list[dict] = []
142
+ for vf in sorted(set(applied_files)):
143
+ vpath = Path(root) / vf
144
+ if not vpath.exists() or vpath.suffix != ".py":
145
+ continue
146
+ try:
147
+ post_findings = high_findings_on_file(vpath)
148
+ except Exception as e:
149
+ _log.warning("verify_proactive_fixes skipped %s: %s", vf, e)
150
+ continue
151
+ post = {(h.type, h.cwe) for h in post_findings}
152
+ new_sigs = post - pre_highs.get(vf, set())
153
+ if not new_sigs:
154
+ continue
155
+ for hf in post_findings:
156
+ if (hf.type, hf.cwe) not in new_sigs:
157
+ continue
158
+ issues.append(
159
+ {
160
+ "source": "proactive_sast_verify",
161
+ "fix_type": "sast_regression",
162
+ "file": vf,
163
+ "name": hf.cwe or hf.type,
164
+ "line": hf.line,
165
+ "description": (
166
+ f"Proactive fix introduced a HIGH/CRITICAL SAST "
167
+ f"finding ({hf.type}{' ' + hf.cwe if hf.cwe else ''}) "
168
+ f"at line {hf.line}"
169
+ ),
170
+ "severity": "high",
171
+ }
172
+ )
173
+ return issues
@@ -0,0 +1,188 @@
1
+ """
2
+ WebSocketSecurityAgent — WebSocket security vulnerability detection.
3
+
4
+ Detects:
5
+ - Unencrypted WebSocket connections (ws:// instead of wss://)
6
+ - Missing origin validation
7
+ - Unvalidated message input (injection via WebSocket messages)
8
+ - Missing authentication in WebSocket handshake
9
+ - Unlimited message size / rate
10
+ - WebSocket message type confusion
11
+ - Sensitive data in WebSocket messages
12
+
13
+ Uses static pattern matching, NOT AI.
14
+ Does NOT write to disk. Does NOT touch the queue.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import re
20
+
21
+ from ..agents.base import (
22
+ AgentDomain,
23
+ AgentGroup,
24
+ AgentInput,
25
+ AgentResult,
26
+ BaseAgent,
27
+ Finding,
28
+ Severity,
29
+ register,
30
+ safe_rglob,
31
+ )
32
+
33
+
34
+ @register
35
+ class WebSocketSecurityAgent(BaseAgent):
36
+ """Detects WebSocket security vulnerabilities."""
37
+
38
+ group = AgentGroup.SECURITY
39
+ domain = AgentDomain.SECURITY
40
+ name = "WebSocketSecurityAgent"
41
+ description = "WebSocket injection, origin validation, unencrypted WS, message validation"
42
+
43
+ def _run(self, inp: AgentInput, result: AgentResult) -> None:
44
+ # Only scan files likely to contain WebSocket code (not every .py in the project)
45
+ patterns = ["*.py", "*.js", "*.ts", "*.jsx", "*.tsx", "*.java", "*.go"]
46
+
47
+ # Pre-scan: skip files that lack any WebSocket keyword (fast path)
48
+ ws_guard = re.compile(
49
+ r"(?i)(?:websocket|wss?://|socket\.io|fastapi\.websocket|starlette\.websocket|flask-socketio|django\.channels|aiohttp\.web\.WebSocketResponse|tornado\.websocket|\.ws\.route)",
50
+ re.I,
51
+ )
52
+ for pat in patterns:
53
+ for fpath in safe_rglob(inp.root, pat):
54
+ if not fpath.is_file():
55
+ continue
56
+ rel = fpath.relative_to(inp.root).as_posix()
57
+ try:
58
+ content = fpath.read_text(encoding="utf-8", errors="ignore")
59
+ except OSError:
60
+ continue
61
+ if not ws_guard.search(content):
62
+ continue
63
+ result.files_scanned += 1
64
+ self._scan_websocket(content, rel, result)
65
+
66
+ def _scan_websocket(self, content: str, rel: str, result: AgentResult) -> None:
67
+ # ── Unencrypted WebSocket connection ────────────────────────────────
68
+ for m in re.finditer(r"""(?i)["']ws://""", content):
69
+ line = content[: m.start()].count("\n") + 1
70
+ result.add_finding(
71
+ Finding(
72
+ agent=self.name,
73
+ type="unencrypted_websocket",
74
+ severity=Severity.HIGH,
75
+ file=rel,
76
+ line=line,
77
+ message="Unencrypted WebSocket connection (ws://) — use wss:// for production",
78
+ suggestion="Replace ws:// with wss:// and ensure TLS is configured",
79
+ cwe="CWE-319",
80
+ extra={"skill": "api-security.skill"},
81
+ )
82
+ )
83
+
84
+ # ── Missing origin validation ───────────────────────────────────────
85
+ if not re.search(
86
+ r"(?i)(?:\borigin\b|check_origin|allowed_origins|validate_origin)", content
87
+ ):
88
+ result.add_finding(
89
+ Finding(
90
+ agent=self.name,
91
+ type="missing_origin_validation",
92
+ severity=Severity.MEDIUM,
93
+ file=rel,
94
+ line=0,
95
+ message="WebSocket handler lacks origin validation — potential cross-origin hijacking",
96
+ suggestion="Validate Origin header on WebSocket upgrade, configure allowed_origins",
97
+ cwe="CWE-346",
98
+ extra={"skill": "api-security.skill"},
99
+ )
100
+ )
101
+
102
+ # ── Message without input validation ───────────────────────────────
103
+ for m in re.finditer(
104
+ r'(?i)(?:websocket|socketio)\.(?:on_message|receive|recv|on\s*\(\s*["\']message)',
105
+ content,
106
+ ):
107
+ line_start = content[: m.start()].count("\n") + 1
108
+ surrounding = content[m.start() : m.start() + 500]
109
+ if not re.search(
110
+ r"(?i)(?:validate|sanitize|check|json\.loads|parse)", surrounding[:200]
111
+ ):
112
+ result.add_finding(
113
+ Finding(
114
+ agent=self.name,
115
+ type="websocket_no_input_validation",
116
+ severity=Severity.HIGH,
117
+ file=rel,
118
+ line=line_start,
119
+ message="WebSocket message processed without input validation — injection risk",
120
+ suggestion="Validate/sanitize all WebSocket message content before processing",
121
+ cwe="CWE-20",
122
+ extra={"skill": "api-security.skill"},
123
+ )
124
+ )
125
+
126
+ # ── Missing authentication on WebSocket upgrade ────────────────────
127
+ for m in re.finditer(
128
+ r"(?i)(?:@(?:ws|websocket)\.route|\bdef\s+websocket\b|\basync\s+def\s+websocket\b|socketio\.on\b)",
129
+ content,
130
+ ):
131
+ line_start = content[: m.start()].count("\n") + 1
132
+ surrounding = content[m.start() : m.start() + 300]
133
+ if not re.search(
134
+ r"(?i)(?:auth|token|jwt|session|api_key|authenticate)", surrounding[:150]
135
+ ):
136
+ result.add_finding(
137
+ Finding(
138
+ agent=self.name,
139
+ type="websocket_no_auth",
140
+ severity=Severity.CRITICAL,
141
+ file=rel,
142
+ line=line_start,
143
+ message="WebSocket endpoint lacks authentication — unauthenticated access",
144
+ suggestion="Add authentication handshake (JWT, session, or API key) to WebSocket upgrade",
145
+ cwe="CWE-287",
146
+ extra={"skill": "api-security.skill"},
147
+ )
148
+ )
149
+
150
+ # ── Message size / rate limiting missing ───────────────────────────
151
+ for m in re.finditer(
152
+ r'(?i)(?:@app\.ws|\bwebsocket_route\b|\.on\s*\(\s*["\']?connect)', content
153
+ ):
154
+ line_start = content[: m.start()].count("\n") + 1
155
+ surrounding = content[m.start() : m.start() + 300]
156
+ if not re.search(r"(?i)(?:max_size|max_length|limit|throttle|rate)", surrounding[:150]):
157
+ result.add_finding(
158
+ Finding(
159
+ agent=self.name,
160
+ type="websocket_no_limit",
161
+ severity=Severity.MEDIUM,
162
+ file=rel,
163
+ line=line_start,
164
+ message="WebSocket missing message size/rate limiting — DoS vector",
165
+ suggestion="Configure max_message_size and rate limiting for WebSocket messages",
166
+ cwe="CWE-770",
167
+ extra={"skill": "api-security.skill"},
168
+ )
169
+ )
170
+
171
+ # ── Sensitive data sent over WebSocket ─────────────────────────────
172
+ for m in re.finditer(
173
+ r'(?i)(?:send|emit)\(.*["\']?(?:\bpassword\b|\btoken\b|\bsecret\b|api_key|secret_key|\bssn\b|\bcredit\b)',
174
+ content,
175
+ ):
176
+ result.add_finding(
177
+ Finding(
178
+ agent=self.name,
179
+ type="sensitive_data_in_websocket",
180
+ severity=Severity.HIGH,
181
+ file=rel,
182
+ line=content[: m.start()].count("\n") + 1,
183
+ message="Sensitive data potentially sent over WebSocket — data leak risk",
184
+ suggestion="Avoid sending secrets/tokens over WebSocket; use server-side operations",
185
+ cwe="CWE-200",
186
+ extra={"skill": "api-security.skill"},
187
+ )
188
+ )