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,416 @@
1
+ """
2
+ Ignore Learner — Patchi builds its own ignore list from evidence.
3
+
4
+ Four evidence layers, each entry stamped with source, confidence, reason:
5
+
6
+ static deterministic rules (noise_filter.classify + DEFAULT_IGNORE_DIRS)
7
+ git non-negated .gitignore patterns
8
+ fp_stats directories whose findings keep getting rejected
9
+ (aggregated upward from known_false_positives.json)
10
+ composition directories that are >=90% data files (yaml/json/md/toml),
11
+ contain no executable entry points -> tool-owned data dirs
12
+ (e.g. scanner rule packs, domain definitions, changelogs)
13
+
14
+ Entries generalize: literal directory evidence also emits a ``**/name/**``
15
+ glob once the same directory name proves noisy in enough distinct places,
16
+ so projects with different layouts inherit the lesson automatically.
17
+ A global store (~/.patchi/global_ignores.json) shares high-confidence
18
+ patterns across every project on the machine; project evidence always
19
+ overrides global.
20
+
21
+ Safety rails:
22
+ - source-prefix dirs (src/, app/, lib/, pkg/, internal/) require
23
+ overwhelming evidence before any ignore applies
24
+ - user entries (p ignore add) always win over learned ones
25
+ - every entry keeps machine-readable provenance
26
+ """
27
+
28
+ from __future__ import annotations
29
+
30
+ import fnmatch
31
+ import json
32
+ import logging
33
+ import os
34
+ import time
35
+ from dataclasses import asdict, dataclass, field
36
+ from pathlib import Path
37
+
38
+ _log = logging.getLogger("patchi.ignore_learner")
39
+
40
+ PROJECT_STORE = ".patchi/memory/learned_ignores.json"
41
+ GLOBAL_STORE = Path.home() / ".patchi" / "global_ignores.json"
42
+
43
+ # Directory names that look like primary source containers — these need
44
+ # overwhelming evidence before we ever ignore them.
45
+ _SOURCE_PREFIXES = ("src", "app", "lib", "pkg", "internal")
46
+
47
+ # Executable extensions = files that can contain running code.
48
+ _EXEC_EXTENSIONS = frozenset(
49
+ {
50
+ ".py",
51
+ ".js",
52
+ ".jsx",
53
+ ".ts",
54
+ ".tsx",
55
+ ".mjs",
56
+ ".cjs",
57
+ ".go",
58
+ ".rs",
59
+ ".java",
60
+ ".kt",
61
+ ".kts",
62
+ ".rb",
63
+ ".php",
64
+ ".cs",
65
+ ".swift",
66
+ ".dart",
67
+ ".c",
68
+ ".h",
69
+ ".cpp",
70
+ ".hpp",
71
+ ".cc",
72
+ ".sh",
73
+ ".ps1",
74
+ ".lua",
75
+ ".sql",
76
+ }
77
+ )
78
+
79
+ # Data/config extensions — presence signals tool-owned content.
80
+ _DATA_EXTENSIONS = frozenset(
81
+ {
82
+ ".yaml",
83
+ ".yml",
84
+ ".json",
85
+ ".toml",
86
+ ".ini",
87
+ ".cfg",
88
+ ".md",
89
+ ".rst",
90
+ ".txt",
91
+ ".xml",
92
+ ".csv",
93
+ ".lock",
94
+ }
95
+ )
96
+
97
+ _MIN_SAMPLES_FOR_GLOBAL = 3 # distinct projects before a pattern goes global
98
+ _MIN_FILES_FOR_COMPOSITION = 4 # dir must hold this many files to judge
99
+ _DATA_RATIO_THRESHOLD = 0.9 # >=90% data files => tool-owned candidate
100
+
101
+
102
+ @dataclass
103
+ class IgnoreEntry:
104
+ """One learned/user ignore decision with provenance."""
105
+
106
+ pattern: str # glob, matched against rel paths
107
+ category: str # generated|lockfile|docs|tests|data_dir|...
108
+ source: str # static|git|fp_stats|composition|user
109
+ reason: str = ""
110
+ confidence: float = 1.0
111
+ added_at: float = field(default_factory=time.time)
112
+
113
+ def to_dict(self) -> dict:
114
+ return asdict(self)
115
+
116
+ @classmethod
117
+ def from_dict(cls, d: dict) -> IgnoreEntry:
118
+ return cls(
119
+ pattern=d.get("pattern", ""),
120
+ category=d.get("category", ""),
121
+ source=d.get("source", "static"),
122
+ reason=d.get("reason", ""),
123
+ confidence=float(d.get("confidence", 1.0)),
124
+ added_at=float(d.get("added_at", 0.0)),
125
+ )
126
+
127
+
128
+ def _load_store(path: Path) -> list[dict]:
129
+ if not path.is_file():
130
+ return []
131
+ try:
132
+ return json.loads(path.read_text(encoding="utf-8"))
133
+ except Exception as exc: # noqa: BLE001 — corrupt store = rebuild
134
+ _log.warning("failed to load %s: %s", path, exc)
135
+ return []
136
+
137
+
138
+ def _save_store(path: Path, entries: list[dict]) -> None:
139
+ path.parent.mkdir(parents=True, exist_ok=True)
140
+ tmp = path.with_suffix(".json.tmp")
141
+ tmp.write_text(json.dumps(entries, indent=2), encoding="utf-8")
142
+ tmp.replace(path)
143
+
144
+
145
+ class IgnoreLearner:
146
+ """Builds, stores, and applies Patchi's self-learned ignore list."""
147
+
148
+ def __init__(self, root: Path, config: dict | None = None):
149
+ self.root = root
150
+ cfg = (config or {}).get("auto_ignore", {})
151
+ self.enabled: bool = bool(cfg.get("enabled", True))
152
+ self.min_fp_count: int = int(cfg.get("min_fp_count", 5))
153
+ self.min_reject_ratio: float = float(cfg.get("min_reject_ratio", 0.8))
154
+ self.use_global: bool = bool(cfg.get("share_globally", True))
155
+
156
+ self.entries: list[IgnoreEntry] = []
157
+ self._match_cache: dict[str, IgnoreEntry | None] = {}
158
+
159
+ # ── Build ───────────────────────────────────────────────────────────────
160
+
161
+ def build(
162
+ self,
163
+ known_fps: list[dict] | None = None,
164
+ file_paths: list[str] | None = None,
165
+ ) -> list[IgnoreEntry]:
166
+ """Run all evidence layers. ``file_paths`` = relative paths in project."""
167
+ self.entries = []
168
+ if not self.enabled:
169
+ return self.entries
170
+
171
+ self._static_rules()
172
+ self._gitignore_rules()
173
+ if known_fps:
174
+ self._fp_stats_rules(known_fps)
175
+ if file_paths:
176
+ self._composition_rules(file_paths)
177
+
178
+ self._merge_global_and_user()
179
+ return self.entries
180
+
181
+ # ── Layer 1: static ─────────────────────────────────────────────────────
182
+
183
+ def _static_rules(self) -> None:
184
+ from patchi.core.brain.languages import DEFAULT_IGNORE_DIRS
185
+
186
+ for d in sorted(DEFAULT_IGNORE_DIRS):
187
+ self.entries.append(
188
+ IgnoreEntry(
189
+ pattern=f"{d}/**",
190
+ category="environment",
191
+ source="static",
192
+ reason="default ignored directory",
193
+ confidence=1.0,
194
+ )
195
+ )
196
+
197
+ # ── Layer 2: gitignore ──────────────────────────────────────────────────
198
+
199
+ def _gitignore_rules(self) -> None:
200
+ gi = self.root / ".gitignore"
201
+ if not gi.is_file():
202
+ return
203
+ try:
204
+ lines = gi.read_text(encoding="utf-8", errors="replace").splitlines()
205
+ except OSError:
206
+ return
207
+ for raw in lines:
208
+ line = raw.strip()
209
+ if not line or line.startswith("#"):
210
+ continue
211
+ if line.startswith("!"): # negation: git says KEEP — obey it
212
+ continue
213
+ pat = line.rstrip("/")
214
+ if not pat or "*" not in pat and "/" in pat:
215
+ # anchored literal path like docs/build-notes -> treat as dir glob
216
+ pat = f"{pat}/**"
217
+ elif "*" not in pat:
218
+ pat = f"**/{pat}/**" if "/" not in line else f"{pat}/**"
219
+ else:
220
+ pat = f"**/{line}" if "/" not in line else line
221
+ self.entries.append(
222
+ IgnoreEntry(
223
+ pattern=pat.replace("\\", "/"),
224
+ category="gitignored",
225
+ source="git",
226
+ reason=".gitignore",
227
+ confidence=0.9,
228
+ )
229
+ )
230
+
231
+ # ── Layer 3: false-positive statistics ──────────────────────────────────
232
+
233
+ def _fp_stats_rules(self, known_fps: list[dict]) -> None:
234
+ dir_total: dict[str, int] = {}
235
+ dir_rejected: dict[str, int] = {}
236
+ name_hits: dict[str, set] = {} # dirname -> {other parent dirs}
237
+
238
+ for e in known_fps:
239
+ f = (e.get("file") or "").replace("\\", "/")
240
+ if not f:
241
+ continue
242
+ parts = f.split("/")
243
+ d = "/".join(parts[:-1])
244
+ dir_total[d] = dir_total.get(d, 0) + 1
245
+ if len(parts) > 1:
246
+ dir_total.setdefault("/".join(parts[:-1]), 0)
247
+ # rejected = present in FP store at all
248
+ dir_rejected[d] = dir_rejected.get(d, 0) + 1
249
+ if len(parts) > 2:
250
+ name = parts[-2]
251
+ name_hits.setdefault(name, set()).add(d.rsplit("/", 1)[0] if "/" in d else ".")
252
+
253
+ for d, rejected in dir_rejected.items():
254
+ total = max(dir_total.get(d, rejected), rejected)
255
+ if rejected < self.min_fp_count:
256
+ continue
257
+ ratio = rejected / max(total, 1)
258
+ if ratio < self.min_reject_ratio:
259
+ continue
260
+ guarded = d.split("/")[0] in _SOURCE_PREFIXES and ratio < 0.95
261
+ if guarded:
262
+ continue # safety rail: source prefixes need overwhelming evidence
263
+ self.entries.append(
264
+ IgnoreEntry(
265
+ pattern=f"{d}/**",
266
+ category="fp_source",
267
+ source="fp_stats",
268
+ reason=f"{rejected}/{total} findings here were rejected",
269
+ confidence=min(0.95, 0.5 + 0.05 * rejected),
270
+ )
271
+ )
272
+ # Generalize: same dir name already noisy elsewhere?
273
+ leaf = d.rsplit("/", 1)[-1]
274
+ others = name_hits.get(leaf, set()) - {d.rsplit("/", 1)[0] if "/" in d else "."}
275
+ if (
276
+ leaf
277
+ and leaf not in _SOURCE_PREFIXES
278
+ and (len(others) >= 1 or self._seen_elsewhere(leaf))
279
+ ):
280
+ self.entries.append(
281
+ IgnoreEntry(
282
+ pattern=f"**/{leaf}/**",
283
+ category="fp_source",
284
+ source="fp_stats",
285
+ reason=f"generalized from noisy '{leaf}' dirs across locations",
286
+ confidence=0.75,
287
+ )
288
+ )
289
+
290
+ def _seen_elsewhere(self, name: str) -> bool:
291
+ """Has this dir name been learned noisy in another project (global)?"""
292
+ if not self.use_global:
293
+ return False
294
+ for e in _load_store(GLOBAL_STORE):
295
+ ent = IgnoreEntry.from_dict(e)
296
+ if ent.source == "fp_stats" and f"/{name}/" in f"/{ent.pattern.strip('*/')}/":
297
+ return True
298
+ return False
299
+
300
+ # ── Layer 4: composition analysis ───────────────────────────────────────
301
+
302
+ def _composition_rules(self, file_paths: list[str]) -> None:
303
+ dir_files: dict[str, list[str]] = {}
304
+ for p in file_paths:
305
+ p = p.replace("\\", "/")
306
+ parts = p.split("/")
307
+ if len(parts) < 2:
308
+ continue
309
+ d = parts[0] if len(parts) == 2 else "/".join(parts[:2])
310
+ dir_files.setdefault(d, []).append(p)
311
+
312
+ for d, files in dir_files.items():
313
+ if len(files) < _MIN_FILES_FOR_COMPOSITION:
314
+ continue
315
+ exts = [os.path.splitext(f)[1].lower() for f in files]
316
+ exec_count = sum(1 for e in exts if e in _EXEC_EXTENSIONS)
317
+ data_count = sum(1 for e in exts if e in _DATA_EXTENSIONS)
318
+ if exec_count == 0 and data_count / len(files) >= _DATA_RATIO_THRESHOLD:
319
+ if d.split("/")[0] in _SOURCE_PREFIXES:
320
+ continue # rail: never judge src/app/... as data-only
321
+ self.entries.append(
322
+ IgnoreEntry(
323
+ pattern=f"{d}/**",
324
+ category="data_dir",
325
+ source="composition",
326
+ reason=(
327
+ f"{len(files)} files, zero executable "
328
+ f"({data_count}/{len(files)} config/data)"
329
+ ),
330
+ confidence=0.85,
331
+ )
332
+ )
333
+
334
+ # ── Merge global + user (user always wins) ──────────────────────────────
335
+
336
+ def _merge_global_and_user(self) -> None:
337
+ user_entries = [
338
+ IgnoreEntry.from_dict(e)
339
+ for e in _load_store(self.root / PROJECT_STORE)
340
+ if e.get("source") == "user"
341
+ ]
342
+ user_bases = {u.pattern.removesuffix("/**").rstrip("/") for u in user_entries}
343
+
344
+ kept = []
345
+ for e in self.entries:
346
+ base = e.pattern.removesuffix("/**").rstrip("/")
347
+ overridden = any(
348
+ base == ub or base.startswith(ub + "/") or ub.startswith(base + "/")
349
+ for ub in user_bases
350
+ )
351
+ if not overridden:
352
+ kept.append(e)
353
+ self.entries = kept + user_entries
354
+
355
+ if self.use_global:
356
+ for g in _load_store(GLOBAL_STORE):
357
+ ge = IgnoreEntry.from_dict(g)
358
+ if ge.confidence >= 0.9 and ge.source in ("fp_stats", "composition"):
359
+ if not any(e.pattern == ge.pattern for e in self.entries):
360
+ ge.reason = f"global: {ge.reason}"
361
+ self.entries.append(ge)
362
+
363
+ # ── Matching ────────────────────────────────────────────────────────────
364
+
365
+ def matches(self, rel_path: str) -> IgnoreEntry | None:
366
+ """Most specific matching entry for a project-relative path."""
367
+ cached = self._match_cache.get(rel_path, KeyError)
368
+ if cached is not KeyError:
369
+ return cached
370
+
371
+ p = rel_path.replace("\\", "/")
372
+ best: IgnoreEntry | None = None
373
+ best_len = -1
374
+ for e in self.entries:
375
+ pat = e.pattern
376
+ if pat.endswith("/**"):
377
+ base = pat[:-3] # strip trailing /** -> "dir" or "**/dir"
378
+ # fnmatch '*' crosses '/', so '**/build/**' hits nested too;
379
+ # startswith covers plain 'dir/**' prefixes exactly.
380
+ hit = fnmatch.fnmatch(p, pat) or p.startswith(base + "/")
381
+ else:
382
+ hit = fnmatch.fnmatch(p, pat)
383
+ if hit and len(pat) > best_len:
384
+ best, best_len = e, len(pat)
385
+
386
+ self._match_cache[rel_path] = best
387
+ return best
388
+
389
+ # ── Persistence ─────────────────────────────────────────────────────────
390
+
391
+ def promote_to_global(self, min_confidence: float = 0.9) -> int:
392
+ """Share high-confidence project lessons to the cross-project store."""
393
+ if not self.use_global:
394
+ return 0
395
+ existing = {IgnoreEntry.from_dict(e).pattern for e in _load_store(GLOBAL_STORE)}
396
+ promoted = 0
397
+ counts: dict[str, int] = {}
398
+ for e in self.entries:
399
+ if e.source not in ("fp_stats", "composition") or e.confidence < min_confidence:
400
+ continue
401
+ counts[e.pattern] = counts.get(e.pattern, 0) + 1
402
+ store = _load_store(GLOBAL_STORE)
403
+ for pat, n in counts.items():
404
+ if n >= _MIN_SAMPLES_FOR_GLOBAL or pat in existing:
405
+ continue
406
+ src = next(e for e in self.entries if e.pattern == pat)
407
+ store.append(src.to_dict())
408
+ promoted += 1
409
+ if promoted:
410
+ _save_store(GLOBAL_STORE, store)
411
+ return promoted
412
+
413
+
414
+ def build_ignore_learner(root: Path, config: dict | None = None) -> IgnoreLearner:
415
+ """Convenience constructor used by FileCorpus / scan paths."""
416
+ return IgnoreLearner(root, config)