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,271 @@
1
+ """
2
+ SensitiveDataAgent — PII and credential exposure detection.
3
+
4
+ Scans source code for:
5
+ - Hardcoded API keys, tokens, passwords, secrets
6
+ - PII patterns (emails, SSNs, credit cards, phone numbers, IPs)
7
+ - Connection strings with embedded credentials
8
+ - Private keys and certificates in source
9
+ - AWS/GCP/Azure credentials
10
+ - Database URLs with passwords
11
+
12
+ Uses regex pattern matching. Does NOT call AI.
13
+ Does NOT write to disk. Does NOT touch the queue.
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import logging
19
+ import os
20
+ import re
21
+ from pathlib import Path
22
+
23
+ from patchi.core.brain.languages import DEFAULT_IGNORE_DIRS
24
+
25
+ from ..agents.base import (
26
+ AgentDomain,
27
+ AgentGroup,
28
+ AgentInput,
29
+ AgentResult,
30
+ AgentStatus,
31
+ BaseAgent,
32
+ Finding,
33
+ Severity,
34
+ make_finding,
35
+ register,
36
+ )
37
+
38
+ _log = logging.getLogger("patchi.security.sensitive_data_agent")
39
+
40
+
41
+ @register
42
+ class SensitiveDataAgent(BaseAgent):
43
+ """Agent for detecting PII and credential exposure in source code."""
44
+
45
+ group = AgentGroup.SECURITY
46
+ domain = AgentDomain.SECURITY
47
+ name = "SensitiveDataAgent"
48
+ description = (
49
+ "PII/credential exposure: API keys, passwords, emails, SSNs, connection strings in code"
50
+ )
51
+
52
+ # ── Credential patterns ─────────────────────────────────────────────────
53
+ _CREDENTIAL_PATTERNS = [
54
+ # Generic API keys / secrets
55
+ (
56
+ re.compile(
57
+ r'(?:api[_-]?key|apikey|secret[_-]?key|access[_-]?key|auth[_-]?token)\s*[:=]\s*["\'][A-Za-z0-9+/=_-]{16,}["\']',
58
+ re.I,
59
+ ),
60
+ Severity.CRITICAL,
61
+ "Hardcoded API key/token in source code",
62
+ ),
63
+ (
64
+ re.compile(r'(?:password|passwd|pwd)\s*[:=]\s*["\'][^"\']{4,}["\']', re.I),
65
+ Severity.CRITICAL,
66
+ "Hardcoded password in source code",
67
+ ),
68
+ (
69
+ re.compile(
70
+ r'(?:secret|client[_-]?secret)\s*[:=]\s*["\'][A-Za-z0-9+/=_-]{8,}["\']', re.I
71
+ ),
72
+ Severity.CRITICAL,
73
+ "Hardcoded secret in source code",
74
+ ),
75
+ # AWS
76
+ (
77
+ re.compile(r"AKIA[0-9A-Z]{16}", re.I),
78
+ Severity.CRITICAL,
79
+ "AWS Access Key ID found in source code",
80
+ ),
81
+ (
82
+ re.compile(
83
+ r'(?:aws[_-]?secret|aws[_-]?access[_-]?key)\s*[:=]\s*["\'][^"\']{8,}["\']', re.I
84
+ ),
85
+ Severity.CRITICAL,
86
+ "AWS credential in source code",
87
+ ),
88
+ # GCP
89
+ (
90
+ re.compile(r'["\']AIza[0-9A-Za-z_-]{35}["\']', re.I),
91
+ Severity.CRITICAL,
92
+ "GCP API key found in source code",
93
+ ),
94
+ (
95
+ re.compile(
96
+ r'(?:gcp[_-]?key|google[_-]?api[_-]?key)\s*[:=]\s*["\'][^"\']{8,}["\']', re.I
97
+ ),
98
+ Severity.CRITICAL,
99
+ "GCP credential in source code",
100
+ ),
101
+ # Azure
102
+ (
103
+ re.compile(
104
+ r'(?:azure|az)[_-]?(?:key|secret|token)\s*[:=]\s*["\'][^"\']{8,}["\']', re.I
105
+ ),
106
+ Severity.CRITICAL,
107
+ "Azure credential in source code",
108
+ ),
109
+ # Private keys
110
+ (
111
+ re.compile(r"-----BEGIN\s+(?:RSA\s+)?PRIVATE\s+KEY-----", re.I),
112
+ Severity.CRITICAL,
113
+ "Private key embedded in source code",
114
+ ),
115
+ (
116
+ re.compile(r"-----BEGIN\s+OPENSSH\s+PRIVATE\s+KEY-----", re.I),
117
+ Severity.CRITICAL,
118
+ "SSH private key embedded in source code",
119
+ ),
120
+ # Connection strings with passwords
121
+ (
122
+ re.compile(r"(?:mongodb|mysql|postgres|redis|amqp|smtp)://[^:]+:[^@]+@", re.I),
123
+ Severity.HIGH,
124
+ "Connection string with embedded credentials",
125
+ ),
126
+ (
127
+ re.compile(r'DATABASE_URL\s*[:=]\s*["\'][^"\']+:.*@', re.I),
128
+ Severity.HIGH,
129
+ "DATABASE_URL with embedded password",
130
+ ),
131
+ # Generic high-entropy strings that look like secrets
132
+ (
133
+ re.compile(r'(?:token|bearer)\s*[:=]\s*["\'][A-Za-z0-9+/=_-]{32,}["\']', re.I),
134
+ Severity.HIGH,
135
+ "Long bearer/token string — possible hardcoded credential",
136
+ ),
137
+ ]
138
+
139
+ # ── PII patterns ────────────────────────────────────────────────────────
140
+ _PII_PATTERNS = [
141
+ # Email addresses
142
+ (
143
+ re.compile(r'["\'][a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}["\']', re.I),
144
+ Severity.MEDIUM,
145
+ "Email address hardcoded in source",
146
+ ),
147
+ # SSN patterns (US)
148
+ (
149
+ re.compile(r'["\']?\d{3}-\d{2}-\d{4}["\']?'),
150
+ Severity.HIGH,
151
+ "Possible Social Security Number in source",
152
+ ),
153
+ # Credit card numbers
154
+ (
155
+ re.compile(r'["\']?\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}["\']?'),
156
+ Severity.HIGH,
157
+ "Possible credit card number in source",
158
+ ),
159
+ # Phone numbers (US format)
160
+ (
161
+ re.compile(r'["\']?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}["\']?'),
162
+ Severity.LOW,
163
+ "Phone number in source — verify if intentional",
164
+ ),
165
+ # IP addresses with ports (possible internal infra exposure)
166
+ (
167
+ re.compile(r'["\']?\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+["\']?'),
168
+ Severity.MEDIUM,
169
+ "IP:port pair in source — verify if intentional",
170
+ ),
171
+ ]
172
+
173
+ _SKIP_DIRS = DEFAULT_IGNORE_DIRS | {"test", "tests", "test_*"}
174
+
175
+ # Test files are excluded from PII checks (may contain test data)
176
+ _SKIP_DIRS_PII = DEFAULT_IGNORE_DIRS | {"test", "tests"}
177
+
178
+ def _run(self, inp: AgentInput, result: AgentResult) -> None:
179
+ findings = []
180
+ files_scanned = 0
181
+
182
+ for p in self._safe_rglob(inp.root, "*.py"):
183
+ rel = str(p.relative_to(inp.root))
184
+ if self._should_skip(rel):
185
+ continue
186
+ files_scanned += 1
187
+ try:
188
+ content = p.read_text(encoding="utf-8", errors="ignore")
189
+ except Exception as e:
190
+ _log.warning("SensitiveDataAgent._run failed: %s", e)
191
+ continue
192
+
193
+ findings.extend(self._check_credentials(content, rel))
194
+ findings.extend(self._check_pii(content, rel))
195
+
196
+ result.status = AgentStatus.DONE
197
+ result.findings = findings
198
+ result.files_scanned = files_scanned
199
+ result.data.update({"files_scanned": files_scanned, "finding_count": len(findings)})
200
+ return
201
+
202
+ def _check_credentials(self, content: str, rel_path: str) -> list[Finding]:
203
+ findings = []
204
+ lines = content.splitlines()
205
+ for line_num, line in enumerate(lines, 1):
206
+ stripped = line.strip()
207
+ if stripped.startswith("#") or stripped.startswith("//"):
208
+ continue
209
+ # Skip obvious test/mock patterns (but not in the key value itself)
210
+ line_lower = stripped.lower()
211
+ if any(
212
+ kw in line_lower.split('"')[0] or kw in line_lower.split("'")[0]
213
+ for kw in ("placeholder", "xxx", "fake_", "mock_")
214
+ ):
215
+ continue
216
+
217
+ for pattern, severity, message in self._CREDENTIAL_PATTERNS:
218
+ if pattern.search(line):
219
+ findings.append(
220
+ make_finding(
221
+ self.name,
222
+ "credential_exposure",
223
+ severity,
224
+ rel_path,
225
+ message,
226
+ line=line_num,
227
+ code_snippet=stripped[:120],
228
+ suggestion="Move credentials to .env file or secret manager. Never commit secrets.",
229
+ )
230
+ )
231
+ return findings
232
+
233
+ def _check_pii(self, content: str, rel_path: str) -> list[Finding]:
234
+ findings = []
235
+ lines = content.splitlines()
236
+ for line_num, line in enumerate(lines, 1):
237
+ stripped = line.strip()
238
+ if stripped.startswith("#") or stripped.startswith("//"):
239
+ continue
240
+ # Skip test files for PII (they use dummy data)
241
+ if "test" in rel_path.lower():
242
+ continue
243
+
244
+ for pattern, severity, message in self._PII_PATTERNS:
245
+ if pattern.search(line):
246
+ findings.append(
247
+ make_finding(
248
+ self.name,
249
+ "pii_exposure",
250
+ severity,
251
+ rel_path,
252
+ message,
253
+ line=line_num,
254
+ code_snippet=stripped[:120],
255
+ suggestion="Remove PII from source code. Use environment variables or test fixtures.",
256
+ )
257
+ )
258
+ return findings
259
+
260
+ def _should_skip(self, rel_path: str) -> bool:
261
+ parts = rel_path.replace("\\", "/").split("/")
262
+ return any(p in self._SKIP_DIRS for p in parts)
263
+
264
+ def _safe_rglob(self, root: Path, pattern: str):
265
+ """Directory-walking with pruning."""
266
+ suffix = pattern.replace("*.", ".")
267
+ for dirpath, dirnames, filenames in os.walk(root):
268
+ dirnames[:] = [d for d in dirnames if d not in self._SKIP_DIRS]
269
+ for fn in filenames:
270
+ if fn.endswith(suffix):
271
+ yield Path(dirpath) / fn
@@ -0,0 +1,412 @@
1
+ """Service mesh security agent — validates Istio/Linkerd mesh configuration."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ import re
7
+ import shutil
8
+ import subprocess
9
+ from pathlib import Path
10
+
11
+ from ..agents.base import (
12
+ AgentDomain,
13
+ AgentGroup,
14
+ AgentInput,
15
+ AgentResult,
16
+ AgentStatus,
17
+ BaseAgent,
18
+ Finding,
19
+ Severity,
20
+ make_finding,
21
+ register,
22
+ safe_rglob,
23
+ )
24
+ from ..brain.trace_log import trace_agent
25
+
26
+ # Istio CRD patterns
27
+ ISTIO_CRDS = {
28
+ "VirtualService": {
29
+ "kind": r"kind:\s*VirtualService",
30
+ "hosts": r"hosts:",
31
+ "http": r"http:",
32
+ "route": r"route:",
33
+ "tls": r"tls:",
34
+ },
35
+ "DestinationRule": {
36
+ "kind": r"kind:\s*DestinationRule",
37
+ "mtls": r"mtls:",
38
+ "tls": r"tls:",
39
+ "traffic_policy": r"trafficPolicy:",
40
+ "connection_pool": r"connectionPool:",
41
+ },
42
+ "PeerAuthentication": {
43
+ "kind": r"kind:\s*PeerAuthentication",
44
+ "mtls": r"mtls:",
45
+ "strict": r"STRICT|mode:\s*STRICT",
46
+ "permissive": r"PERMISSIVE|mode:\s*PERMISSIVE",
47
+ },
48
+ "AuthorizationPolicy": {
49
+ "kind": r"kind:\s*AuthorizationPolicy",
50
+ "rules": r"rules:",
51
+ "action": r"action:",
52
+ "deny": r"DENY|action:\s*DENY",
53
+ "allow": r"ALLOW|action:\s*ALLOW",
54
+ },
55
+ "Gateway": {
56
+ "kind": r"kind:\s*Gateway",
57
+ "servers": r"servers:",
58
+ "tls_mode": r"tls:",
59
+ },
60
+ }
61
+
62
+ # Linkerd CRD patterns
63
+ LINKERD_CRDS = {
64
+ "ServiceProfile": {
65
+ "kind": r"kind:\s*ServiceProfile",
66
+ "routes": r"routes:",
67
+ },
68
+ "Server": {
69
+ "kind": r"kind:\s*Server",
70
+ "pod_selector": r"podSelector:",
71
+ "proxy_protocol": r"proxyProtocol:",
72
+ },
73
+ "ServerAuthorization": {
74
+ "kind": r"kind:\s*ServerAuthorization",
75
+ "client": r"client:",
76
+ "server": r"server:",
77
+ },
78
+ "TrafficSplit": {
79
+ "kind": r"kind:\s*TrafficSplit",
80
+ "backends": r"backends:",
81
+ "weighted": r"weighted:",
82
+ },
83
+ }
84
+
85
+ # mTLS mode patterns
86
+ MTLS_PATTERNS = [
87
+ (r"mode:\s*STRICT", "mTLS strict mode (good)", Severity.INFO),
88
+ (r"mode:\s*PERMISSIVE", "mTLS permissive mode (weak)", Severity.MEDIUM),
89
+ (r"mode:\s*DISABLE", "mTLS disabled", Severity.HIGH),
90
+ (r"mtls:\s*\{\s*\}", "mTLS empty config (defaults may apply)", Severity.MEDIUM),
91
+ ]
92
+
93
+ # Sidecar injection patterns
94
+ SIDECAR_PATTERNS = [
95
+ (r"sidecar\.istio\.io/inject:\s*[\"']?true[\"']?", "Sidecar injection enabled"),
96
+ (r"sidecar\.istio\.io/inject:\s*[\"']?false[\"']?", "Sidecar injection disabled"),
97
+ (r"linkerd\.io/inject:\s*[\"']?enabled[\"']?", "Linkerd injection enabled"),
98
+ (r"linkerd\.io/inject:\s*[\"']?disabled[\"']?", "Linkerd injection disabled"),
99
+ ]
100
+
101
+ # Default deny patterns
102
+ DEFAULT_DENY_PATTERNS = [
103
+ (r"action:\s*DENY.*\n.*rules:\s*\[\s*\]", "Default deny policy with empty rules"),
104
+ (r"action:\s*DENY", "DENY action policy"),
105
+ (r"action:\s*ALLOW", "ALLOW action policy"),
106
+ ]
107
+
108
+ CONTROL_IDS = {
109
+ "MESH-01": ("mTLS not in strict mode", Severity.HIGH),
110
+ "MESH-02": ("Missing PeerAuthentication", Severity.MEDIUM),
111
+ "MESH-03": ("Sidecar injection not configured", Severity.MEDIUM),
112
+ "MESH-04": ("No AuthorizationPolicy (default allow)", Severity.MEDIUM),
113
+ "MESH-05": ("Missing default-deny policy", Severity.MEDIUM),
114
+ "MESH-06": ("VirtualService without TLS", Severity.MEDIUM),
115
+ "MESH-07": ("Service mesh CRD detected", Severity.INFO),
116
+ }
117
+
118
+
119
+ _log = logging.getLogger("patchi.security.service_mesh_agent")
120
+
121
+
122
+ @register
123
+ class ServiceMeshAgent(BaseAgent):
124
+ name = "ServiceMeshAgent"
125
+ group = AgentGroup.SECURITY
126
+ domain = AgentDomain.SECURITY
127
+ description = "Service mesh security: mTLS enforcement, sidecar injection, RBAC least privilege, authorization policies"
128
+
129
+ def _run(self, inp: AgentInput, result: AgentResult) -> None:
130
+ with trace_agent(self.name, inp.root) as trace:
131
+ findings: list[Finding] = []
132
+ files_scanned = 0
133
+
134
+ # Track which CRDs we find for aggregate analysis
135
+ found_crds: dict[str, list[str]] = {}
136
+ has_peer_auth = False
137
+ has_authz_policy = False
138
+ has_default_deny = False
139
+
140
+ # Phase 1: Scan YAML/JSON for service mesh CRDs
141
+ for fpath in safe_rglob(inp.root, "*.yaml"):
142
+ files_scanned += 1
143
+ self._scan_manifest(fpath, inp.root, findings, found_crds)
144
+ for fpath in safe_rglob(inp.root, "*.yml"):
145
+ files_scanned += 1
146
+ self._scan_manifest(fpath, inp.root, findings, found_crds)
147
+
148
+ # Phase 2: Scan Helm charts and values
149
+ for fpath in safe_rglob(inp.root, "values.yaml"):
150
+ files_scanned += 1
151
+ self._scan_helm_values(fpath, inp.root, findings)
152
+
153
+ # Phase 3: Scan Helm templates for CRD generation
154
+ for fpath in safe_rglob(inp.root, "*.tpl"):
155
+ files_scanned += 1
156
+ self._scan_manifest(fpath, inp.root, findings, found_crds)
157
+
158
+ # Phase 4: Aggregate analysis
159
+ crd_types = set(found_crds.keys())
160
+ has_peer_auth = "PeerAuthentication" in crd_types
161
+ has_authz_policy = "AuthorizationPolicy" in crd_types
162
+
163
+ # Check for missing PeerAuthentication
164
+ if crd_types and not has_peer_auth:
165
+ findings.append(
166
+ make_finding(
167
+ Severity.MEDIUM,
168
+ "",
169
+ 0,
170
+ "MESH-02: No PeerAuthentication found",
171
+ "Service mesh CRDs detected but no PeerAuthentication policy. mTLS may not be enforced.",
172
+ suggestion="Add PeerAuthentication with mode=STRICT for all namespaces",
173
+ control_id="MESH-02",
174
+ )
175
+ )
176
+
177
+ # Check for missing AuthorizationPolicy
178
+ if crd_types and not has_authz_policy:
179
+ findings.append(
180
+ make_finding(
181
+ Severity.MEDIUM,
182
+ "",
183
+ 0,
184
+ "MESH-04: No AuthorizationPolicy (default allow)",
185
+ "Service mesh CRDs detected but no AuthorizationPolicy. All traffic may be allowed by default.",
186
+ suggestion="Implement default-deny AuthorizationPolicy and explicit ALLOW rules",
187
+ control_id="MESH-04",
188
+ )
189
+ )
190
+
191
+ # Check for missing default-deny
192
+ if crd_types and not has_default_deny:
193
+ findings.append(
194
+ make_finding(
195
+ Severity.MEDIUM,
196
+ "",
197
+ 0,
198
+ "MESH-05: No default-deny policy detected",
199
+ "No DENY action policy found. Consider implementing default-deny for least privilege.",
200
+ suggestion="Create default-deny AuthorizationPolicy in each namespace",
201
+ control_id="MESH-05",
202
+ )
203
+ )
204
+
205
+ # Phase 5: Try external tools
206
+ # Try istioctl analyze
207
+ istioctl_path = shutil.which("istioctl")
208
+ if istioctl_path:
209
+ try:
210
+ proc = subprocess.run(
211
+ [istioctl_path, "analyze", "--namespace", "default", str(inp.root)],
212
+ capture_output=True,
213
+ text=True,
214
+ timeout=60,
215
+ )
216
+ output = proc.stdout + proc.stderr
217
+ if output and "Error" in output:
218
+ for line in output.split("\n"):
219
+ if "Error" in line or "Warning" in line:
220
+ severity = Severity.HIGH if "Error" in line else Severity.MEDIUM
221
+ findings.append(
222
+ make_finding(
223
+ severity,
224
+ "",
225
+ 0,
226
+ f"istioctl: {line.strip()[:80]}",
227
+ line.strip(),
228
+ suggestion="Fix istioctl analysis finding",
229
+ )
230
+ )
231
+ except (subprocess.TimeoutExpired, Exception):
232
+ pass
233
+
234
+ # Try kube-linter
235
+ kube_linter_path = shutil.which("kube-linter")
236
+ if kube_linter_path:
237
+ try:
238
+ proc = subprocess.run(
239
+ [kube_linter_path, "lint", str(inp.root), "--format", "json"],
240
+ capture_output=True,
241
+ text=True,
242
+ timeout=60,
243
+ )
244
+ if proc.stdout:
245
+ import json
246
+
247
+ try:
248
+ data = json.loads(proc.stdout)
249
+ for item in data.get("objects", []):
250
+ for diag in item.get("diagnostics", []):
251
+ check = diag.get("check", "")
252
+ if any(
253
+ kw in check.lower()
254
+ for kw in ["mtls", "auth", "mesh", "sidecar"]
255
+ ):
256
+ findings.append(
257
+ make_finding(
258
+ Severity.MEDIUM,
259
+ item.get("metadata", {}).get("filePath", ""),
260
+ 0,
261
+ f"kube-linter: {check}",
262
+ diag.get("message", ""),
263
+ suggestion="Review kube-linter finding for mesh security impact",
264
+ )
265
+ )
266
+ except json.JSONDecodeError:
267
+ pass
268
+ except (subprocess.TimeoutExpired, Exception):
269
+ pass
270
+
271
+ result.findings = findings
272
+ result.files_scanned = files_scanned
273
+ result.status = AgentStatus.DONE
274
+ trace.findings = len(findings)
275
+ trace.files_scanned = files_scanned
276
+
277
+ return
278
+
279
+ def _scan_manifest(self, fpath: Path, root: Path, findings: list, found_crds: dict) -> None:
280
+ """Scan a YAML/YML file for service mesh CRDs."""
281
+ try:
282
+ content = fpath.read_text(encoding="utf-8", errors="replace")
283
+ except Exception as e:
284
+ _log.warning("ServiceMeshAgent._scan_manifest failed: %s", e)
285
+ return
286
+
287
+ rel = str(fpath.relative_to(root))
288
+
289
+ # Check for Istio CRDs
290
+ for crd_name, patterns in ISTIO_CRDS.items():
291
+ if re.search(patterns["kind"], content):
292
+ found_crds.setdefault(crd_name, []).append(rel)
293
+ findings.append(
294
+ make_finding(
295
+ Severity.INFO,
296
+ rel,
297
+ 0,
298
+ f"MESH-07: Istio CRD detected: {crd_name}",
299
+ f"Found {crd_name} custom resource. Verify security configuration.",
300
+ control_id="MESH-07",
301
+ )
302
+ )
303
+
304
+ # Check mTLS configuration in PeerAuthentication
305
+ if crd_name == "PeerAuthentication":
306
+ for pattern, desc, severity in MTLS_PATTERNS:
307
+ if re.search(pattern, content):
308
+ if severity != Severity.INFO:
309
+ findings.append(
310
+ make_finding(
311
+ severity,
312
+ rel,
313
+ 0,
314
+ f"MESH-01: {desc}",
315
+ f"PeerAuthentication {desc}. All mesh traffic should use mTLS STRICT.",
316
+ suggestion="Set mTLS mode to STRICT for all PeerAuthentication policies",
317
+ control_id="MESH-01",
318
+ )
319
+ )
320
+
321
+ # Check for sidecar injection in Deployment/Service
322
+ if crd_name in ("VirtualService", "DestinationRule"):
323
+ for pattern, desc in SIDECAR_PATTERNS:
324
+ if re.search(pattern, content):
325
+ if "disabled" in desc.lower() or "false" in desc.lower():
326
+ findings.append(
327
+ make_finding(
328
+ Severity.MEDIUM,
329
+ rel,
330
+ 0,
331
+ "MESH-03: Sidecar injection disabled",
332
+ desc,
333
+ suggestion="Enable sidecar injection for mesh security benefits",
334
+ control_id="MESH-03",
335
+ )
336
+ )
337
+
338
+ # Check for Linkerd CRDs
339
+ for crd_name, patterns in LINKERD_CRDS.items():
340
+ if re.search(patterns["kind"], content):
341
+ found_crds.setdefault(crd_name, []).append(rel)
342
+ findings.append(
343
+ make_finding(
344
+ Severity.INFO,
345
+ rel,
346
+ 0,
347
+ f"MESH-07: Linkerd CRD detected: {crd_name}",
348
+ f"Found {crd_name} custom resource. Verify security configuration.",
349
+ control_id="MESH-07",
350
+ )
351
+ )
352
+
353
+ # Check for default-deny patterns
354
+ for pattern, desc in DEFAULT_DENY_PATTERNS:
355
+ if re.search(pattern, content, re.DOTALL):
356
+ if "DENY" in desc:
357
+ findings.append(
358
+ make_finding(
359
+ Severity.INFO,
360
+ rel,
361
+ 0,
362
+ f"MESH-05: {desc}",
363
+ desc,
364
+ control_id="MESH-05",
365
+ )
366
+ )
367
+
368
+ def _scan_helm_values(self, fpath: Path, root: Path, findings: list) -> None:
369
+ """Scan Helm values files for mesh configuration."""
370
+ try:
371
+ content = fpath.read_text(encoding="utf-8", errors="replace")
372
+ except Exception as e:
373
+ _log.warning("ServiceMeshAgent._scan_helm_values failed: %s", e)
374
+ return
375
+
376
+ rel = str(fpath.relative_to(root))
377
+
378
+ # Check for Istio Helm values
379
+ if re.search(r"istio|mesh|sidecar", content, re.IGNORECASE):
380
+ # Check mTLS settings
381
+ mtls_match = re.search(r"mtls:\s*\n\s*(?:enabled|mode):\s*(\w+)", content)
382
+ if mtls_match:
383
+ mode = mtls_match.group(1)
384
+ if mode.lower() not in ("strict", "true"):
385
+ findings.append(
386
+ make_finding(
387
+ Severity.MEDIUM,
388
+ rel,
389
+ 0,
390
+ "MESH-01: mTLS not in strict mode in Helm values",
391
+ f"Helm values specify mTLS mode={mode}. Use STRICT for production.",
392
+ suggestion="Set mtls.enabled=true or mtls.mode=STRICT",
393
+ control_id="MESH-01",
394
+ )
395
+ )
396
+
397
+ # Check sidecar injection
398
+ injection_match = re.search(
399
+ r"sidecar.*inject(?:ion)?:\s*\n\s*enabled:\s*(\w+)", content
400
+ )
401
+ if injection_match and injection_match.group(1).lower() == "false":
402
+ findings.append(
403
+ make_finding(
404
+ Severity.MEDIUM,
405
+ rel,
406
+ 0,
407
+ "MESH-03: Sidecar injection disabled in Helm values",
408
+ "Helm values have sidecar injection disabled. Services won't get mesh security.",
409
+ suggestion="Enable sidecar injection in Helm values",
410
+ control_id="MESH-03",
411
+ )
412
+ )