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,523 @@
1
+ """
2
+ Stress Orchestrator — Load testing engine for web applications.
3
+
4
+ Supports:
5
+ - Load testing (steady state)
6
+ - Spike testing (sudden bursts)
7
+ - Soak testing (long duration)
8
+ - Breakpoint testing (find limits)
9
+ - Custom scenarios
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import asyncio
15
+ import logging
16
+ import random
17
+ import statistics
18
+ import time
19
+ from collections.abc import Callable
20
+ from dataclasses import dataclass, field
21
+ from datetime import UTC, datetime
22
+ from typing import Any
23
+
24
+ _log = logging.getLogger("patchi.testing.stress_orchestrator")
25
+
26
+
27
+ @dataclass
28
+ class StressConfig:
29
+ """Configuration for a stress test."""
30
+
31
+ base_url: str
32
+ scenario: str = "load" # load, spike, soak, breakpoint
33
+ users: int = 10
34
+ duration_seconds: int = 60
35
+ ramp_up_seconds: int = 10
36
+ # Spike config
37
+ spike_multiplier: float = 3.0
38
+ spike_duration_seconds: int = 10
39
+ # Soak config
40
+ soak_check_interval: int = 300 # seconds
41
+ # Breakpoint config
42
+ max_users: int = 1000
43
+ step_users: int = 50
44
+ step_duration: int = 30
45
+ # Request config
46
+ requests_per_second: float | None = None # None = as fast as possible
47
+ think_time_ms: int = 100
48
+ # Endpoints to test
49
+ endpoints: list[dict] = field(default_factory=list)
50
+ # Custom scenario function
51
+ custom_scenario: Callable | None = None
52
+
53
+
54
+ @dataclass
55
+ class RequestResult:
56
+ """Result of a single HTTP request."""
57
+
58
+ timestamp: float
59
+ method: str
60
+ url: str
61
+ status_code: int
62
+ response_time_ms: float
63
+ success: bool
64
+ error: str = ""
65
+ response_size: int = 0
66
+
67
+
68
+ @dataclass
69
+ class UserSession:
70
+ """A single virtual user's session."""
71
+
72
+ user_id: int
73
+ start_time: float
74
+ results: list[RequestResult] = field(default_factory=list)
75
+ active: bool = True
76
+
77
+
78
+ @dataclass
79
+ class StressTestReport:
80
+ """Complete stress test report."""
81
+
82
+ config: StressConfig
83
+ started_at: str
84
+ completed_at: str
85
+ duration_seconds: float
86
+ total_requests: int
87
+ successful_requests: int
88
+ failed_requests: int
89
+ requests_per_second: float
90
+ latency: dict # min, max, mean, median, p50, p90, p95, p99
91
+ status_codes: dict[str, int]
92
+ errors: dict[str, int]
93
+ throughput_over_time: list[dict] # time series
94
+ latency_over_time: list[dict]
95
+ user_sessions: int
96
+ peak_users: int
97
+ # Scenario-specific
98
+ breakpoint_found: bool = False
99
+ breakpoint_users: int = 0
100
+ soak_stability: bool = True
101
+
102
+ def to_dict(self) -> dict:
103
+ import dataclasses
104
+ return dataclasses.asdict(self)
105
+
106
+
107
+ class StressOrchestrator:
108
+ """
109
+ Orchestrates load/stress tests against a web application.
110
+
111
+ Usage:
112
+ config = StressConfig(
113
+ base_url="https://api.example.com",
114
+ scenario="spike",
115
+ users=100,
116
+ duration_seconds=60,
117
+ )
118
+ orchestrator = StressOrchestrator(config)
119
+ report = await orchestrator.run()
120
+ """
121
+
122
+ def __init__(
123
+ self,
124
+ config: StressConfig,
125
+ on_progress: Callable[[str], None] = None,
126
+ ):
127
+ self.config = config
128
+ self.on_progress = on_progress or (lambda _: None)
129
+ self._session: Any = None
130
+ self._users: list[UserSession] = []
131
+ self._running = False
132
+ self._start_time = 0
133
+ self._results: list[RequestResult] = []
134
+ self._time_series: list[dict] = []
135
+
136
+ async def run(self) -> StressTestReport:
137
+ """Run the stress test based on scenario."""
138
+ self._start_time = time.time()
139
+ started_at = datetime.now(UTC).isoformat()
140
+
141
+ # Initialize HTTP session
142
+ await self._init_session()
143
+
144
+ try:
145
+ if self.config.scenario == "load":
146
+ await self._run_load_test()
147
+ elif self.config.scenario == "spike":
148
+ await self._run_spike_test()
149
+ elif self.config.scenario == "soak":
150
+ await self._run_soak_test()
151
+ elif self.config.scenario == "breakpoint":
152
+ await self._run_breakpoint_test()
153
+ elif self.config.scenario == "custom" and self.config.custom_scenario:
154
+ await self.config.custom_scenario(self)
155
+ else:
156
+ raise ValueError(f"Unknown scenario: {self.config.scenario}")
157
+ finally:
158
+ await self._close_session()
159
+
160
+ completed_at = datetime.now(UTC).isoformat()
161
+ duration = time.time() - self._start_time
162
+
163
+ return self._generate_report(started_at, completed_at, duration)
164
+
165
+ async def _init_session(self):
166
+ """Initialize aiohttp session."""
167
+ try:
168
+ import aiohttp
169
+
170
+ self._session = aiohttp.ClientSession(
171
+ timeout=aiohttp.ClientTimeout(total=30),
172
+ connector=aiohttp.TCPConnector(limit=1000),
173
+ )
174
+ except ImportError:
175
+ _log.error("aiohttp not installed. Install with: pip install aiohttp")
176
+ raise
177
+
178
+ async def _close_session(self):
179
+ """Close aiohttp session."""
180
+ if self._session:
181
+ await self._session.close()
182
+
183
+ async def _run_load_test(self):
184
+ """Run steady-state load test."""
185
+ self.on_progress(
186
+ f"🚀 Starting load test: {self.config.users} users for {self.config.duration_seconds}s"
187
+ )
188
+
189
+ # Ramp up users
190
+ await self._ramp_up_users(self.config.users, self.config.ramp_up_seconds)
191
+
192
+ # Steady state
193
+ steady_duration = self.config.duration_seconds - self.config.ramp_up_seconds
194
+ if steady_duration > 0:
195
+ await self._run_steady_state(steady_duration)
196
+
197
+ # Ramp down
198
+ await self._ramp_down_users()
199
+
200
+ async def _run_spike_test(self):
201
+ """Run spike test: baseline -> spike -> baseline."""
202
+ self.on_progress(
203
+ f"⚡ Starting spike test: {self.config.users} baseline -> {int(self.config.users * self.config.spike_multiplier)} spike"
204
+ )
205
+
206
+ # Baseline
207
+ await self._ramp_up_users(self.config.users, self.config.ramp_up_seconds)
208
+ baseline_duration = (self.config.duration_seconds - self.config.spike_duration_seconds) // 2
209
+ await self._run_steady_state(baseline_duration)
210
+
211
+ # Spike
212
+ spike_users = int(self.config.users * self.config.spike_multiplier)
213
+ self.on_progress(f"📈 Spiking to {spike_users} users")
214
+ await self._ramp_up_users(spike_users, 5) # Fast ramp
215
+ await self._run_steady_state(self.config.spike_duration_seconds)
216
+
217
+ # Return to baseline
218
+ self.on_progress("📉 Returning to baseline")
219
+ await self._ramp_down_users(spike_users - self.config.users)
220
+ await self._run_steady_state(baseline_duration)
221
+
222
+ # Final ramp down
223
+ await self._ramp_down_users()
224
+
225
+ async def _run_soak_test(self):
226
+ """Run long-duration soak test."""
227
+ self.on_progress(
228
+ f"🏃 Starting soak test: {self.config.users} users for {self.config.duration_seconds}s"
229
+ )
230
+
231
+ await self._ramp_up_users(self.config.users, self.config.ramp_up_seconds)
232
+
233
+ # Run with periodic checks
234
+ elapsed = 0
235
+ check_interval = self.config.soak_check_interval
236
+
237
+ while elapsed < self.config.duration_seconds - self.config.ramp_up_seconds:
238
+ remaining = min(
239
+ check_interval, self.config.duration_seconds - self.config.ramp_up_seconds - elapsed
240
+ )
241
+ await self._run_steady_state(remaining)
242
+ elapsed += remaining
243
+
244
+ # Check for degradation
245
+ await self._check_soak_health()
246
+
247
+ await self._ramp_down_users()
248
+
249
+ async def _run_breakpoint_test(self):
250
+ """Run breakpoint test: gradually increase load until failure."""
251
+ self.on_progress(f"🔍 Starting breakpoint test: up to {self.config.max_users} users")
252
+
253
+ current_users = self.config.step_users
254
+ breakpoint_found = False
255
+
256
+ while current_users <= self.config.max_users and not breakpoint_found:
257
+ self.on_progress(f" Testing {current_users} users...")
258
+
259
+ await self._ramp_up_users(current_users, self.config.ramp_up_seconds)
260
+ await self._run_steady_state(self.config.step_duration)
261
+
262
+ # Check if system is degrading
263
+ if await self._check_breakpoint():
264
+ breakpoint_found = True
265
+ self.on_progress(f"💥 Breakpoint found at {current_users} users")
266
+ break
267
+
268
+ # Ramp down before next step
269
+ await self._ramp_down_users()
270
+ current_users += self.config.step_users
271
+
272
+ if not breakpoint_found:
273
+ self.on_progress(f"✅ No breakpoint found up to {self.config.max_users} users")
274
+
275
+ async def _ramp_up_users(self, target_users: int, duration: float):
276
+ """Gradually ramp up virtual users."""
277
+ if target_users <= len(self._users):
278
+ return
279
+
280
+ users_to_add = target_users - len(self._users)
281
+ interval = duration / users_to_add if users_to_add > 0 else 0
282
+
283
+ for _i in range(users_to_add):
284
+ user_id = len(self._users) + 1
285
+ self._users.append(UserSession(user_id=user_id, start_time=time.time()))
286
+
287
+ # Start user task
288
+ asyncio.create_task(self._run_user(user_id))
289
+
290
+ if interval > 0:
291
+ await asyncio.sleep(interval)
292
+
293
+ self.on_progress(f" Ramped up to {target_users} users")
294
+
295
+ async def _ramp_down_users(self, count: int = None):
296
+ """Ramp down virtual users."""
297
+ if count is None:
298
+ count = len(self._users)
299
+
300
+ for _ in range(min(count, len(self._users))):
301
+ if self._users:
302
+ user = self._users.pop()
303
+ user.active = False
304
+
305
+ self.on_progress(f" Ramped down to {len(self._users)} users")
306
+
307
+ async def _run_steady_state(self, duration: float):
308
+ """Run steady state for specified duration."""
309
+ end_time = time.time() + duration
310
+
311
+ while time.time() < end_time and self._running:
312
+ # Collect time series data
313
+ await self._collect_time_series()
314
+ await asyncio.sleep(1)
315
+
316
+ async def _run_user(self, user_id: int):
317
+ """Run a single virtual user's workload."""
318
+ user = next((u for u in self._users if u.user_id == user_id), None)
319
+ if not user:
320
+ return
321
+
322
+ endpoints = self.config.endpoints or [{"method": "GET", "path": "/"}]
323
+
324
+ while user.active and self._running:
325
+ # Select endpoint
326
+ endpoint = random.choice(endpoints)
327
+ method = endpoint.get("method", "GET")
328
+ path = endpoint.get("path", "/")
329
+ url = f"{self.config.base_url.rstrip('/')}{path}"
330
+
331
+ # Execute request
332
+ result = await self._make_request(method, url)
333
+ user.results.append(result)
334
+ self._results.append(result)
335
+
336
+ # Think time
337
+ if self.config.think_time_ms > 0:
338
+ await asyncio.sleep(self.config.think_time_ms / 1000)
339
+
340
+ # Rate limiting
341
+ if self.config.requests_per_second:
342
+ await asyncio.sleep(1.0 / self.config.requests_per_second)
343
+
344
+ async def _make_request(self, method: str, url: str) -> RequestResult:
345
+ """Make an HTTP request and record result."""
346
+ start = time.time()
347
+
348
+ try:
349
+ async with self._session.request(method, url) as response:
350
+ await response.read()
351
+ response_time = (time.time() - start) * 1000
352
+
353
+ return RequestResult(
354
+ timestamp=time.time(),
355
+ method=method,
356
+ url=url,
357
+ status_code=response.status,
358
+ response_time_ms=response_time,
359
+ success=200 <= response.status < 400,
360
+ response_size=len(response.body) if hasattr(response, "body") else 0,
361
+ )
362
+ except Exception as e:
363
+ response_time = (time.time() - start) * 1000
364
+ return RequestResult(
365
+ timestamp=time.time(),
366
+ method=method,
367
+ url=url,
368
+ status_code=0,
369
+ response_time_ms=response_time,
370
+ success=False,
371
+ error=str(e),
372
+ )
373
+
374
+ async def _collect_time_series(self):
375
+ """Collect time series metrics."""
376
+ now = time.time()
377
+ recent = [r for r in self._results if now - r.timestamp < 5]
378
+
379
+ if recent:
380
+ latencies = [r.response_time_ms for r in recent]
381
+ self._time_series.append(
382
+ {
383
+ "timestamp": now,
384
+ "requests_per_sec": len(recent) / 5,
385
+ "latency_p50": statistics.median(latencies),
386
+ "latency_p95": self._percentile(latencies, 95),
387
+ "error_rate": sum(1 for r in recent if not r.success) / len(recent),
388
+ "active_users": len([u for u in self._users if u.active]),
389
+ }
390
+ )
391
+
392
+ async def _check_soak_health(self) -> bool:
393
+ """Check system health during soak test."""
394
+ # Check for memory leaks, error rate increase, latency degradation
395
+ recent = [r for r in self._results if time.time() - r.timestamp < 60]
396
+ if not recent:
397
+ return True
398
+
399
+ error_rate = sum(1 for r in recent if not r.success) / len(recent)
400
+ if error_rate > 0.05: # 5% error rate
401
+ self.on_progress(f"⚠️ High error rate: {error_rate:.1%}")
402
+ return False
403
+
404
+ latencies = [r.response_time_ms for r in recent]
405
+ p99 = self._percentile(latencies, 99)
406
+ if p99 > 10000: # 10 seconds
407
+ self.on_progress(f"⚠️ High latency P99: {p99:.0f}ms")
408
+ return False
409
+
410
+ return True
411
+
412
+ async def _check_breakpoint(self) -> bool:
413
+ """Check if breakpoint has been reached."""
414
+ recent = [r for r in self._results if time.time() - r.timestamp < 10]
415
+ if not recent:
416
+ return False
417
+
418
+ error_rate = sum(1 for r in recent if not r.success) / len(recent)
419
+ if error_rate > 0.10: # 10% error rate
420
+ return True
421
+
422
+ latencies = [r.response_time_ms for r in recent]
423
+ p99 = self._percentile(latencies, 99)
424
+ if p99 > 30000: # 30 seconds
425
+ return True
426
+
427
+ return False
428
+
429
+ def _percentile(self, data: list[float], percentile: int) -> float:
430
+ """Calculate percentile."""
431
+ if not data:
432
+ return 0
433
+ sorted_data = sorted(data)
434
+ index = int(len(sorted_data) * percentile / 100)
435
+ return sorted_data[min(index, len(sorted_data) - 1)]
436
+
437
+ def _generate_report(
438
+ self,
439
+ started_at: str,
440
+ completed_at: str,
441
+ duration: float,
442
+ ) -> StressTestReport:
443
+ """Generate final stress test report."""
444
+ if not self._results:
445
+ return StressTestReport(
446
+ config=self.config,
447
+ started_at=started_at,
448
+ completed_at=completed_at,
449
+ duration_seconds=duration,
450
+ total_requests=0,
451
+ successful_requests=0,
452
+ failed_requests=0,
453
+ requests_per_second=0,
454
+ latency={},
455
+ status_codes={},
456
+ errors={},
457
+ throughput_over_time=[],
458
+ latency_over_time=[],
459
+ user_sessions=len(self._users),
460
+ peak_users=max(len(self._users), 1),
461
+ )
462
+
463
+ latencies = [r.response_time_ms for r in self._results]
464
+ status_codes = {}
465
+ errors = {}
466
+
467
+ for r in self._results:
468
+ status_codes[str(r.status_code)] = status_codes.get(str(r.status_code), 0) + 1
469
+ if not r.success:
470
+ errors[r.error or "unknown"] = errors.get(r.error or "unknown", 0) + 1
471
+
472
+ return StressTestReport(
473
+ config=self.config,
474
+ started_at=started_at,
475
+ completed_at=completed_at,
476
+ duration_seconds=duration,
477
+ total_requests=len(self._results),
478
+ successful_requests=sum(1 for r in self._results if r.success),
479
+ failed_requests=sum(1 for r in self._results if not r.success),
480
+ requests_per_second=len(self._results) / duration if duration > 0 else 0,
481
+ latency={
482
+ "min": min(latencies),
483
+ "max": max(latencies),
484
+ "mean": statistics.mean(latencies),
485
+ "median": statistics.median(latencies),
486
+ "p50": self._percentile(latencies, 50),
487
+ "p90": self._percentile(latencies, 90),
488
+ "p95": self._percentile(latencies, 95),
489
+ "p99": self._percentile(latencies, 99),
490
+ },
491
+ status_codes=status_codes,
492
+ errors=errors,
493
+ throughput_over_time=[
494
+ {"timestamp": ts["timestamp"], "rps": ts["requests_per_sec"]}
495
+ for ts in self._time_series
496
+ ],
497
+ latency_over_time=[
498
+ {"timestamp": ts["timestamp"], "p50": ts["latency_p50"], "p95": ts["latency_p95"]}
499
+ for ts in self._time_series
500
+ ],
501
+ user_sessions=len(self._users),
502
+ peak_users=max(len(self._users), 1),
503
+ )
504
+
505
+
506
+ # Convenience function
507
+ async def run_stress_test(
508
+ base_url: str,
509
+ scenario: str = "load",
510
+ users: int = 10,
511
+ duration_seconds: int = 60,
512
+ **kwargs,
513
+ ) -> StressTestReport:
514
+ """Run a stress test with simple parameters."""
515
+ config = StressConfig(
516
+ base_url=base_url,
517
+ scenario=scenario,
518
+ users=users,
519
+ duration_seconds=duration_seconds,
520
+ **kwargs,
521
+ )
522
+ orchestrator = StressOrchestrator(config)
523
+ return await orchestrator.run()