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,304 @@
1
+ """
2
+ EnvVarValidator — Environment variable usage validation.
3
+
4
+ Scans source code for environment variable reads and cross-references them
5
+ against `.env.example` / `.env.sample` files to find:
6
+ - Undocumented env vars (used in code but not listed in .env.example)
7
+ - Unused env vars (listed in .env.example but never read in code)
8
+ - Missing validation (env vars read without default or guard)
9
+
10
+ Covers all 11 languages (process.env.*, os.getenv(), std::env::var(), etc.).
11
+
12
+ Env access spans both function calls AND property/subscript access (e.g.
13
+ `process.env.X` is not a call node in tree-sitter), so literal pattern
14
+ matching is the correct abstraction here. Patterns are scoped to exact
15
+ API names with word boundaries to avoid false positives.
16
+ Does NOT call AI (except for fix suggestions).
17
+ Does NOT write to disk.
18
+ Does NOT touch the queue.
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import logging
24
+ import re
25
+ from pathlib import Path
26
+
27
+ from ..agents.base import (
28
+ AgentGroup,
29
+ AgentInput,
30
+ AgentResult,
31
+ AgentStatus,
32
+ BaseAgent,
33
+ Severity,
34
+ make_finding,
35
+ register,
36
+ safe_rglob,
37
+ )
38
+
39
+ _log = logging.getLogger("patchi.security.env_var_validator")
40
+
41
+
42
+ @register
43
+ class EnvVarValidator(BaseAgent):
44
+ """Agent for validating environment variable usage."""
45
+
46
+ group = AgentGroup.SECURITY
47
+ name = "EnvVarValidator"
48
+ description = "Environment variable validation: undocumented, unused, missing defaults"
49
+
50
+ # Per-language env var read patterns. Each is anchored to a specific API
51
+ # (function call or property/subscript access) so it won't false-match
52
+ # unrelated identifiers.
53
+ ENV_READ_PATTERNS: list[tuple[str, str, str]] = [
54
+ # JavaScript / TypeScript (call and property access)
55
+ (r"process\.env\.(\w+)", "process.env.{}", "JavaScript"),
56
+ (r"process\.env\[\s*['\"](\w+)['\"]\s*\]", "process.env['{}']", "JavaScript"),
57
+ # Python
58
+ (r"os\.getenv\s*\(\s*['\"](\w+)['\"]", "os.getenv('{}')", "Python"),
59
+ (r"os\.environ\.get\s*\(\s*['\"](\w+)['\"]", "os.environ.get('{}')", "Python"),
60
+ (r"os\.environ\[\s*['\"](\w+)['\"]\s*\]", "os.environ['{}']", "Python"),
61
+ # Rust
62
+ (r"env::var\s*\(\s*['\"](\w+)['\"]", 'env::var("{}")', "Rust"),
63
+ (r"env!\(\s*['\"](\w+)['\"]", 'env!("{}")', "Rust"),
64
+ # Go
65
+ (r"os\.Getenv\s*\(\s*['\"](\w+)['\"]", 'os.Getenv("{}")', "Go"),
66
+ (r"os\.LookupEnv\s*\(\s*['\"](\w+)['\"]", 'os.LookupEnv("{}")', "Go"),
67
+ # Java
68
+ (r"System\.getenv\s*\(\s*['\"](\w+)['\"]", 'System.getenv("{}")', "Java"),
69
+ # Ruby
70
+ (r"ENV\[\s*['\"](\w+)['\"]\s*\]", 'ENV["{}"]', "Ruby"),
71
+ (r"ENV\.fetch\s*\(\s*['\"](\w+)['\"]", 'ENV.fetch("{}")', "Ruby"),
72
+ # Swift
73
+ (
74
+ r"ProcessInfo\.processInfo\.environment\[\s*['\"](\w+)['\"]\s*\]",
75
+ 'ProcessInfo.environment["{}"]',
76
+ "Swift",
77
+ ),
78
+ # C / C++ (getenv)
79
+ (r"getenv\s*\(\s*['\"](\w+)['\"]", 'getenv("{}")', "C/C++"),
80
+ # PHP
81
+ (r"getenv\s*\(\s*['\"](\w+)['\"]", 'getenv("{}")', "PHP"),
82
+ # Kotlin
83
+ (r"System\.getenv\s*\(\s*['\"](\w+)['\"]", 'System.getenv("{}")', "Kotlin"),
84
+ # C#
85
+ (
86
+ r"Environment\.GetEnvironmentVariable\s*\(\s*['\"](\w+)['\"]",
87
+ 'Environment.GetEnvironmentVariable("{}")',
88
+ "C#",
89
+ ),
90
+ ]
91
+
92
+ def _run(self, inp: AgentInput, result: AgentResult) -> None:
93
+ findings = []
94
+
95
+ source_patterns = [
96
+ "*.py",
97
+ "*.js",
98
+ "*.jsx",
99
+ "*.ts",
100
+ "*.tsx",
101
+ "*.java",
102
+ "*.go",
103
+ "*.rs",
104
+ "*.c",
105
+ "*.h",
106
+ "*.cpp",
107
+ "*.cxx",
108
+ "*.cc",
109
+ "*.hpp",
110
+ "*.rb",
111
+ "*.swift",
112
+ "*.php",
113
+ "*.kt",
114
+ "*.cs",
115
+ ]
116
+
117
+ # Step 1: Collect all env vars used in source code
118
+ used_env_vars: dict[str, list[dict]] = {}
119
+ for pattern in source_patterns:
120
+ for file_path in safe_rglob(inp.root, pattern):
121
+ if file_path.is_file():
122
+ rel_path = file_path.relative_to(inp.root).as_posix()
123
+ if not self._should_skip_file(rel_path, inp):
124
+ content = self._safe_read(file_path)
125
+ if content is None:
126
+ continue
127
+ self._collect_env_vars(content, rel_path, used_env_vars)
128
+
129
+ # Step 2: Find .env.example / .env.sample
130
+ env_example = self._find_env_example(inp.root)
131
+ documented_vars: set[str] = set()
132
+ if env_example:
133
+ documented_vars = self._parse_env_example(env_example)
134
+ # Check for documented but unused vars
135
+ for var in sorted(documented_vars):
136
+ if var not in used_env_vars:
137
+ findings.append(
138
+ make_finding(
139
+ severity=Severity.INFO,
140
+ file=env_example.relative_to(inp.root).as_posix(),
141
+ line_start=0,
142
+ title=f"Unused documented env var: {var}",
143
+ description=(
144
+ f"Environment variable '{var}' is documented in {env_example.name} "
145
+ "but never referenced in source code. Consider removing it."
146
+ ),
147
+ )
148
+ )
149
+
150
+ # Step 3: Check each used env var
151
+ for var, usages in sorted(used_env_vars.items()):
152
+ is_documented = var in documented_vars
153
+ all_with_default = all(u.get("has_default", False) for u in usages)
154
+ all(not u.get("is_bare_access", True) for u in usages)
155
+
156
+ if not is_documented:
157
+ first_usage = usages[0]
158
+ findings.append(
159
+ make_finding(
160
+ severity=Severity.MEDIUM,
161
+ file=first_usage["file"],
162
+ line_start=first_usage["line"],
163
+ title=f"Undocumented env var: {var}",
164
+ description=(
165
+ f"Environment variable '{var}' is read in source code "
166
+ f"but not documented in {env_example.name if env_example else '.env.example'}. "
167
+ "Add it to the env template with a default or description."
168
+ ),
169
+ evidence=first_usage["snippet"],
170
+ )
171
+ )
172
+
173
+ if not all_with_default:
174
+ bare_usages = [u for u in usages if not u.get("has_default", False)]
175
+ for u in bare_usages[:1]:
176
+ findings.append(
177
+ make_finding(
178
+ severity=Severity.LOW,
179
+ file=u["file"],
180
+ line_start=u["line"],
181
+ title=f"Unvalidated env var access: {var}",
182
+ description=(
183
+ f"Environment variable '{var}' is accessed without a default value "
184
+ "or guard. This may crash at runtime if the variable is not set."
185
+ ),
186
+ evidence=u["snippet"],
187
+ cwe="CWE-754",
188
+ )
189
+ )
190
+
191
+ result.status = AgentStatus.SUCCEEDED
192
+ result.findings = findings
193
+ result.data.update(
194
+ {
195
+ "used_env_vars": len(used_env_vars),
196
+ "documented_vars": len(documented_vars),
197
+ "env_findings": len(findings),
198
+ "needs_ai": False,
199
+ }
200
+ )
201
+ return
202
+
203
+ def _should_skip_file(self, file_path: str, inp: AgentInput) -> bool:
204
+ from pathlib import PurePosixPath
205
+
206
+ restrictions = inp.config.get("restrictions", [])
207
+ for r in restrictions:
208
+ if r.get("enabled", True):
209
+ path = r["path"]
210
+ if file_path.startswith(path) or PurePosixPath(file_path).match(path):
211
+ if r["type"] == "NO_TOUCH":
212
+ return True
213
+ if r["type"] == "SCAN_ONLY" and self.__class__.__name__ == "FixAgent":
214
+ return True
215
+ return False
216
+
217
+ def _safe_read(self, path: Path) -> str | None:
218
+ try:
219
+ return path.read_text(encoding="utf-8")
220
+ except Exception as e:
221
+ _log.warning("EnvVarValidator._safe_read failed: %s", e)
222
+ return None
223
+
224
+ def _collect_env_vars(self, content: str, rel_path: str, acc: dict[str, list[dict]]) -> None:
225
+ lines = content.splitlines()
226
+ for pattern, _fmt, _lang in self.ENV_READ_PATTERNS:
227
+ for match in re.finditer(pattern, content):
228
+ var_name = match.group(1)
229
+ line_num = content[: match.start()].count("\n") + 1
230
+ line = lines[line_num - 1] if line_num <= len(lines) else ""
231
+ # Heuristic: does this access have a default value?
232
+ has_default = bool(
233
+ re.search(
234
+ rf"process\.env\.{re.escape(var_name)}\s*(\?\?|\|\|)\s*",
235
+ line,
236
+ )
237
+ or re.search(
238
+ rf"os\.getenv\(['\"]{re.escape(var_name)}['\"],\s*",
239
+ line,
240
+ )
241
+ or re.search(
242
+ rf"os\.environ\.get\(['\"]{re.escape(var_name)}['\"],\s*",
243
+ line,
244
+ )
245
+ or re.search(
246
+ rf"ENV\.fetch\(['\"]{re.escape(var_name)}['\"]",
247
+ line,
248
+ )
249
+ )
250
+ # Heuristic: bare access without guard
251
+ is_bare_access = bool(
252
+ re.search(rf"process\.env\.{re.escape(var_name)}\b", line)
253
+ and not re.search(rf"process\.env\.{re.escape(var_name)}\s*(\?\?|\|\|)", line)
254
+ )
255
+ if var_name not in acc:
256
+ acc[var_name] = []
257
+ acc[var_name].append(
258
+ {
259
+ "file": rel_path,
260
+ "line": line_num,
261
+ "snippet": line.strip(),
262
+ "has_default": has_default,
263
+ "is_bare_access": is_bare_access,
264
+ }
265
+ )
266
+
267
+ def _find_env_example(self, root: Path) -> Path | None:
268
+ candidates = [
269
+ root / ".env.example",
270
+ root / ".env.sample",
271
+ root / ".env.dist",
272
+ root / "env.example",
273
+ root / ".env.template",
274
+ ]
275
+ for p in candidates:
276
+ if p.is_file():
277
+ return p
278
+ # Also search one level deep
279
+ for p in root.iterdir():
280
+ if p.is_file() and p.name.lower() in (
281
+ ".env.example",
282
+ ".env.sample",
283
+ ".env.dist",
284
+ "env.example",
285
+ ".env.template",
286
+ ):
287
+ return p
288
+ return None
289
+
290
+ def _parse_env_example(self, path: Path) -> set[str]:
291
+ """Parse .env.example into a set of variable names."""
292
+ content = self._safe_read(path)
293
+ if content is None:
294
+ return set()
295
+ vars_set: set[str] = set()
296
+ for line in content.splitlines():
297
+ line = line.strip()
298
+ if not line or line.startswith("#"):
299
+ continue
300
+ # Split on = or :
301
+ var_name = re.split(r"[=:]", line, maxsplit=1)[0].strip()
302
+ if var_name and re.match(r"^[a-zA-Z_]\w*$", var_name):
303
+ vars_set.add(var_name)
304
+ return vars_set
@@ -0,0 +1,100 @@
1
+ """
2
+ Evidence Capture — screenshot/video evidence for security findings.
3
+
4
+ Captures browser screenshots and session recordings during security testing.
5
+ Stores evidence paths in findings for audit trail.
6
+
7
+ Requires: Playwright
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import logging
13
+ from pathlib import Path
14
+
15
+ from patchi.core.agents.base import (
16
+ AgentGroup,
17
+ AgentInput,
18
+ AgentResult,
19
+ BaseAgent,
20
+ Finding,
21
+ Severity,
22
+ register,
23
+ )
24
+
25
+ _log = logging.getLogger("patchi.security.evidence")
26
+
27
+
28
+ def patchi_capture_screenshot(
29
+ page, finding_id: str, evidence_dir: Path, step: int = 0
30
+ ) -> str | None:
31
+ """Capture a screenshot during a browser test. Returns file path."""
32
+ try:
33
+ evidence_dir.mkdir(parents=True, exist_ok=True)
34
+ filename = f"{finding_id}_step_{step}.png"
35
+ path = evidence_dir / filename
36
+ page.screenshot(path=str(path), full_page=True)
37
+ return str(path)
38
+ except Exception as e:
39
+ _log.warning("patchi_capture_screenshot failed: %s", e)
40
+ return None
41
+
42
+
43
+ def patchi_start_recording(context, scan_id: str, evidence_dir: Path) -> str | None:
44
+ """Start video recording for a browser context. Returns recording path on close."""
45
+ try:
46
+ record_dir = evidence_dir / scan_id
47
+ record_dir.mkdir(parents=True, exist_ok=True)
48
+ # Playwright handles recording via context manager
49
+ return str(record_dir)
50
+ except Exception as e:
51
+ _log.warning("patchi_start_recording failed: %s", e)
52
+ return None
53
+
54
+
55
+ def patchi_get_evidence_dir(root: Path) -> Path:
56
+ """Get the standard evidence directory path."""
57
+ return root / ".patchi" / "evidence"
58
+
59
+
60
+ # ── Evidence Agent ────────────────────────────────────────────────────────────
61
+
62
+
63
+ @register
64
+ class EvidenceAgent(BaseAgent):
65
+ """Captures screenshots and video evidence for security findings."""
66
+
67
+ name = "EvidenceAgent"
68
+ group = AgentGroup.SECURITY
69
+ timeout = 30
70
+
71
+ def _run(self, inp: AgentInput, result: AgentResult) -> None:
72
+ evidence_dir = patchi_get_evidence_dir(inp.root)
73
+ evidence_dir.mkdir(parents=True, exist_ok=True)
74
+
75
+ # Check if Playwright is available
76
+ try:
77
+ import importlib.util
78
+
79
+ has_playwright = importlib.util.find_spec("playwright") is not None
80
+ result.data["playwright_available"] = has_playwright
81
+ except (ImportError, ValueError):
82
+ result.data["playwright_available"] = False
83
+ result.add_finding(
84
+ Finding(
85
+ agent=self.name,
86
+ type="playwright_missing",
87
+ severity=Severity.INFO,
88
+ file="",
89
+ message="Playwright not installed — evidence capture unavailable. pip install playwright",
90
+ )
91
+ )
92
+ return
93
+
94
+ # Record evidence directory location
95
+ result.data["evidence_dir"] = str(evidence_dir)
96
+ result.data["message"] = f"Evidence directory ready at {evidence_dir}"
97
+
98
+ # Scan for existing evidence
99
+ existing = list(evidence_dir.rglob("*.png")) + list(evidence_dir.rglob("*.webm"))
100
+ result.data["existing_evidence_count"] = len(existing)