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,168 @@
1
+ """
2
+ Invariant engine — what should be impossible, checked statically.
3
+
4
+ Each Invariant pairs a property statement with a static verifier that runs
5
+ over project data (routes, findings, config). Verification produces Evidence;
6
+ the AssuranceGraph decides the verdict.
7
+
8
+ InvariantType mirrors UNIFIED_UPGRADE_PLAN.md's semantics:
9
+ MUST — property must hold unconditionally
10
+ MUST_NOT — named condition must never occur
11
+ ONLY_IF — action permitted only under stated precondition
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import logging
17
+ from collections.abc import Callable
18
+ from dataclasses import dataclass
19
+ from enum import StrEnum
20
+ from typing import Any
21
+
22
+ _log = logging.getLogger("patchi.core.assurance.invariants")
23
+
24
+
25
+ class InvariantType(StrEnum):
26
+ MUST = "must"
27
+ MUST_NOT = "must_not"
28
+ ONLY_IF = "only_if"
29
+
30
+
31
+ @dataclass
32
+ class Invariant:
33
+ """One provable property about the project."""
34
+
35
+ id: str # stable slug
36
+ invariant_type: InvariantType
37
+ statement: str # human-readable property
38
+ domain: str # owning domain tag
39
+ severity_if_disproved: str = "high"
40
+
41
+ # verifier(project_data) -> (supports: bool, detail: str, artifact: dict)
42
+ # project_data keys available: routes(IntentReport|None), findings(list[dict]),
43
+ # config(dict), files(set[str])
44
+ verifier: Callable[[dict], tuple[bool, str, dict]] | None = None
45
+
46
+ def to_dict(self) -> dict:
47
+ return {
48
+ "id": self.id,
49
+ "type": self.invariant_type.value,
50
+ "statement": self.statement,
51
+ "domain": self.domain,
52
+ "severity_if_disproved": self.severity_if_disproved,
53
+ }
54
+
55
+
56
+ def verify_invariants(
57
+ invariants: list[Invariant],
58
+ project_data: dict[str, Any],
59
+ ):
60
+ """
61
+ Run every invariant's verifier against project_data.
62
+
63
+ Returns list of (invariant, supports, detail, artifact) tuples.
64
+ Invariants without a verifier yield NOT_PROVED evidence (never silently
65
+ proved — honesty rule).
66
+ """
67
+ results = []
68
+ for inv in invariants:
69
+ if inv.verifier is None:
70
+ results.append((inv, False, "no verifier registered for this invariant", {}))
71
+ continue
72
+ try:
73
+ supports, detail, artifact = inv.verifier(project_data)
74
+ results.append((inv, bool(supports), detail, artifact))
75
+ except Exception as e: # noqa: BLE001 — a broken check is NOT_PROVED
76
+ _log.warning("invariant %s verifier failed: %s", inv.id, e)
77
+ results.append((inv, False, f"verifier error: {e}", {}))
78
+ return results
79
+
80
+
81
+ # ── Built-in invariant library ────────────────────────────────────────────────
82
+
83
+
84
+ def _check_all_writes_authenticated(data: dict) -> tuple[bool, str, dict]:
85
+ """Every state-changing route must carry an auth guard."""
86
+ report = data.get("routes")
87
+ if report is None or not getattr(report, "routes", None):
88
+ return False, "no route data available", {}
89
+ bad = [
90
+ r
91
+ for r in report.routes
92
+ if r.method.lower() in ("post", "put", "delete", "patch") and not r.has_auth_guard
93
+ ]
94
+ total_writes = sum(
95
+ 1 for r in report.routes if r.method.lower() in ("post", "put", "delete", "patch")
96
+ )
97
+ if not total_writes:
98
+ return False, "no state-changing routes discovered", {}
99
+ artifact = {"unguarded": [r.file + ":" + str(r.line) for r in bad]}
100
+ if bad:
101
+ names = ", ".join(f"{r.path or '?'}" for r in bad[:3])
102
+ return (
103
+ False,
104
+ f"{len(bad)}/{total_writes} write endpoints lack auth ({names}...)",
105
+ artifact,
106
+ )
107
+ return True, f"all {total_writes} write endpoints carry auth guards", {}
108
+
109
+
110
+ def _check_no_hardcoded_secrets(data: dict) -> tuple[bool, str, dict]:
111
+ """No finding of type hardcoded_secret may exist."""
112
+ findings = data.get("findings", [])
113
+ hits = [f for f in findings if "hardcoded_secret" in str(f.get("type", ""))]
114
+ artifact = {"count": len(hits)}
115
+ if hits:
116
+ first = hits[0]
117
+ return (
118
+ False,
119
+ f"{len(hits)} hardcoded secret(s), e.g. {first.get('file', '?')}:{first.get('line', 0)}",
120
+ artifact,
121
+ )
122
+ return True, "no hardcoded secrets detected by scanners", {}
123
+
124
+
125
+ def _check_no_debug_mode(data: dict) -> tuple[bool, str, dict]:
126
+ """Application must not run with debug enabled."""
127
+ findings = data.get("findings", [])
128
+ hits = [
129
+ f
130
+ for f in findings
131
+ if "debug" in str(f.get("type", "")).lower()
132
+ and f.get("severity", "").lower() in ("high", "critical")
133
+ ]
134
+ if hits:
135
+ return (
136
+ False,
137
+ f"{len(hits)} debug-enabled finding(s) at high+ severity",
138
+ {"count": len(hits)},
139
+ )
140
+ return True, "no high+ debug-mode findings", {}
141
+
142
+
143
+ BUILTIN_INVARIANTS: list[Invariant] = [
144
+ Invariant(
145
+ id="auth-all-writes",
146
+ invariant_type=InvariantType.MUST,
147
+ statement="All state-changing endpoints require authentication",
148
+ domain="access-control-authz",
149
+ severity_if_disproved="critical",
150
+ verifier=_check_all_writes_authenticated,
151
+ ),
152
+ Invariant(
153
+ id="secrets-none-hardcoded",
154
+ invariant_type=InvariantType.MUST_NOT,
155
+ statement="No credentials committed to source",
156
+ domain="secrets-runtime-management",
157
+ severity_if_disproved="critical",
158
+ verifier=_check_no_hardcoded_secrets,
159
+ ),
160
+ Invariant(
161
+ id="debug-not-in-prod",
162
+ invariant_type=InvariantType.MUST_NOT,
163
+ statement="Debug mode is never enabled in production configuration",
164
+ domain="configuration-hardening",
165
+ severity_if_disproved="high",
166
+ verifier=_check_no_debug_mode,
167
+ ),
168
+ ]
patchi/core/atomic.py ADDED
@@ -0,0 +1,61 @@
1
+ """
2
+ Atomic file operations for Patchi.
3
+
4
+ All persistent writes go through these helpers to prevent data loss on crash.
5
+ The pattern is: write to a temp file, then atomically rename to the target.
6
+
7
+ On Windows, ``Path.replace()`` fails if the target exists, so we unlink first.
8
+ Retries handle transient PermissionError from antivirus or indexer locks.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import sys
14
+ import time
15
+ from pathlib import Path
16
+
17
+
18
+ def atomic_replace(src: Path, dst: Path, retries: int = 3) -> None:
19
+ """Replace *dst* with *src* atomically, with retries for Windows file-lock races.
20
+
21
+ This is the single source of truth for atomic file replacement across Patchi.
22
+ Every module that writes persistent JSON or source files should use this.
23
+ """
24
+ for attempt in range(retries):
25
+ try:
26
+ if sys.platform == "win32":
27
+ if dst.exists():
28
+ dst.unlink()
29
+ src.replace(dst)
30
+ return
31
+ except PermissionError:
32
+ if attempt < retries - 1:
33
+ time.sleep(0.05 * (attempt + 1))
34
+ continue
35
+ raise
36
+
37
+
38
+ def atomic_write_json(path: Path, data, indent: int = 2, **dump_kwargs) -> None:
39
+ """Write *data* as JSON to *path* atomically (tmp + rename).
40
+
41
+ Convenience wrapper: serializes to JSON, writes to a sibling ``.tmp`` file,
42
+ then calls :func:`atomic_replace`.
43
+ """
44
+ import json
45
+
46
+ path.parent.mkdir(parents=True, exist_ok=True)
47
+ tmp = path.with_suffix(".tmp")
48
+ with tmp.open("w", encoding="utf-8") as f:
49
+ json.dump(data, f, indent=indent, **dump_kwargs)
50
+ atomic_replace(tmp, path)
51
+
52
+
53
+ def atomic_write_text(path: Path, text: str) -> None:
54
+ """Write *text* to *path* atomically (tmp + rename).
55
+
56
+ Used by the patch applier to write source files without risk of partial writes.
57
+ """
58
+ path.parent.mkdir(parents=True, exist_ok=True)
59
+ tmp = path.with_suffix(path.suffix + ".tmp")
60
+ tmp.write_text(text, encoding="utf-8")
61
+ atomic_replace(tmp, path)
@@ -0,0 +1,17 @@
1
+ """Attackers module — adversarial personalities for security testing."""
2
+
3
+ from .api_abuse import ApiAbuseAttacker
4
+ from .auth_bypass import AuthBypassAttacker
5
+ from .business_logic import BusinessLogicAttacker
6
+ from .planner import AttackPlanner
7
+ from .priv_escalation import PrivEscAttacker
8
+ from .recon import ReconAttacker
9
+
10
+ __all__ = [
11
+ "AttackPlanner",
12
+ "ReconAttacker",
13
+ "AuthBypassAttacker",
14
+ "PrivEscAttacker",
15
+ "BusinessLogicAttacker",
16
+ "ApiAbuseAttacker",
17
+ ]
@@ -0,0 +1,89 @@
1
+ """ApiAbuseAttacker — test API abuse (injection, rate limiting, info disclosure)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from .base import AttackResult, BaseAttacker, Hypothesis
6
+
7
+
8
+ class ApiAbuseAttacker(BaseAttacker):
9
+ """Tests for injection risks, missing rate limits, information disclosure."""
10
+
11
+ name = "api_abuse"
12
+ objective = "api_abuse"
13
+
14
+ def hypothesize(self) -> list[Hypothesis]:
15
+ hypotheses: list[Hypothesis] = []
16
+
17
+ for claim in self._graph.claims.values():
18
+ # Injection risks
19
+ if any(kw in claim.statement.lower() for kw in ["inject", "sanitiz", "validat"]):
20
+ if claim.verdict.value in ("not_proved", "unproven"):
21
+ hypotheses.append(
22
+ Hypothesis(
23
+ attacker=self.name,
24
+ objective=self.objective,
25
+ target=claim.id,
26
+ risk="high",
27
+ confidence=0.7,
28
+ description=f"Injection risk unverified: {claim.statement}",
29
+ )
30
+ )
31
+
32
+ # Rate limiting
33
+ if "rate" in claim.statement.lower() and "limit" in claim.statement.lower():
34
+ hypotheses.append(
35
+ Hypothesis(
36
+ attacker=self.name,
37
+ objective=self.objective,
38
+ target=claim.id,
39
+ risk="medium",
40
+ confidence=0.5,
41
+ description=f"Rate limiting unverified: {claim.statement}",
42
+ )
43
+ )
44
+
45
+ # Information disclosure
46
+ if "error" in claim.statement.lower() and (
47
+ "detail" in claim.statement.lower() or "disclose" in claim.statement.lower()
48
+ ):
49
+ hypotheses.append(
50
+ Hypothesis(
51
+ attacker=self.name,
52
+ objective=self.objective,
53
+ target=claim.id,
54
+ risk="medium",
55
+ confidence=0.4,
56
+ description=f"Info disclosure unverified: {claim.statement}",
57
+ )
58
+ )
59
+
60
+ return hypotheses
61
+
62
+ def test(self, hypothesis: Hypothesis) -> AttackResult:
63
+ claim = self._graph.claims.get(hypothesis.target)
64
+ if claim is None:
65
+ return AttackResult(hypothesis=hypothesis, confirmed=False, evidence="Claim not found")
66
+
67
+ refuting = [e for e in claim.evidence if not e.supports]
68
+ if refuting:
69
+ return AttackResult(
70
+ hypothesis=hypothesis,
71
+ confirmed=False,
72
+ evidence=f"API protection verified: {refuting[0].detail}",
73
+ )
74
+
75
+ supporting = [e for e in claim.evidence if e.supports]
76
+ if supporting:
77
+ return AttackResult(
78
+ hypothesis=hypothesis,
79
+ confirmed=True,
80
+ evidence=f"API abuse confirmed: {supporting[0].detail}",
81
+ severity=hypothesis.risk,
82
+ remediation="Add input validation, rate limiting, or error sanitization",
83
+ )
84
+
85
+ return AttackResult(
86
+ hypothesis=hypothesis,
87
+ confirmed=False,
88
+ evidence="Insufficient evidence",
89
+ )
@@ -0,0 +1,73 @@
1
+ """AuthBypassAttacker — test authentication bypass vulnerabilities."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from .base import AttackResult, BaseAttacker, Hypothesis
6
+
7
+
8
+ class AuthBypassAttacker(BaseAttacker):
9
+ """Tests for unauthenticated access, unguarded state transitions, weak auth mechanisms."""
10
+
11
+ name = "auth_bypass"
12
+ objective = "authentication_bypass"
13
+
14
+ def hypothesize(self) -> list[Hypothesis]:
15
+ hypotheses: list[Hypothesis] = []
16
+
17
+ for claim in self._graph.claims.values():
18
+ if "auth" in claim.domain.lower() or "session" in claim.domain.lower():
19
+ if claim.verdict.value in ("not_proved", "unproven"):
20
+ hypotheses.append(
21
+ Hypothesis(
22
+ attacker=self.name,
23
+ objective=self.objective,
24
+ target=claim.id,
25
+ risk="high",
26
+ confidence=0.7,
27
+ description=f"Auth property unverified: {claim.statement}",
28
+ )
29
+ )
30
+
31
+ # State transitions without auth guards
32
+ if "transition" in claim.statement.lower() and "guard" not in claim.statement.lower():
33
+ hypotheses.append(
34
+ Hypothesis(
35
+ attacker=self.name,
36
+ objective=self.objective,
37
+ target=claim.id,
38
+ risk="high",
39
+ confidence=0.5,
40
+ description=f"State transition may lack auth guard: {claim.statement}",
41
+ )
42
+ )
43
+
44
+ return hypotheses
45
+
46
+ def test(self, hypothesis: Hypothesis) -> AttackResult:
47
+ claim = self._graph.claims.get(hypothesis.target)
48
+ if claim is None:
49
+ return AttackResult(hypothesis=hypothesis, confirmed=False, evidence="Claim not found")
50
+
51
+ refuting = [e for e in claim.evidence if not e.supports]
52
+ if refuting:
53
+ return AttackResult(
54
+ hypothesis=hypothesis,
55
+ confirmed=False,
56
+ evidence=f"Auth verified: {refuting[0].detail}",
57
+ )
58
+
59
+ supporting = [e for e in claim.evidence if e.supports]
60
+ if supporting:
61
+ return AttackResult(
62
+ hypothesis=hypothesis,
63
+ confirmed=True,
64
+ evidence=f"Auth bypass confirmed: {supporting[0].detail}",
65
+ severity="critical",
66
+ remediation="Add authentication check before this operation",
67
+ )
68
+
69
+ return AttackResult(
70
+ hypothesis=hypothesis,
71
+ confirmed=False,
72
+ evidence="Insufficient evidence",
73
+ )
@@ -0,0 +1,91 @@
1
+ """Base attacker — shared types and ABC for adversarial personalities."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import abc
6
+ import logging
7
+ from dataclasses import dataclass, field
8
+
9
+ from patchi.core.assurance.graph import AssuranceGraph
10
+
11
+ _log = logging.getLogger("patchi.core.attackers")
12
+
13
+
14
+ @dataclass
15
+ class Hypothesis:
16
+ """An attacker's hypothesis about where a vulnerability exists."""
17
+
18
+ attacker: str
19
+ objective: str
20
+ target: str # file, endpoint, or state
21
+ risk: str # "critical", "high", "medium", "low"
22
+ confidence: float # 0.0–1.0
23
+ description: str = ""
24
+ evidence_required: list[str] = field(default_factory=list)
25
+
26
+ def to_dict(self) -> dict:
27
+ return {
28
+ "attacker": self.attacker,
29
+ "objective": self.objective,
30
+ "target": self.target,
31
+ "risk": self.risk,
32
+ "confidence": self.confidence,
33
+ "description": self.description,
34
+ }
35
+
36
+
37
+ @dataclass
38
+ class AttackResult:
39
+ """Result of testing a hypothesis."""
40
+
41
+ hypothesis: Hypothesis
42
+ confirmed: bool
43
+ evidence: str = ""
44
+ severity: str = ""
45
+ remediation: str = ""
46
+
47
+ def to_dict(self) -> dict:
48
+ return {
49
+ "hypothesis": self.hypothesis.to_dict(),
50
+ "confirmed": self.confirmed,
51
+ "evidence": self.evidence,
52
+ "severity": self.severity,
53
+ "remediation": self.remediation,
54
+ }
55
+
56
+
57
+ class BaseAttacker(abc.ABC):
58
+ """Base class for adversarial personalities."""
59
+
60
+ name: str = "base"
61
+ objective: str = "unknown"
62
+
63
+ def __init__(self, graph: AssuranceGraph):
64
+ self._graph = graph
65
+
66
+ @abc.abstractmethod
67
+ def hypothesize(self) -> list[Hypothesis]:
68
+ """Generate hypotheses about where vulnerabilities might exist."""
69
+
70
+ @abc.abstractmethod
71
+ def test(self, hypothesis: Hypothesis) -> AttackResult:
72
+ """Test a specific hypothesis."""
73
+
74
+ def run(self) -> list[AttackResult]:
75
+ """Generate and test all hypotheses."""
76
+ hypotheses = self.hypothesize()
77
+ results: list[AttackResult] = []
78
+ for h in hypotheses:
79
+ try:
80
+ result = self.test(h)
81
+ results.append(result)
82
+ except Exception as e:
83
+ _log.warning("Attacker %s failed testing %s: %s", self.name, h.target, e)
84
+ results.append(
85
+ AttackResult(
86
+ hypothesis=h,
87
+ confirmed=False,
88
+ evidence=f"Test error: {e}",
89
+ )
90
+ )
91
+ return results
@@ -0,0 +1,88 @@
1
+ """BusinessLogicAttacker — test business logic abuse (replay, race conditions)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from .base import AttackResult, BaseAttacker, Hypothesis
6
+
7
+
8
+ class BusinessLogicAttacker(BaseAttacker):
9
+ """Tests for replay attacks, concurrent races, unprotected business flows."""
10
+
11
+ name = "business_logic"
12
+ objective = "business_logic_abuse"
13
+
14
+ def hypothesize(self) -> list[Hypothesis]:
15
+ hypotheses: list[Hypothesis] = []
16
+
17
+ for claim in self._graph.claims.values():
18
+ # Idempotency concerns
19
+ if "idempoten" in claim.statement.lower():
20
+ if claim.verdict.value in ("not_proved", "unproven"):
21
+ hypotheses.append(
22
+ Hypothesis(
23
+ attacker=self.name,
24
+ objective=self.objective,
25
+ target=claim.id,
26
+ risk="medium",
27
+ confidence=0.6,
28
+ description=f"Idempotency unverified: {claim.statement}",
29
+ )
30
+ )
31
+
32
+ # Race conditions
33
+ if "concurrent" in claim.statement.lower() or "race" in claim.statement.lower():
34
+ hypotheses.append(
35
+ Hypothesis(
36
+ attacker=self.name,
37
+ objective=self.objective,
38
+ target=claim.id,
39
+ risk="high",
40
+ confidence=0.5,
41
+ description=f"Race condition unverified: {claim.statement}",
42
+ )
43
+ )
44
+
45
+ # State machine integrity
46
+ if "state" in claim.domain.lower() and "transition" in claim.statement.lower():
47
+ if claim.verdict.value == "unproven":
48
+ hypotheses.append(
49
+ Hypothesis(
50
+ attacker=self.name,
51
+ objective=self.objective,
52
+ target=claim.id,
53
+ risk="medium",
54
+ confidence=0.4,
55
+ description=f"State machine integrity unverified: {claim.statement}",
56
+ )
57
+ )
58
+
59
+ return hypotheses
60
+
61
+ def test(self, hypothesis: Hypothesis) -> AttackResult:
62
+ claim = self._graph.claims.get(hypothesis.target)
63
+ if claim is None:
64
+ return AttackResult(hypothesis=hypothesis, confirmed=False, evidence="Claim not found")
65
+
66
+ refuting = [e for e in claim.evidence if not e.supports]
67
+ if refuting:
68
+ return AttackResult(
69
+ hypothesis=hypothesis,
70
+ confirmed=False,
71
+ evidence=f"Business logic verified: {refuting[0].detail}",
72
+ )
73
+
74
+ supporting = [e for e in claim.evidence if e.supports]
75
+ if supporting:
76
+ return AttackResult(
77
+ hypothesis=hypothesis,
78
+ confirmed=True,
79
+ evidence=f"Business logic abuse confirmed: {supporting[0].detail}",
80
+ severity=hypothesis.risk,
81
+ remediation="Add idempotency keys, rate limiting, or state guards",
82
+ )
83
+
84
+ return AttackResult(
85
+ hypothesis=hypothesis,
86
+ confirmed=False,
87
+ evidence="Insufficient evidence",
88
+ )