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,453 @@
1
+ """
2
+ JWTSecurityAgent — JWT implementation security analysis.
3
+
4
+ Scans for JWT-related security issues:
5
+ - Weak algorithms (HS256 when RS256 should be used, 'none' algorithm)
6
+ - Missing expiration claims (exp, iat)
7
+ - Insecure secret/key storage (hardcoded, weak secrets)
8
+ - Missing audience/issuer validation
9
+ - Token stored in localStorage vs httpOnly cookies
10
+ - Insecure JWT transmission (over non-HTTPS)
11
+ - Key material in version control
12
+
13
+ Uses AST via ast_utils for JWT library call/import detection;
14
+ regex for literal patterns (algorithms, secrets, storage).
15
+ Does NOT call AI. Does NOT write to disk. Does NOT touch the queue.
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import logging
21
+ import os
22
+ from pathlib import Path
23
+
24
+ from ..agents.base import (
25
+ AgentDomain,
26
+ AgentGroup,
27
+ AgentInput,
28
+ AgentResult,
29
+ AgentStatus,
30
+ BaseAgent,
31
+ Finding,
32
+ Severity,
33
+ make_finding,
34
+ register,
35
+ )
36
+ from ..brain.ast_utils import find_assignments, find_calls, find_imports
37
+ from ..brain.code_query import (
38
+ js_calls,
39
+ js_property_names,
40
+ lang_for_file,
41
+ parse_js,
42
+ string_literals,
43
+ )
44
+ from ..brain.languages import DEFAULT_IGNORE_DIRS, Lang, detect_language
45
+
46
+ # ── JWT library names used across languages ──────────────────────────────────
47
+ JWT_LIBRARIES: set[str] = {
48
+ "jsonwebtoken",
49
+ "pyjwt",
50
+ "PyJWT",
51
+ "jose",
52
+ "jwt",
53
+ "JJWT",
54
+ "firebase/php-jwt",
55
+ "ruby-jwt",
56
+ "go-jose",
57
+ "jwt-go",
58
+ }
59
+ JWT_API_CALLS: set[str] = {
60
+ "jwt.encode",
61
+ "jwt.decode",
62
+ "jwt.sign",
63
+ "jwt.verify",
64
+ "jwt.create",
65
+ "jwt.parse",
66
+ "JWT.encode",
67
+ "JWT.decode",
68
+ "JWT.verify",
69
+ "encode",
70
+ "decode",
71
+ "sign",
72
+ "verify",
73
+ }
74
+
75
+
76
+ _log = logging.getLogger("patchi.security.jwt_agent")
77
+
78
+
79
+ @register
80
+ class JWTSecurityAgent(BaseAgent):
81
+ """Agent for detecting JWT implementation vulnerabilities."""
82
+
83
+ group = AgentGroup.SECURITY
84
+ domain = AgentDomain.SECURITY
85
+ name = "JWTSecurityAgent"
86
+ description = (
87
+ "JWT security: weak algorithms, missing expiry, insecure storage, hardcoded secrets"
88
+ )
89
+
90
+ # ── Literal value markers (matched against parsed string/assignment
91
+ # nodes — never raw text, so comments can't trigger findings) ──────────
92
+ _SECRET_NAMES = ("secret", "jwt_secret", "signing_key", "private_key")
93
+ _KEY_NAMES = ("secret", "key")
94
+ _STORAGE_PREFIXES = ("jwt", "token", "auth", "access")
95
+ _EXPIRY_NAMES = (
96
+ "exp", "expires", "expiry", "expiration", "expires_in", "expiresin", "ttl",
97
+ )
98
+ _ALGO_SEVERITY = {
99
+ "none": (
100
+ Severity.CRITICAL,
101
+ "none algorithm allowed — can bypass signature verification",
102
+ ),
103
+ "hs256": (
104
+ Severity.MEDIUM,
105
+ "HS256 symmetric algorithm — consider RS256/ES256 for distributed systems",
106
+ ),
107
+ "hs384": (
108
+ Severity.LOW,
109
+ "HS384 symmetric algorithm — acceptable but verify key strength",
110
+ ),
111
+ "hs512": (
112
+ Severity.LOW,
113
+ "HS512 symmetric algorithm — acceptable but verify key strength",
114
+ ),
115
+ }
116
+
117
+ _SOURCE_PATTERNS = [
118
+ "*.py",
119
+ "*.js",
120
+ "*.mjs",
121
+ "*.cjs",
122
+ "*.jsx",
123
+ "*.ts",
124
+ "*.tsx",
125
+ "*.java",
126
+ "*.php",
127
+ "*.rb",
128
+ "*.go",
129
+ "*.rs",
130
+ "*.kt",
131
+ "*.swift",
132
+ ]
133
+
134
+ _SKIP_DIRS = DEFAULT_IGNORE_DIRS | {"test", "tests", "vendor"}
135
+
136
+ def _run(self, inp: AgentInput, result: AgentResult) -> None:
137
+ findings = []
138
+ files_scanned = 0
139
+
140
+ for pattern in self._SOURCE_PATTERNS:
141
+ for p in self._safe_rglob(inp.root, pattern):
142
+ rel = str(p.relative_to(inp.root))
143
+ if self._should_skip(rel):
144
+ continue
145
+ files_scanned += 1
146
+ try:
147
+ content = p.read_text(encoding="utf-8", errors="ignore")
148
+ except Exception as e:
149
+ _log.warning("JWTSecurityAgent._run failed: %s", e)
150
+ continue
151
+ lang = detect_language(p)
152
+ findings.extend(self._analyze_file(content, lang, rel))
153
+ findings.extend(self._check_no_jwt_in_codebase(content, rel))
154
+
155
+ result.status = AgentStatus.DONE
156
+ result.findings = findings
157
+ result.files_scanned = files_scanned
158
+ result.data.update({"files_scanned": files_scanned, "finding_count": len(findings)})
159
+ return
160
+
161
+ @staticmethod
162
+ def _unquote(value: str) -> str:
163
+ value = value.strip()
164
+ if len(value) >= 2 and value[0] == value[-1] and value[0] in "\"'`":
165
+ return value[1:-1]
166
+ return value
167
+
168
+ def _analyze_file(self, content: str, lang: Lang, rel_path: str) -> list[Finding]:
169
+ findings = []
170
+ lines = content.splitlines()
171
+
172
+ def _snippet(line_num: int) -> str:
173
+ if 0 < line_num <= len(lines):
174
+ return lines[line_num - 1].strip()
175
+ return ""
176
+
177
+ # Detect JWT usage via AST (calls + imports)
178
+ jwt_calls = find_calls(content, lang, JWT_API_CALLS)
179
+ jwt_imports = find_imports(content, lang, JWT_LIBRARIES)
180
+ has_jwt = bool(jwt_calls) or bool(jwt_imports)
181
+
182
+ # String literals carry algorithm names — structural, comments can't match
183
+ literals = string_literals(content, lang)
184
+ for value, line_num in literals:
185
+ lowered = value.strip().lower()
186
+ if lowered in self._ALGO_SEVERITY:
187
+ severity, message = self._ALGO_SEVERITY[lowered]
188
+ findings.append(
189
+ self._make_finding(
190
+ "jwt_weak_algorithm",
191
+ severity,
192
+ rel_path,
193
+ line_num,
194
+ message,
195
+ "Use RS256 or ES256 for distributed systems. Never allow 'none' algorithm.",
196
+ _snippet(line_num),
197
+ )
198
+ )
199
+
200
+ # Hardcoded secrets via assignment targets (all languages)
201
+ try:
202
+ assignments = find_assignments(content, lang)
203
+ except Exception:
204
+ assignments = []
205
+ for assignment in assignments:
206
+ target = str(assignment.get("target", "")).lower()
207
+ value = self._unquote(str(assignment.get("value", "")))
208
+ line_num = int(assignment.get("line", 0) or 0)
209
+ if any(marker in target for marker in self._SECRET_NAMES) and len(value) >= 8:
210
+ findings.append(
211
+ self._make_finding(
212
+ "jwt_hardcoded_secret",
213
+ Severity.CRITICAL,
214
+ rel_path,
215
+ line_num,
216
+ "Hardcoded JWT secret in source code",
217
+ "Move JWT secret to environment variable.",
218
+ _snippet(line_num),
219
+ )
220
+ )
221
+ elif any(marker in target for marker in self._KEY_NAMES) and len(value) >= 16:
222
+ findings.append(
223
+ self._make_finding(
224
+ "jwt_hardcoded_secret",
225
+ Severity.HIGH,
226
+ rel_path,
227
+ line_num,
228
+ "Possible hardcoded cryptographic key",
229
+ "Move JWT secret to environment variable.",
230
+ _snippet(line_num),
231
+ )
232
+ )
233
+
234
+ # Insecure storage: storage calls with JWT-ish string args on the same line
235
+ lits_by_line: dict[int, list[str]] = {}
236
+ for value, line_num in literals:
237
+ lits_by_line.setdefault(line_num, []).append(value.lower())
238
+ storage_calls = find_calls(
239
+ content, lang, {"setItem", "getItem", "cookie", "set_cookie", "setcookie"}
240
+ )
241
+ for call in storage_calls:
242
+ lowered_name = str(call.get("name", "")).lower()
243
+ line_num = int(call.get("line", 0) or 0)
244
+ values = lits_by_line.get(line_num, [])
245
+ if not any(
246
+ v.startswith(prefix) for v in values for prefix in self._STORAGE_PREFIXES
247
+ ):
248
+ continue
249
+ if "localstorage" in lowered_name:
250
+ findings.append(
251
+ self._make_finding(
252
+ "jwt_insecure_storage",
253
+ Severity.HIGH,
254
+ rel_path,
255
+ line_num,
256
+ "JWT stored in localStorage — vulnerable to XSS",
257
+ "Use httpOnly, secure cookies for JWT storage.",
258
+ _snippet(line_num),
259
+ )
260
+ )
261
+ elif "sessionstorage" in lowered_name:
262
+ findings.append(
263
+ self._make_finding(
264
+ "jwt_insecure_storage",
265
+ Severity.MEDIUM,
266
+ rel_path,
267
+ line_num,
268
+ "JWT stored in sessionStorage — vulnerable to XSS",
269
+ "Use httpOnly, secure cookies for JWT storage.",
270
+ _snippet(line_num),
271
+ )
272
+ )
273
+ elif "cookie" in lowered_name:
274
+ findings.append(
275
+ self._make_finding(
276
+ "jwt_insecure_storage",
277
+ Severity.LOW,
278
+ rel_path,
279
+ line_num,
280
+ "JWT in cookie — verify httpOnly and secure flags are set",
281
+ "Use httpOnly, secure cookies for JWT storage.",
282
+ _snippet(line_num),
283
+ )
284
+ )
285
+
286
+ # Missing validation: decode sites without verify / algorithms
287
+ decode_calls = find_calls(content, lang, {"decode"})
288
+ if decode_calls:
289
+ verify_calls = find_calls(
290
+ content, lang, {"verify", "authenticate", "validate"}
291
+ )
292
+ if not verify_calls:
293
+ first = decode_calls[0]
294
+ findings.append(
295
+ self._make_finding(
296
+ "jwt_missing_validation",
297
+ Severity.HIGH,
298
+ rel_path,
299
+ int(first.get("line", 0) or 0),
300
+ "JWT decoded without signature verification",
301
+ "Always verify JWT signatures and specify allowed algorithms.",
302
+ _snippet(int(first.get("line", 0) or 0)),
303
+ )
304
+ )
305
+ for call in decode_calls:
306
+ if not self._call_has_algorithms(content, rel_path, call):
307
+ findings.append(
308
+ self._make_finding(
309
+ "jwt_missing_validation",
310
+ Severity.MEDIUM,
311
+ rel_path,
312
+ int(call.get("line", 0) or 0),
313
+ "JWT decode missing explicit algorithms parameter",
314
+ "Always verify JWT signatures and specify allowed algorithms.",
315
+ _snippet(int(call.get("line", 0) or 0)),
316
+ )
317
+ )
318
+
319
+ # Check for missing expiration (if JWT is used but no expiry marker found)
320
+ if has_jwt and not self._has_expiry(content, lang, rel_path, assignments):
321
+ findings.append(
322
+ make_finding(
323
+ agent=self.name,
324
+ finding_type="jwt_no_expiry",
325
+ severity=Severity.HIGH,
326
+ file=rel_path,
327
+ message="JWT used but no expiration claim (exp) found in file",
328
+ suggestion="Always set expiration on JWT tokens. Recommended: short-lived access tokens (15min) + refresh tokens.",
329
+ )
330
+ )
331
+
332
+ return findings
333
+
334
+ def _call_has_algorithms(self, content: str, rel_path: str, call: dict) -> bool:
335
+ """True when a decode call passes algorithms (kwarg, object key, or span)."""
336
+ line_no = int(call.get("line", 0) or 0)
337
+ if rel_path.endswith(".py"):
338
+ try:
339
+ import ast as _ast
340
+
341
+ tree = _ast.parse(content)
342
+ for node in _ast.walk(tree):
343
+ if not isinstance(node, _ast.Call):
344
+ continue
345
+ if getattr(node, "lineno", 0) != line_no:
346
+ continue
347
+ if any((kw.arg or "") == "algorithms" for kw in node.keywords):
348
+ return True
349
+ except SyntaxError:
350
+ pass
351
+ return False
352
+ try:
353
+ lang_key = lang_for_file(rel_path)
354
+ for site in js_calls(parse_js(content, lang_key), lang_key):
355
+ if site.line == line_no and "algorithms" in site.text:
356
+ return True
357
+ except Exception:
358
+ pass
359
+ return "algorithms" in str(call.get("full_text", ""))
360
+
361
+ def _has_expiry(
362
+ self, content: str, lang: Lang, rel_path: str, assignments: list[dict]
363
+ ) -> bool:
364
+ """Expiry markers via assignment targets, kwargs, and object keys."""
365
+ for assignment in assignments:
366
+ if str(assignment.get("target", "")).lower() in self._EXPIRY_NAMES:
367
+ return True
368
+ if lang == Lang.PYTHON:
369
+ try:
370
+ import ast as _ast
371
+
372
+ tree = _ast.parse(content)
373
+ for node in _ast.walk(tree):
374
+ if isinstance(node, _ast.Call):
375
+ if any((kw.arg or "").lower() in self._EXPIRY_NAMES for kw in node.keywords):
376
+ return True
377
+ elif isinstance(node, _ast.Dict):
378
+ for key in node.keys:
379
+ if isinstance(key, _ast.Constant) and str(key.value).lower() in self._EXPIRY_NAMES:
380
+ return True
381
+ except SyntaxError:
382
+ pass
383
+ return False
384
+ try:
385
+ lang_key = lang_for_file(rel_path)
386
+ props = js_property_names(parse_js(content, lang_key), lang_key)
387
+ return any(p.lower() in self._EXPIRY_NAMES for p in props)
388
+ except Exception:
389
+ return False
390
+
391
+ def _make_finding(
392
+ self,
393
+ finding_type: str,
394
+ severity: Severity,
395
+ file: str,
396
+ line: int,
397
+ message: str,
398
+ suggestion: str,
399
+ snippet: str,
400
+ ) -> Finding:
401
+ return make_finding(
402
+ agent=self.name,
403
+ finding_type=finding_type,
404
+ severity=severity,
405
+ file=file,
406
+ line_start=line,
407
+ message=message,
408
+ suggestion=suggestion,
409
+ code_snippet=snippet[:120],
410
+ )
411
+
412
+ def _check_no_jwt_in_codebase(self, content: str, rel_path: str) -> list[Finding]:
413
+ findings = []
414
+ # KEY=VALUE lines need splitting, not patterns (env-style parsing).
415
+ for line_num, line in enumerate(content.splitlines(), 1):
416
+ if "=" not in line:
417
+ continue
418
+ name, _, value = line.partition("=")
419
+ name, value = name.strip().upper(), value.strip().strip("\"'")
420
+ if not value:
421
+ continue
422
+ if name == "JWT_SECRET":
423
+ severity = Severity.CRITICAL
424
+ message = "JWT secret in environment/config file — use .env, not hardcoded"
425
+ elif name == "JWT_KEY":
426
+ severity = Severity.HIGH
427
+ message = "JWT key in environment/config file — ensure .env is in .gitignore"
428
+ else:
429
+ continue
430
+ findings.append(
431
+ self._make_finding(
432
+ "jwt_env_exposure",
433
+ severity,
434
+ rel_path,
435
+ line_num,
436
+ message,
437
+ "Ensure .env files are in .gitignore. Use secret managers in production.",
438
+ line.strip(),
439
+ )
440
+ )
441
+ return findings
442
+
443
+ def _should_skip(self, rel_path: str) -> bool:
444
+ parts = rel_path.replace("\\", "/").split("/")
445
+ return any(p in self._SKIP_DIRS for p in parts)
446
+
447
+ def _safe_rglob(self, root: Path, pattern: str):
448
+ suffix = pattern.replace("*.", ".")
449
+ for dirpath, dirnames, filenames in os.walk(root):
450
+ dirnames[:] = [d for d in dirnames if d not in self._SKIP_DIRS]
451
+ for fn in filenames:
452
+ if fn.endswith(suffix):
453
+ yield Path(dirpath) / fn