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,450 @@
1
+ """
2
+ APIContractAgent — OpenAPI/JSON Schema compliance tests.
3
+
4
+ Validates API responses against:
5
+ - OpenAPI specifications
6
+ - JSON Schema definitions
7
+ - Contract-first API designs
8
+ - Generated API contracts from app contract
9
+
10
+ Tests for:
11
+ - Response structure compliance
12
+ - Type mismatches
13
+ - Missing required fields
14
+ - Extra unexpected fields
15
+ - Status code validation
16
+ - Parameter validation
17
+
18
+ Does NOT write to disk.
19
+ Does NOT call AI.
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ import json
25
+ import logging
26
+ import time
27
+ from pathlib import Path
28
+ from typing import Any
29
+
30
+ from ..agents.base import (
31
+ AgentDomain,
32
+ AgentGroup,
33
+ AgentInput,
34
+ AgentResult,
35
+ AgentStatus,
36
+ BaseAgent,
37
+ Severity,
38
+ make_finding,
39
+ register,
40
+ )
41
+
42
+ _log = logging.getLogger("patchi.testing.api_contract_agent")
43
+
44
+
45
+ @register
46
+ class APIContractAgent(BaseAgent):
47
+ """Agent for running API contract tests."""
48
+
49
+ group = AgentGroup.TEST
50
+ domain = AgentDomain.TESTING
51
+ name = "APIContractAgent"
52
+ description = "OpenAPI/JSON Schema compliance: response structure, types, status codes"
53
+
54
+ def _run(self, inp: AgentInput, result: AgentResult) -> None:
55
+ """Run API contract compliance tests."""
56
+ start_time = time.time()
57
+ findings = []
58
+
59
+ # Find API contract files
60
+ contract_files = self._find_api_contracts(inp.root)
61
+
62
+ if not contract_files:
63
+ findings.append(
64
+ make_finding(
65
+ severity=Severity.INFO,
66
+ file="__api_contract__",
67
+ line_start=0,
68
+ title="No API contracts found",
69
+ description="No OpenAPI or JSON Schema files found in project",
70
+ evidence="No .json/.yaml files with API contract indicators found",
71
+ )
72
+ )
73
+ result.status = AgentStatus.SKIPPED
74
+ result.findings = findings
75
+ result.data.update({"contracts_found": 0, "needs_ai": False})
76
+ return
77
+
78
+ # Validate contracts against running API
79
+ validation_results = self._validate_contracts(contract_files, inp)
80
+
81
+ # Process validation results
82
+ # Note: loop targets use `r`, never `result` — `result` is the
83
+ # AgentResult this _run mutates (it is NOT a local here).
84
+ total_errors = sum(len(r.get("errors", [])) for r in validation_results.values())
85
+
86
+ if total_errors == 0:
87
+ findings.append(
88
+ make_finding(
89
+ severity=Severity.INFO,
90
+ file="__api_contract_summary__",
91
+ line_start=0,
92
+ title="API Contract Validation Passed",
93
+ description="All API endpoints comply with their contracts",
94
+ evidence="No contract violations found",
95
+ )
96
+ )
97
+ else:
98
+ findings.append(
99
+ make_finding(
100
+ severity=Severity.HIGH,
101
+ file="__api_contract_summary__",
102
+ line_start=0,
103
+ title=f"API Contract Violations: {total_errors} errors",
104
+ description=f"Found {total_errors} contract violations across endpoints",
105
+ evidence=f"Violations found in {len([r for r in validation_results.values() if r.get('errors')])} endpoints",
106
+ )
107
+ )
108
+
109
+ # Add individual violation findings
110
+ for endpoint, r in validation_results.items():
111
+ for error in r.get("errors", []):
112
+ severity = self._map_contract_error_severity(error.get("type", "validation"))
113
+
114
+ findings.append(
115
+ make_finding(
116
+ severity=severity,
117
+ file=endpoint,
118
+ line_start=error.get("line", 0),
119
+ title=f"API Contract Violation: {error.get('type', 'unknown')}",
120
+ description=error.get("message", "Unknown contract violation"),
121
+ evidence=f"Expected: {error.get('expected', 'N/A')}\nActual: {error.get('actual', 'N/A')}\nPath: {error.get('path', 'N/A')}",
122
+ )
123
+ )
124
+
125
+ duration = time.time() - start_time
126
+
127
+ # NOTE: FAILED here is a *findings* signal (violations found), not a
128
+ # crash — errors stays empty by design. The per-agent smoke sweep and
129
+ # the --pipeline orchestration gate both treat FAILED as a crash, so
130
+ # an agent that legitimately finds violations must be careful to only
131
+ # do so when the project actually ships contracts (and never against
132
+ # .patchi state — see _find_api_contracts).
133
+ result.status = AgentStatus.SUCCEEDED if total_errors == 0 else AgentStatus.FAILED
134
+ result.findings = findings
135
+ result.data.update(
136
+ {
137
+ "contracts_found": len(contract_files),
138
+ "endpoints_tested": len(validation_results),
139
+ "total_violations": total_errors,
140
+ "duration": round(duration, 2),
141
+ "needs_ai": False, # Contract validation doesn't require AI
142
+ }
143
+ )
144
+ return
145
+
146
+ def _find_api_contracts(self, root: Path) -> list[Path]:
147
+ """Find API contract files (OpenAPI/Swagger/JSON Schema)."""
148
+ contract_files = []
149
+
150
+ # Look for common API contract file patterns
151
+ contract_patterns = [
152
+ "**/openapi*.json",
153
+ "**/openapi*.yaml",
154
+ "**/openapi*.yml",
155
+ "**/swagger*.json",
156
+ "**/swagger*.yaml",
157
+ "**/swagger*.yml",
158
+ "**/api*.json", # Might contain OpenAPI spec
159
+ "**/*.schema.json", # JSON Schema files
160
+ "**/schemas/**/*.json", # Schema directory
161
+ "**/specs/**/*.json", # Spec directory
162
+ "**/specs/**/*.yaml",
163
+ "**/specs/**/*.yml",
164
+ ]
165
+
166
+ for pattern in contract_patterns:
167
+ for contract_file in root.rglob(pattern):
168
+ if not contract_file.is_file():
169
+ continue
170
+ # Never treat the tool's own state directory as project source:
171
+ # on case-insensitive filesystems (Windows) the agent's own
172
+ # cache file .patchi/cache/agent_cache/APIContractAgent.json
173
+ # matches the `**/api*.json` pattern, so a re-run would
174
+ # validate its own cache and report spurious violations (found
175
+ # by the smoke-sweep --pipeline orchestration gate).
176
+ if ".patchi" in contract_file.parts:
177
+ continue
178
+ # Verify it's actually an API contract by checking content
179
+ if self._is_api_contract(contract_file):
180
+ contract_files.append(contract_file)
181
+
182
+ return contract_files
183
+
184
+ def _is_api_contract(self, file_path: Path) -> bool:
185
+ """Check if a file is actually an API contract."""
186
+ try:
187
+ content = file_path.read_text(encoding="utf-8")
188
+
189
+ # Check for OpenAPI/Swagger indicators
190
+ if "openapi" in content.lower() or "swagger" in content.lower():
191
+ return True
192
+
193
+ # Check for JSON Schema indicators
194
+ if '"$schema"' in content and (
195
+ "json-schema" in content.lower() or "jsonschema" in content.lower()
196
+ ):
197
+ return True
198
+
199
+ # For YAML files, check for OpenAPI structure
200
+ if file_path.suffix.lower() in [".yaml", ".yml"]:
201
+ # Look for common OpenAPI fields
202
+ if any(
203
+ field in content for field in ["openapi:", "swagger:", "paths:", "components:"]
204
+ ):
205
+ return True
206
+
207
+ # For JSON files, check for API contract structure
208
+ if file_path.suffix.lower() == ".json":
209
+ try:
210
+ data = json.loads(content)
211
+ # Check for OpenAPI fields
212
+ if isinstance(data, dict) and any(
213
+ field in data for field in ["openapi", "swagger", "paths", "components"]
214
+ ):
215
+ return True
216
+ # Check for JSON Schema fields
217
+ if isinstance(data, dict) and any(
218
+ field in data for field in ["$schema", "type", "properties", "required"]
219
+ ):
220
+ return True
221
+ except json.JSONDecodeError:
222
+ pass # Not a valid JSON file
223
+
224
+ return False
225
+ except Exception as e:
226
+ _log.debug("APIContractAgent._is_api_contract failed: %s", e)
227
+ return False
228
+
229
+ def _validate_contracts(self, contract_files: list[Path], inp: AgentInput) -> dict:
230
+ """Validate contracts against the running API."""
231
+ results = {}
232
+
233
+ for contract_file in contract_files:
234
+ try:
235
+ # Read the contract
236
+ contract_content = contract_file.read_text(encoding="utf-8")
237
+
238
+ # Parse the contract based on type
239
+ if contract_file.suffix.lower() in [".json"]:
240
+ contract = json.loads(contract_content)
241
+ validation_result = self._validate_openapi_contract_json(
242
+ contract, contract_file, inp
243
+ )
244
+ else:
245
+ # For YAML, we'd need a YAML parser
246
+ # For now, we'll treat it as a string
247
+ validation_result = self._validate_openapi_contract_yaml(
248
+ contract_content, contract_file, inp
249
+ )
250
+
251
+ results[str(contract_file)] = validation_result
252
+ except Exception as e:
253
+ results[str(contract_file)] = {
254
+ "error": f"Could not parse contract file: {str(e)}",
255
+ "errors": [
256
+ {
257
+ "type": "parse_error",
258
+ "message": f"Failed to parse contract file: {str(e)}",
259
+ "path": "",
260
+ "expected": "valid OpenAPI/JSON Schema",
261
+ "actual": "parse error",
262
+ }
263
+ ],
264
+ }
265
+
266
+ return results
267
+
268
+ def _resolve_server_url(self, contract: dict, inp: AgentInput) -> str:
269
+ """Extract server URL from contract or brain config."""
270
+ extra_base = (inp.extra or {}).get("base_url")
271
+ if extra_base:
272
+ return extra_base.rstrip("/")
273
+ test_config = inp.config.get("test_config", {})
274
+ if test_config.get("base_url"):
275
+ return test_config["base_url"].rstrip("/")
276
+ servers = contract.get("servers", [])
277
+ if servers:
278
+ return servers[0].get("url", "").rstrip("/")
279
+ brain = inp.brain or {}
280
+ return brain.get("base_url", "http://localhost:3000").rstrip("/")
281
+
282
+ def _validate_openapi_contract_json(
283
+ self, contract: dict, contract_file: Path, inp: AgentInput
284
+ ) -> dict:
285
+ """Validate OpenAPI contract by probing the running API."""
286
+ errors = []
287
+ if not isinstance(contract, dict) or (
288
+ "openapi" not in contract and "swagger" not in contract
289
+ ):
290
+ errors.append(
291
+ {
292
+ "type": "invalid_spec",
293
+ "message": "File is not a valid OpenAPI specification",
294
+ "path": "$",
295
+ "expected": "OpenAPI object with 'openapi' or 'swagger' field",
296
+ "actual": f"{type(contract).__name__} object",
297
+ }
298
+ )
299
+ return {"errors": errors}
300
+
301
+ base_url = self._resolve_server_url(contract, inp)
302
+ paths = contract.get("paths", {})
303
+
304
+ try:
305
+ import httpx
306
+ except ImportError:
307
+ errors.append({"type": "missing_dep", "message": "httpx required for API validation"})
308
+ return {"errors": errors}
309
+
310
+ for path, path_item in paths.items():
311
+ for method, operation in path_item.items():
312
+ if not isinstance(operation, dict):
313
+ continue
314
+ responses_spec = operation.get("responses", {})
315
+ if not responses_spec:
316
+ continue
317
+
318
+ url = f"{base_url}{path}"
319
+ try:
320
+ resp = httpx.request(method.upper(), url, timeout=10)
321
+ except (httpx.ConnectError, httpx.TimeoutException) as e:
322
+ errors.append(
323
+ {
324
+ "type": "connection_error",
325
+ "severity": "high",
326
+ "path": f"{method.upper()} {path}",
327
+ "message": f"Cannot reach {url}: {e}",
328
+ "expected": "200 range response",
329
+ "actual": "connection failed",
330
+ }
331
+ )
332
+ continue
333
+ except Exception as e:
334
+ errors.append(
335
+ {
336
+ "type": "request_error",
337
+ "severity": "medium",
338
+ "path": f"{method.upper()} {path}",
339
+ "message": f"Request failed: {e}",
340
+ "expected": "valid response",
341
+ "actual": str(e),
342
+ }
343
+ )
344
+ continue
345
+
346
+ expected_statuses = [int(s) for s in responses_spec if s != "default"]
347
+ if expected_statuses and resp.status_code not in expected_statuses:
348
+ errors.append(
349
+ {
350
+ "type": "status_code_mismatch",
351
+ "severity": "high" if resp.status_code >= 400 else "medium",
352
+ "path": f"{method.upper()} {path}",
353
+ "message": f"Expected {expected_statuses}, got {resp.status_code}",
354
+ "expected": str(expected_statuses),
355
+ "actual": str(resp.status_code),
356
+ }
357
+ )
358
+
359
+ # Validate response body against first successful response schema
360
+ for status_code in ("200", "201", "204"):
361
+ response_schema = responses_spec.get(status_code, {})
362
+ content = response_schema.get("content", {})
363
+ for media_type, media_type_obj in content.items():
364
+ schema = media_type_obj.get("schema", {})
365
+ if not schema or not resp.content:
366
+ continue
367
+ if not self._validate_json_schema(resp.json(), schema):
368
+ errors.append(
369
+ {
370
+ "type": "type_mismatch",
371
+ "severity": "high",
372
+ "path": f"{method.upper()} {path} -> {status_code}",
373
+ "message": f"Response body does not match {media_type} schema",
374
+ "expected": str(schema),
375
+ "actual": resp.text[:500],
376
+ }
377
+ )
378
+ break # only validate first success schema
379
+
380
+ return {"errors": errors}
381
+
382
+ def _validate_openapi_contract_yaml(
383
+ self, content: str, contract_file: Path, inp: AgentInput
384
+ ) -> dict:
385
+ """Validate OpenAPI contract from YAML."""
386
+ try:
387
+ import yaml
388
+
389
+ contract = yaml.safe_load(content)
390
+ if isinstance(contract, dict):
391
+ return self._validate_openapi_contract_json(contract, contract_file, inp)
392
+ except ImportError:
393
+ pass
394
+ return {
395
+ "errors": [
396
+ {
397
+ "type": "yaml_not_implemented",
398
+ "message": "Install PyYAML to validate YAML contracts",
399
+ "path": "$",
400
+ "expected": "YAML parsing",
401
+ "actual": "PyYAML not installed",
402
+ }
403
+ ]
404
+ }
405
+
406
+ def _validate_json_schema(self, instance: Any, schema: dict) -> bool:
407
+ """Basic JSON Schema validation (subset — type and required fields)."""
408
+ if not isinstance(schema, dict):
409
+ return True
410
+ schema_type = schema.get("type")
411
+ if schema_type == "object" and isinstance(instance, dict):
412
+ required = schema.get("required", [])
413
+ for field in required:
414
+ if field not in instance:
415
+ return False
416
+ props = schema.get("properties", {})
417
+ for key, val in instance.items():
418
+ prop_schema = props.get(key, {})
419
+ if not self._validate_json_schema(val, prop_schema):
420
+ return False
421
+ elif schema_type == "array" and isinstance(instance, list):
422
+ items_schema = schema.get("items", {})
423
+ if items_schema:
424
+ for item in instance:
425
+ if not self._validate_json_schema(item, items_schema):
426
+ return False
427
+ elif schema_type == "string" and not isinstance(instance, str):
428
+ return False
429
+ elif schema_type == "integer" and not isinstance(instance, int):
430
+ return False
431
+ elif schema_type == "number" and not isinstance(instance, (int, float)):
432
+ return False
433
+ elif schema_type == "boolean" and not isinstance(instance, bool):
434
+ return False
435
+ return True
436
+
437
+ def _map_contract_error_severity(self, error_type: str) -> Severity:
438
+ """Map contract error types to severity levels."""
439
+ error_severity = {
440
+ "required_field_missing": Severity.HIGH,
441
+ "type_mismatch": Severity.HIGH,
442
+ "invalid_format": Severity.MEDIUM,
443
+ "extra_field": Severity.LOW,
444
+ "status_code_mismatch": Severity.HIGH,
445
+ "parse_error": Severity.CRITICAL,
446
+ "runtime_validation_needed": Severity.INFO,
447
+ "yaml_not_implemented": Severity.INFO,
448
+ }
449
+
450
+ return error_severity.get(error_type, Severity.MEDIUM)