raxe 0.4.6__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 (668) hide show
  1. raxe/__init__.py +101 -0
  2. raxe/application/__init__.py +48 -0
  3. raxe/application/ab_testing.py +170 -0
  4. raxe/application/analytics/__init__.py +30 -0
  5. raxe/application/analytics/achievement_service.py +444 -0
  6. raxe/application/analytics/repositories.py +172 -0
  7. raxe/application/analytics/retention_service.py +267 -0
  8. raxe/application/analytics/statistics_service.py +419 -0
  9. raxe/application/analytics/streak_service.py +283 -0
  10. raxe/application/apply_policy.py +291 -0
  11. raxe/application/eager_l2.py +503 -0
  12. raxe/application/preloader.py +353 -0
  13. raxe/application/scan_merger.py +321 -0
  14. raxe/application/scan_pipeline.py +1059 -0
  15. raxe/application/scan_pipeline_async.py +403 -0
  16. raxe/application/session_tracker.py +458 -0
  17. raxe/application/telemetry_manager.py +357 -0
  18. raxe/application/telemetry_orchestrator.py +1210 -0
  19. raxe/async_sdk/__init__.py +34 -0
  20. raxe/async_sdk/cache.py +286 -0
  21. raxe/async_sdk/client.py +556 -0
  22. raxe/async_sdk/wrappers/__init__.py +23 -0
  23. raxe/async_sdk/wrappers/openai.py +238 -0
  24. raxe/cli/__init__.py +21 -0
  25. raxe/cli/auth.py +1047 -0
  26. raxe/cli/branding.py +235 -0
  27. raxe/cli/config.py +334 -0
  28. raxe/cli/custom_rules.py +458 -0
  29. raxe/cli/doctor.py +686 -0
  30. raxe/cli/error_handler.py +665 -0
  31. raxe/cli/event.py +648 -0
  32. raxe/cli/exit_codes.py +57 -0
  33. raxe/cli/expiry_warning.py +302 -0
  34. raxe/cli/export.py +183 -0
  35. raxe/cli/history.py +247 -0
  36. raxe/cli/l2_formatter.py +872 -0
  37. raxe/cli/main.py +1137 -0
  38. raxe/cli/models.py +590 -0
  39. raxe/cli/output.py +403 -0
  40. raxe/cli/privacy.py +84 -0
  41. raxe/cli/profiler.py +262 -0
  42. raxe/cli/progress.py +379 -0
  43. raxe/cli/progress_context.py +101 -0
  44. raxe/cli/repl.py +394 -0
  45. raxe/cli/rules.py +542 -0
  46. raxe/cli/setup_wizard.py +721 -0
  47. raxe/cli/stats.py +292 -0
  48. raxe/cli/suppress.py +501 -0
  49. raxe/cli/telemetry.py +1384 -0
  50. raxe/cli/test.py +130 -0
  51. raxe/cli/tune.py +315 -0
  52. raxe/cli/validate.py +218 -0
  53. raxe/domain/__init__.py +30 -0
  54. raxe/domain/analytics/__init__.py +97 -0
  55. raxe/domain/analytics/achievements.py +306 -0
  56. raxe/domain/analytics/models.py +120 -0
  57. raxe/domain/analytics/retention.py +168 -0
  58. raxe/domain/analytics/statistics.py +207 -0
  59. raxe/domain/analytics/streaks.py +173 -0
  60. raxe/domain/engine/__init__.py +15 -0
  61. raxe/domain/engine/executor.py +396 -0
  62. raxe/domain/engine/matcher.py +212 -0
  63. raxe/domain/inline_suppression.py +176 -0
  64. raxe/domain/ml/__init__.py +133 -0
  65. raxe/domain/ml/embedding_cache.py +309 -0
  66. raxe/domain/ml/gemma_detector.py +921 -0
  67. raxe/domain/ml/gemma_models.py +346 -0
  68. raxe/domain/ml/l2_config.py +428 -0
  69. raxe/domain/ml/l2_output_schema.py +443 -0
  70. raxe/domain/ml/manifest_loader.py +309 -0
  71. raxe/domain/ml/manifest_schema.py +345 -0
  72. raxe/domain/ml/model_metadata.py +263 -0
  73. raxe/domain/ml/model_registry.py +786 -0
  74. raxe/domain/ml/protocol.py +282 -0
  75. raxe/domain/ml/scoring_models.py +419 -0
  76. raxe/domain/ml/stub_detector.py +397 -0
  77. raxe/domain/ml/threat_scorer.py +757 -0
  78. raxe/domain/ml/tokenizer_registry.py +372 -0
  79. raxe/domain/ml/voting/__init__.py +89 -0
  80. raxe/domain/ml/voting/config.py +595 -0
  81. raxe/domain/ml/voting/engine.py +465 -0
  82. raxe/domain/ml/voting/head_voters.py +378 -0
  83. raxe/domain/ml/voting/models.py +222 -0
  84. raxe/domain/models.py +82 -0
  85. raxe/domain/packs/__init__.py +17 -0
  86. raxe/domain/packs/models.py +304 -0
  87. raxe/domain/policies/__init__.py +20 -0
  88. raxe/domain/policies/evaluator.py +212 -0
  89. raxe/domain/policies/models.py +223 -0
  90. raxe/domain/rules/__init__.py +32 -0
  91. raxe/domain/rules/custom.py +286 -0
  92. raxe/domain/rules/models.py +273 -0
  93. raxe/domain/rules/schema.py +166 -0
  94. raxe/domain/rules/validator.py +556 -0
  95. raxe/domain/suppression.py +801 -0
  96. raxe/domain/suppression_factory.py +174 -0
  97. raxe/domain/telemetry/__init__.py +116 -0
  98. raxe/domain/telemetry/backpressure.py +424 -0
  99. raxe/domain/telemetry/event_creator.py +362 -0
  100. raxe/domain/telemetry/events.py +1282 -0
  101. raxe/domain/telemetry/priority.py +263 -0
  102. raxe/domain/telemetry/scan_telemetry_builder.py +670 -0
  103. raxe/infrastructure/__init__.py +25 -0
  104. raxe/infrastructure/analytics/__init__.py +18 -0
  105. raxe/infrastructure/analytics/aggregator.py +484 -0
  106. raxe/infrastructure/analytics/aggregator_optimized.py +184 -0
  107. raxe/infrastructure/analytics/engine.py +748 -0
  108. raxe/infrastructure/analytics/repository.py +409 -0
  109. raxe/infrastructure/analytics/streaks.py +467 -0
  110. raxe/infrastructure/analytics/views.py +178 -0
  111. raxe/infrastructure/cloud/__init__.py +9 -0
  112. raxe/infrastructure/config/__init__.py +56 -0
  113. raxe/infrastructure/config/endpoints.py +641 -0
  114. raxe/infrastructure/config/scan_config.py +352 -0
  115. raxe/infrastructure/config/yaml_config.py +459 -0
  116. raxe/infrastructure/database/__init__.py +10 -0
  117. raxe/infrastructure/database/connection.py +200 -0
  118. raxe/infrastructure/database/models.py +325 -0
  119. raxe/infrastructure/database/scan_history.py +764 -0
  120. raxe/infrastructure/ml/__init__.py +0 -0
  121. raxe/infrastructure/ml/download_progress.py +438 -0
  122. raxe/infrastructure/ml/model_downloader.py +457 -0
  123. raxe/infrastructure/models/__init__.py +16 -0
  124. raxe/infrastructure/models/discovery.py +461 -0
  125. raxe/infrastructure/packs/__init__.py +13 -0
  126. raxe/infrastructure/packs/loader.py +407 -0
  127. raxe/infrastructure/packs/registry.py +381 -0
  128. raxe/infrastructure/policies/__init__.py +16 -0
  129. raxe/infrastructure/policies/api_client.py +256 -0
  130. raxe/infrastructure/policies/validator.py +227 -0
  131. raxe/infrastructure/policies/yaml_loader.py +250 -0
  132. raxe/infrastructure/rules/__init__.py +18 -0
  133. raxe/infrastructure/rules/custom_loader.py +224 -0
  134. raxe/infrastructure/rules/versioning.py +222 -0
  135. raxe/infrastructure/rules/yaml_loader.py +286 -0
  136. raxe/infrastructure/security/__init__.py +31 -0
  137. raxe/infrastructure/security/auth.py +145 -0
  138. raxe/infrastructure/security/policy_validator.py +124 -0
  139. raxe/infrastructure/security/signatures.py +171 -0
  140. raxe/infrastructure/suppression/__init__.py +36 -0
  141. raxe/infrastructure/suppression/composite_repository.py +154 -0
  142. raxe/infrastructure/suppression/sqlite_repository.py +231 -0
  143. raxe/infrastructure/suppression/yaml_composite_repository.py +156 -0
  144. raxe/infrastructure/suppression/yaml_repository.py +510 -0
  145. raxe/infrastructure/telemetry/__init__.py +79 -0
  146. raxe/infrastructure/telemetry/acquisition.py +179 -0
  147. raxe/infrastructure/telemetry/config.py +254 -0
  148. raxe/infrastructure/telemetry/credential_store.py +947 -0
  149. raxe/infrastructure/telemetry/dual_queue.py +1123 -0
  150. raxe/infrastructure/telemetry/flush_helper.py +343 -0
  151. raxe/infrastructure/telemetry/flush_scheduler.py +776 -0
  152. raxe/infrastructure/telemetry/health_client.py +394 -0
  153. raxe/infrastructure/telemetry/hook.py +347 -0
  154. raxe/infrastructure/telemetry/queue.py +520 -0
  155. raxe/infrastructure/telemetry/sender.py +476 -0
  156. raxe/infrastructure/tracking/__init__.py +13 -0
  157. raxe/infrastructure/tracking/usage.py +389 -0
  158. raxe/integrations/__init__.py +55 -0
  159. raxe/integrations/availability.py +143 -0
  160. raxe/integrations/registry.py +122 -0
  161. raxe/integrations/utils.py +135 -0
  162. raxe/mcp/__init__.py +62 -0
  163. raxe/mcp/cli.py +97 -0
  164. raxe/mcp/server.py +409 -0
  165. raxe/monitoring/__init__.py +51 -0
  166. raxe/monitoring/metrics.py +372 -0
  167. raxe/monitoring/profiler.py +388 -0
  168. raxe/monitoring/server.py +136 -0
  169. raxe/packs/core/v1.0.0/pack.yaml +1394 -0
  170. raxe/packs/core/v1.0.0/rules/PI/pi-001@1.0.0.yaml +49 -0
  171. raxe/packs/core/v1.0.0/rules/PI/pi-006@1.0.0.yaml +48 -0
  172. raxe/packs/core/v1.0.0/rules/PI/pi-014@1.0.0.yaml +54 -0
  173. raxe/packs/core/v1.0.0/rules/PI/pi-017@1.0.0.yaml +52 -0
  174. raxe/packs/core/v1.0.0/rules/PI/pi-022@1.0.0.yaml +67 -0
  175. raxe/packs/core/v1.0.0/rules/PI/pi-023@1.0.0.yaml +91 -0
  176. raxe/packs/core/v1.0.0/rules/PI/pi-024@1.0.0.yaml +80 -0
  177. raxe/packs/core/v1.0.0/rules/PI/pi-025@1.0.0.yaml +81 -0
  178. raxe/packs/core/v1.0.0/rules/PI/pi-026@1.0.0.yaml +50 -0
  179. raxe/packs/core/v1.0.0/rules/PI/pi-027@1.0.0.yaml +77 -0
  180. raxe/packs/core/v1.0.0/rules/PI/pi-028@1.0.0.yaml +52 -0
  181. raxe/packs/core/v1.0.0/rules/PI/pi-029@1.0.0.yaml +51 -0
  182. raxe/packs/core/v1.0.0/rules/PI/pi-030@1.0.0.yaml +55 -0
  183. raxe/packs/core/v1.0.0/rules/PI/pi-033@1.0.0.yaml +50 -0
  184. raxe/packs/core/v1.0.0/rules/PI/pi-034@1.0.0.yaml +50 -0
  185. raxe/packs/core/v1.0.0/rules/PI/pi-035@1.0.0.yaml +50 -0
  186. raxe/packs/core/v1.0.0/rules/PI/pi-046@1.0.0.yaml +50 -0
  187. raxe/packs/core/v1.0.0/rules/PI/pi-047@1.0.0.yaml +50 -0
  188. raxe/packs/core/v1.0.0/rules/PI/pi-048@1.0.0.yaml +50 -0
  189. raxe/packs/core/v1.0.0/rules/PI/pi-049@1.0.0.yaml +50 -0
  190. raxe/packs/core/v1.0.0/rules/PI/pi-050@1.0.0.yaml +50 -0
  191. raxe/packs/core/v1.0.0/rules/PI/pi-068@1.0.0.yaml +50 -0
  192. raxe/packs/core/v1.0.0/rules/PI/pi-078@1.0.0.yaml +50 -0
  193. raxe/packs/core/v1.0.0/rules/PI/pi-2001@1.0.0.yaml +35 -0
  194. raxe/packs/core/v1.0.0/rules/PI/pi-2004@1.0.0.yaml +39 -0
  195. raxe/packs/core/v1.0.0/rules/PI/pi-201@1.0.0.yaml +43 -0
  196. raxe/packs/core/v1.0.0/rules/PI/pi-202@1.0.0.yaml +47 -0
  197. raxe/packs/core/v1.0.0/rules/PI/pi-203@1.0.0.yaml +46 -0
  198. raxe/packs/core/v1.0.0/rules/PI/pi-3007@1.0.0.yaml +44 -0
  199. raxe/packs/core/v1.0.0/rules/PI/pi-3016@1.0.0.yaml +44 -0
  200. raxe/packs/core/v1.0.0/rules/PI/pi-3026@1.0.0.yaml +39 -0
  201. raxe/packs/core/v1.0.0/rules/PI/pi-3027@1.0.0.yaml +64 -0
  202. raxe/packs/core/v1.0.0/rules/PI/pi-3028@1.0.0.yaml +51 -0
  203. raxe/packs/core/v1.0.0/rules/PI/pi-3029@1.0.0.yaml +53 -0
  204. raxe/packs/core/v1.0.0/rules/PI/pi-3030@1.0.0.yaml +50 -0
  205. raxe/packs/core/v1.0.0/rules/PI/pi-3031@1.0.0.yaml +50 -0
  206. raxe/packs/core/v1.0.0/rules/PI/pi-3032@1.0.0.yaml +50 -0
  207. raxe/packs/core/v1.0.0/rules/PI/pi-3033@1.0.0.yaml +56 -0
  208. raxe/packs/core/v1.0.0/rules/PI/pi-3034@1.0.0.yaml +50 -0
  209. raxe/packs/core/v1.0.0/rules/PI/pi-79@1.0.0.yaml +38 -0
  210. raxe/packs/core/v1.0.0/rules/PI/pi-80@1.0.0.yaml +38 -0
  211. raxe/packs/core/v1.0.0/rules/PI/pi-81@1.0.0.yaml +38 -0
  212. raxe/packs/core/v1.0.0/rules/PI/pi-82@1.0.0.yaml +38 -0
  213. raxe/packs/core/v1.0.0/rules/PI/pi-83@1.0.0.yaml +38 -0
  214. raxe/packs/core/v1.0.0/rules/PI/pi-84@1.0.0.yaml +38 -0
  215. raxe/packs/core/v1.0.0/rules/PI/pi-85@1.0.0.yaml +38 -0
  216. raxe/packs/core/v1.0.0/rules/PI/pi-86@1.0.0.yaml +38 -0
  217. raxe/packs/core/v1.0.0/rules/PI/pi-87@1.0.0.yaml +38 -0
  218. raxe/packs/core/v1.0.0/rules/PI/pi-88@1.0.0.yaml +38 -0
  219. raxe/packs/core/v1.0.0/rules/PI/pi-89@1.0.0.yaml +38 -0
  220. raxe/packs/core/v1.0.0/rules/PI/pi-90@1.0.0.yaml +38 -0
  221. raxe/packs/core/v1.0.0/rules/PI/pi-91@1.0.0.yaml +38 -0
  222. raxe/packs/core/v1.0.0/rules/PI/pi-92@1.0.0.yaml +38 -0
  223. raxe/packs/core/v1.0.0/rules/PI/pi-93@1.0.0.yaml +38 -0
  224. raxe/packs/core/v1.0.0/rules/PI/pi-94@1.0.0.yaml +38 -0
  225. raxe/packs/core/v1.0.0/rules/PI/pi-95@1.0.0.yaml +38 -0
  226. raxe/packs/core/v1.0.0/rules/PI/pi-96@1.0.0.yaml +38 -0
  227. raxe/packs/core/v1.0.0/rules/PI/pi-97@1.0.0.yaml +38 -0
  228. raxe/packs/core/v1.0.0/rules/PI/pi-98@1.0.0.yaml +38 -0
  229. raxe/packs/core/v1.0.0/rules/cmd/cmd-001@1.0.0.yaml +48 -0
  230. raxe/packs/core/v1.0.0/rules/cmd/cmd-007@1.0.0.yaml +48 -0
  231. raxe/packs/core/v1.0.0/rules/cmd/cmd-015@1.0.0.yaml +56 -0
  232. raxe/packs/core/v1.0.0/rules/cmd/cmd-016@1.0.0.yaml +46 -0
  233. raxe/packs/core/v1.0.0/rules/cmd/cmd-017@1.0.0.yaml +57 -0
  234. raxe/packs/core/v1.0.0/rules/cmd/cmd-021@1.0.0.yaml +46 -0
  235. raxe/packs/core/v1.0.0/rules/cmd/cmd-022@1.0.0.yaml +46 -0
  236. raxe/packs/core/v1.0.0/rules/cmd/cmd-023@1.0.0.yaml +78 -0
  237. raxe/packs/core/v1.0.0/rules/cmd/cmd-024@1.0.0.yaml +46 -0
  238. raxe/packs/core/v1.0.0/rules/cmd/cmd-025@1.0.0.yaml +93 -0
  239. raxe/packs/core/v1.0.0/rules/cmd/cmd-026@1.0.0.yaml +81 -0
  240. raxe/packs/core/v1.0.0/rules/cmd/cmd-027@1.0.0.yaml +82 -0
  241. raxe/packs/core/v1.0.0/rules/cmd/cmd-028@1.0.0.yaml +46 -0
  242. raxe/packs/core/v1.0.0/rules/cmd/cmd-033@1.0.0.yaml +48 -0
  243. raxe/packs/core/v1.0.0/rules/cmd/cmd-036@1.0.0.yaml +47 -0
  244. raxe/packs/core/v1.0.0/rules/cmd/cmd-037@1.0.0.yaml +44 -0
  245. raxe/packs/core/v1.0.0/rules/cmd/cmd-052@1.0.0.yaml +43 -0
  246. raxe/packs/core/v1.0.0/rules/cmd/cmd-054@1.0.0.yaml +44 -0
  247. raxe/packs/core/v1.0.0/rules/cmd/cmd-056@1.0.0.yaml +43 -0
  248. raxe/packs/core/v1.0.0/rules/cmd/cmd-065@1.0.0.yaml +46 -0
  249. raxe/packs/core/v1.0.0/rules/cmd/cmd-075@1.0.0.yaml +45 -0
  250. raxe/packs/core/v1.0.0/rules/cmd/cmd-079@1.0.0.yaml +44 -0
  251. raxe/packs/core/v1.0.0/rules/cmd/cmd-1080@1.0.0.yaml +41 -0
  252. raxe/packs/core/v1.0.0/rules/cmd/cmd-1090@1.0.0.yaml +41 -0
  253. raxe/packs/core/v1.0.0/rules/cmd/cmd-1104@1.0.0.yaml +44 -0
  254. raxe/packs/core/v1.0.0/rules/cmd/cmd-1105@1.0.0.yaml +41 -0
  255. raxe/packs/core/v1.0.0/rules/cmd/cmd-1112@1.0.0.yaml +44 -0
  256. raxe/packs/core/v1.0.0/rules/cmd/cmd-201@1.0.0.yaml +47 -0
  257. raxe/packs/core/v1.0.0/rules/cmd/cmd-202@1.0.0.yaml +42 -0
  258. raxe/packs/core/v1.0.0/rules/cmd/cmd-203@1.0.0.yaml +43 -0
  259. raxe/packs/core/v1.0.0/rules/cmd/cmd-204@1.0.0.yaml +47 -0
  260. raxe/packs/core/v1.0.0/rules/cmd/cmd-205@1.0.0.yaml +44 -0
  261. raxe/packs/core/v1.0.0/rules/cmd/cmd-206@1.0.0.yaml +47 -0
  262. raxe/packs/core/v1.0.0/rules/cmd/cmd-207@1.0.0.yaml +46 -0
  263. raxe/packs/core/v1.0.0/rules/cmd/cmd-208@1.0.0.yaml +42 -0
  264. raxe/packs/core/v1.0.0/rules/cmd/cmd-209@1.0.0.yaml +38 -0
  265. raxe/packs/core/v1.0.0/rules/cmd/cmd-210@1.0.0.yaml +38 -0
  266. raxe/packs/core/v1.0.0/rules/cmd/cmd-211@1.0.0.yaml +38 -0
  267. raxe/packs/core/v1.0.0/rules/cmd/cmd-212@1.0.0.yaml +38 -0
  268. raxe/packs/core/v1.0.0/rules/cmd/cmd-213@1.0.0.yaml +38 -0
  269. raxe/packs/core/v1.0.0/rules/cmd/cmd-214@1.0.0.yaml +38 -0
  270. raxe/packs/core/v1.0.0/rules/cmd/cmd-215@1.0.0.yaml +38 -0
  271. raxe/packs/core/v1.0.0/rules/cmd/cmd-216@1.0.0.yaml +38 -0
  272. raxe/packs/core/v1.0.0/rules/cmd/cmd-217@1.0.0.yaml +38 -0
  273. raxe/packs/core/v1.0.0/rules/cmd/cmd-218@1.0.0.yaml +38 -0
  274. raxe/packs/core/v1.0.0/rules/cmd/cmd-219@1.0.0.yaml +38 -0
  275. raxe/packs/core/v1.0.0/rules/cmd/cmd-220@1.0.0.yaml +38 -0
  276. raxe/packs/core/v1.0.0/rules/cmd/cmd-221@1.0.0.yaml +38 -0
  277. raxe/packs/core/v1.0.0/rules/cmd/cmd-222@1.0.0.yaml +38 -0
  278. raxe/packs/core/v1.0.0/rules/cmd/cmd-223@1.0.0.yaml +38 -0
  279. raxe/packs/core/v1.0.0/rules/cmd/cmd-224@1.0.0.yaml +38 -0
  280. raxe/packs/core/v1.0.0/rules/cmd/cmd-225@1.0.0.yaml +38 -0
  281. raxe/packs/core/v1.0.0/rules/cmd/cmd-226@1.0.0.yaml +38 -0
  282. raxe/packs/core/v1.0.0/rules/cmd/cmd-227@1.0.0.yaml +38 -0
  283. raxe/packs/core/v1.0.0/rules/cmd/cmd-228@1.0.0.yaml +38 -0
  284. raxe/packs/core/v1.0.0/rules/cmd/cmd-229@1.0.0.yaml +38 -0
  285. raxe/packs/core/v1.0.0/rules/cmd/cmd-230@1.0.0.yaml +38 -0
  286. raxe/packs/core/v1.0.0/rules/cmd/cmd-231@1.0.0.yaml +38 -0
  287. raxe/packs/core/v1.0.0/rules/cmd/cmd-232@1.0.0.yaml +38 -0
  288. raxe/packs/core/v1.0.0/rules/cmd/cmd-233@1.0.0.yaml +38 -0
  289. raxe/packs/core/v1.0.0/rules/cmd/cmd-234@1.0.0.yaml +38 -0
  290. raxe/packs/core/v1.0.0/rules/cmd/cmd-235@1.0.0.yaml +38 -0
  291. raxe/packs/core/v1.0.0/rules/cmd/cmd-236@1.0.0.yaml +38 -0
  292. raxe/packs/core/v1.0.0/rules/cmd/cmd-237@1.0.0.yaml +38 -0
  293. raxe/packs/core/v1.0.0/rules/cmd/cmd-238@1.0.0.yaml +38 -0
  294. raxe/packs/core/v1.0.0/rules/enc/enc-001@1.0.0.yaml +48 -0
  295. raxe/packs/core/v1.0.0/rules/enc/enc-013@1.0.0.yaml +46 -0
  296. raxe/packs/core/v1.0.0/rules/enc/enc-019@1.0.0.yaml +43 -0
  297. raxe/packs/core/v1.0.0/rules/enc/enc-020@1.0.0.yaml +47 -0
  298. raxe/packs/core/v1.0.0/rules/enc/enc-024@1.0.0.yaml +46 -0
  299. raxe/packs/core/v1.0.0/rules/enc/enc-029@1.0.0.yaml +44 -0
  300. raxe/packs/core/v1.0.0/rules/enc/enc-038@1.0.0.yaml +44 -0
  301. raxe/packs/core/v1.0.0/rules/enc/enc-044@1.0.0.yaml +46 -0
  302. raxe/packs/core/v1.0.0/rules/enc/enc-067@1.0.0.yaml +42 -0
  303. raxe/packs/core/v1.0.0/rules/enc/enc-069@1.0.0.yaml +42 -0
  304. raxe/packs/core/v1.0.0/rules/enc/enc-100@1.0.0.yaml +38 -0
  305. raxe/packs/core/v1.0.0/rules/enc/enc-101@1.0.0.yaml +38 -0
  306. raxe/packs/core/v1.0.0/rules/enc/enc-102@1.0.0.yaml +38 -0
  307. raxe/packs/core/v1.0.0/rules/enc/enc-103@1.0.0.yaml +38 -0
  308. raxe/packs/core/v1.0.0/rules/enc/enc-104@1.0.0.yaml +38 -0
  309. raxe/packs/core/v1.0.0/rules/enc/enc-105@1.0.0.yaml +38 -0
  310. raxe/packs/core/v1.0.0/rules/enc/enc-106@1.0.0.yaml +38 -0
  311. raxe/packs/core/v1.0.0/rules/enc/enc-107@1.0.0.yaml +38 -0
  312. raxe/packs/core/v1.0.0/rules/enc/enc-108@1.0.0.yaml +38 -0
  313. raxe/packs/core/v1.0.0/rules/enc/enc-109@1.0.0.yaml +38 -0
  314. raxe/packs/core/v1.0.0/rules/enc/enc-110@1.0.0.yaml +38 -0
  315. raxe/packs/core/v1.0.0/rules/enc/enc-111@1.0.0.yaml +38 -0
  316. raxe/packs/core/v1.0.0/rules/enc/enc-112@1.0.0.yaml +38 -0
  317. raxe/packs/core/v1.0.0/rules/enc/enc-113@1.0.0.yaml +38 -0
  318. raxe/packs/core/v1.0.0/rules/enc/enc-114@1.0.0.yaml +38 -0
  319. raxe/packs/core/v1.0.0/rules/enc/enc-115@1.0.0.yaml +38 -0
  320. raxe/packs/core/v1.0.0/rules/enc/enc-116@1.0.0.yaml +38 -0
  321. raxe/packs/core/v1.0.0/rules/enc/enc-117@1.0.0.yaml +38 -0
  322. raxe/packs/core/v1.0.0/rules/enc/enc-118@1.0.0.yaml +38 -0
  323. raxe/packs/core/v1.0.0/rules/enc/enc-119@1.0.0.yaml +38 -0
  324. raxe/packs/core/v1.0.0/rules/enc/enc-120@1.0.0.yaml +38 -0
  325. raxe/packs/core/v1.0.0/rules/enc/enc-201@1.0.0.yaml +37 -0
  326. raxe/packs/core/v1.0.0/rules/enc/enc-202@1.0.0.yaml +41 -0
  327. raxe/packs/core/v1.0.0/rules/enc/enc-203@1.0.0.yaml +41 -0
  328. raxe/packs/core/v1.0.0/rules/enc/enc-3004@1.0.0.yaml +40 -0
  329. raxe/packs/core/v1.0.0/rules/enc/enc-3006@1.0.0.yaml +40 -0
  330. raxe/packs/core/v1.0.0/rules/enc/enc-3011@1.0.0.yaml +40 -0
  331. raxe/packs/core/v1.0.0/rules/enc/enc-5016@1.0.0.yaml +46 -0
  332. raxe/packs/core/v1.0.0/rules/enc/enc-6001@1.0.0.yaml +53 -0
  333. raxe/packs/core/v1.0.0/rules/enc/enc-6002@1.0.0.yaml +41 -0
  334. raxe/packs/core/v1.0.0/rules/enc/enc-70@1.0.0.yaml +38 -0
  335. raxe/packs/core/v1.0.0/rules/enc/enc-71@1.0.0.yaml +38 -0
  336. raxe/packs/core/v1.0.0/rules/enc/enc-72@1.0.0.yaml +38 -0
  337. raxe/packs/core/v1.0.0/rules/enc/enc-73@1.0.0.yaml +38 -0
  338. raxe/packs/core/v1.0.0/rules/enc/enc-74@1.0.0.yaml +38 -0
  339. raxe/packs/core/v1.0.0/rules/enc/enc-75@1.0.0.yaml +38 -0
  340. raxe/packs/core/v1.0.0/rules/enc/enc-76@1.0.0.yaml +38 -0
  341. raxe/packs/core/v1.0.0/rules/enc/enc-77@1.0.0.yaml +38 -0
  342. raxe/packs/core/v1.0.0/rules/enc/enc-78@1.0.0.yaml +38 -0
  343. raxe/packs/core/v1.0.0/rules/enc/enc-79@1.0.0.yaml +38 -0
  344. raxe/packs/core/v1.0.0/rules/enc/enc-80@1.0.0.yaml +38 -0
  345. raxe/packs/core/v1.0.0/rules/enc/enc-81@1.0.0.yaml +38 -0
  346. raxe/packs/core/v1.0.0/rules/enc/enc-82@1.0.0.yaml +38 -0
  347. raxe/packs/core/v1.0.0/rules/enc/enc-83@1.0.0.yaml +38 -0
  348. raxe/packs/core/v1.0.0/rules/enc/enc-84@1.0.0.yaml +38 -0
  349. raxe/packs/core/v1.0.0/rules/enc/enc-85@1.0.0.yaml +38 -0
  350. raxe/packs/core/v1.0.0/rules/enc/enc-86@1.0.0.yaml +38 -0
  351. raxe/packs/core/v1.0.0/rules/enc/enc-87@1.0.0.yaml +38 -0
  352. raxe/packs/core/v1.0.0/rules/enc/enc-88@1.0.0.yaml +38 -0
  353. raxe/packs/core/v1.0.0/rules/enc/enc-89@1.0.0.yaml +38 -0
  354. raxe/packs/core/v1.0.0/rules/enc/enc-90@1.0.0.yaml +38 -0
  355. raxe/packs/core/v1.0.0/rules/enc/enc-91@1.0.0.yaml +38 -0
  356. raxe/packs/core/v1.0.0/rules/enc/enc-92@1.0.0.yaml +38 -0
  357. raxe/packs/core/v1.0.0/rules/enc/enc-93@1.0.0.yaml +38 -0
  358. raxe/packs/core/v1.0.0/rules/enc/enc-94@1.0.0.yaml +38 -0
  359. raxe/packs/core/v1.0.0/rules/enc/enc-95@1.0.0.yaml +38 -0
  360. raxe/packs/core/v1.0.0/rules/enc/enc-96@1.0.0.yaml +38 -0
  361. raxe/packs/core/v1.0.0/rules/enc/enc-97@1.0.0.yaml +38 -0
  362. raxe/packs/core/v1.0.0/rules/enc/enc-98@1.0.0.yaml +38 -0
  363. raxe/packs/core/v1.0.0/rules/enc/enc-99@1.0.0.yaml +38 -0
  364. raxe/packs/core/v1.0.0/rules/hc/hc-001@1.0.0.yaml +73 -0
  365. raxe/packs/core/v1.0.0/rules/hc/hc-002@1.0.0.yaml +71 -0
  366. raxe/packs/core/v1.0.0/rules/hc/hc-003@1.0.0.yaml +65 -0
  367. raxe/packs/core/v1.0.0/rules/hc/hc-004@1.0.0.yaml +73 -0
  368. raxe/packs/core/v1.0.0/rules/hc/hc-101@1.0.0.yaml +47 -0
  369. raxe/packs/core/v1.0.0/rules/hc/hc-102@1.0.0.yaml +47 -0
  370. raxe/packs/core/v1.0.0/rules/hc/hc-103@1.0.0.yaml +47 -0
  371. raxe/packs/core/v1.0.0/rules/hc/hc-104@1.0.0.yaml +47 -0
  372. raxe/packs/core/v1.0.0/rules/hc/hc-105@1.0.0.yaml +48 -0
  373. raxe/packs/core/v1.0.0/rules/hc/hc-106@1.0.0.yaml +40 -0
  374. raxe/packs/core/v1.0.0/rules/hc/hc-107@1.0.0.yaml +47 -0
  375. raxe/packs/core/v1.0.0/rules/hc/hc-108@1.0.0.yaml +47 -0
  376. raxe/packs/core/v1.0.0/rules/hc/hc-109@1.0.0.yaml +50 -0
  377. raxe/packs/core/v1.0.0/rules/hc/hc-110@1.0.0.yaml +56 -0
  378. raxe/packs/core/v1.0.0/rules/hc/hc-111@1.0.0.yaml +49 -0
  379. raxe/packs/core/v1.0.0/rules/hc/hc-112@1.0.0.yaml +53 -0
  380. raxe/packs/core/v1.0.0/rules/hc/hc-113@1.0.0.yaml +52 -0
  381. raxe/packs/core/v1.0.0/rules/hc/hc-114@1.0.0.yaml +52 -0
  382. raxe/packs/core/v1.0.0/rules/hc/hc-115@1.0.0.yaml +52 -0
  383. raxe/packs/core/v1.0.0/rules/hc/hc-116@1.0.0.yaml +53 -0
  384. raxe/packs/core/v1.0.0/rules/hc/hc-117@1.0.0.yaml +54 -0
  385. raxe/packs/core/v1.0.0/rules/hc/hc-118@1.0.0.yaml +52 -0
  386. raxe/packs/core/v1.0.0/rules/hc/hc-119@1.0.0.yaml +51 -0
  387. raxe/packs/core/v1.0.0/rules/hc/hc-120@1.0.0.yaml +52 -0
  388. raxe/packs/core/v1.0.0/rules/hc/hc-121@1.0.0.yaml +51 -0
  389. raxe/packs/core/v1.0.0/rules/hc/hc-122@1.0.0.yaml +51 -0
  390. raxe/packs/core/v1.0.0/rules/hc/hc-123@1.0.0.yaml +52 -0
  391. raxe/packs/core/v1.0.0/rules/hc/hc-124@1.0.0.yaml +53 -0
  392. raxe/packs/core/v1.0.0/rules/hc/hc-125@1.0.0.yaml +53 -0
  393. raxe/packs/core/v1.0.0/rules/hc/hc-126@1.0.0.yaml +53 -0
  394. raxe/packs/core/v1.0.0/rules/hc/hc-127@1.0.0.yaml +53 -0
  395. raxe/packs/core/v1.0.0/rules/hc/hc-128@1.0.0.yaml +53 -0
  396. raxe/packs/core/v1.0.0/rules/hc/hc-129@1.0.0.yaml +51 -0
  397. raxe/packs/core/v1.0.0/rules/hc/hc-130@1.0.0.yaml +51 -0
  398. raxe/packs/core/v1.0.0/rules/hc/hc-131@1.0.0.yaml +51 -0
  399. raxe/packs/core/v1.0.0/rules/hc/hc-132@1.0.0.yaml +51 -0
  400. raxe/packs/core/v1.0.0/rules/hc/hc-133@1.0.0.yaml +53 -0
  401. raxe/packs/core/v1.0.0/rules/hc/hc-134@1.0.0.yaml +51 -0
  402. raxe/packs/core/v1.0.0/rules/hc/hc-135@1.0.0.yaml +51 -0
  403. raxe/packs/core/v1.0.0/rules/hc/hc-136@1.0.0.yaml +51 -0
  404. raxe/packs/core/v1.0.0/rules/hc/hc-137@1.0.0.yaml +51 -0
  405. raxe/packs/core/v1.0.0/rules/hc/hc-138@1.0.0.yaml +51 -0
  406. raxe/packs/core/v1.0.0/rules/hc/hc-139@1.0.0.yaml +51 -0
  407. raxe/packs/core/v1.0.0/rules/hc/hc-140@1.0.0.yaml +51 -0
  408. raxe/packs/core/v1.0.0/rules/hc/hc-141@1.0.0.yaml +41 -0
  409. raxe/packs/core/v1.0.0/rules/hc/hc-142@1.0.0.yaml +37 -0
  410. raxe/packs/core/v1.0.0/rules/hc/hc-143@1.0.0.yaml +37 -0
  411. raxe/packs/core/v1.0.0/rules/hc/hc-144@1.0.0.yaml +37 -0
  412. raxe/packs/core/v1.0.0/rules/hc/hc-145@1.0.0.yaml +37 -0
  413. raxe/packs/core/v1.0.0/rules/hc/hc-146@1.0.0.yaml +37 -0
  414. raxe/packs/core/v1.0.0/rules/hc/hc-147@1.0.0.yaml +37 -0
  415. raxe/packs/core/v1.0.0/rules/hc/hc-148@1.0.0.yaml +37 -0
  416. raxe/packs/core/v1.0.0/rules/hc/hc-149@1.0.0.yaml +37 -0
  417. raxe/packs/core/v1.0.0/rules/hc/hc-150@1.0.0.yaml +37 -0
  418. raxe/packs/core/v1.0.0/rules/hc/hc-151@1.0.0.yaml +37 -0
  419. raxe/packs/core/v1.0.0/rules/hc/hc-152@1.0.0.yaml +37 -0
  420. raxe/packs/core/v1.0.0/rules/hc/hc-153@1.0.0.yaml +37 -0
  421. raxe/packs/core/v1.0.0/rules/hc/hc-154@1.0.0.yaml +37 -0
  422. raxe/packs/core/v1.0.0/rules/hc/hc-155@1.0.0.yaml +37 -0
  423. raxe/packs/core/v1.0.0/rules/hc/hc-156@1.0.0.yaml +37 -0
  424. raxe/packs/core/v1.0.0/rules/hc/hc-157@1.0.0.yaml +37 -0
  425. raxe/packs/core/v1.0.0/rules/hc/hc-158@1.0.0.yaml +37 -0
  426. raxe/packs/core/v1.0.0/rules/hc/hc-159@1.0.0.yaml +37 -0
  427. raxe/packs/core/v1.0.0/rules/hc/hc-160@1.0.0.yaml +37 -0
  428. raxe/packs/core/v1.0.0/rules/hc/hc-161@1.0.0.yaml +37 -0
  429. raxe/packs/core/v1.0.0/rules/jb/jb-001@1.0.0.yaml +47 -0
  430. raxe/packs/core/v1.0.0/rules/jb/jb-009@1.0.0.yaml +47 -0
  431. raxe/packs/core/v1.0.0/rules/jb/jb-020@1.0.0.yaml +47 -0
  432. raxe/packs/core/v1.0.0/rules/jb/jb-021@1.0.0.yaml +46 -0
  433. raxe/packs/core/v1.0.0/rules/jb/jb-022@1.0.0.yaml +47 -0
  434. raxe/packs/core/v1.0.0/rules/jb/jb-028@1.0.0.yaml +43 -0
  435. raxe/packs/core/v1.0.0/rules/jb/jb-033@1.0.0.yaml +46 -0
  436. raxe/packs/core/v1.0.0/rules/jb/jb-034@1.0.0.yaml +46 -0
  437. raxe/packs/core/v1.0.0/rules/jb/jb-036@1.0.0.yaml +41 -0
  438. raxe/packs/core/v1.0.0/rules/jb/jb-039@1.0.0.yaml +41 -0
  439. raxe/packs/core/v1.0.0/rules/jb/jb-056@1.0.0.yaml +38 -0
  440. raxe/packs/core/v1.0.0/rules/jb/jb-066@1.0.0.yaml +37 -0
  441. raxe/packs/core/v1.0.0/rules/jb/jb-076@1.0.0.yaml +37 -0
  442. raxe/packs/core/v1.0.0/rules/jb/jb-098@1.0.0.yaml +46 -0
  443. raxe/packs/core/v1.0.0/rules/jb/jb-103@1.0.0.yaml +47 -0
  444. raxe/packs/core/v1.0.0/rules/jb/jb-104@1.0.0.yaml +52 -0
  445. raxe/packs/core/v1.0.0/rules/jb/jb-105@1.0.0.yaml +56 -0
  446. raxe/packs/core/v1.0.0/rules/jb/jb-110@1.0.0.yaml +56 -0
  447. raxe/packs/core/v1.0.0/rules/jb/jb-111@1.0.0.yaml +57 -0
  448. raxe/packs/core/v1.0.0/rules/jb/jb-112@1.0.0.yaml +38 -0
  449. raxe/packs/core/v1.0.0/rules/jb/jb-113@1.0.0.yaml +38 -0
  450. raxe/packs/core/v1.0.0/rules/jb/jb-114@1.0.0.yaml +38 -0
  451. raxe/packs/core/v1.0.0/rules/jb/jb-115@1.0.0.yaml +38 -0
  452. raxe/packs/core/v1.0.0/rules/jb/jb-116@1.0.0.yaml +38 -0
  453. raxe/packs/core/v1.0.0/rules/jb/jb-117@1.0.0.yaml +38 -0
  454. raxe/packs/core/v1.0.0/rules/jb/jb-118@1.0.0.yaml +38 -0
  455. raxe/packs/core/v1.0.0/rules/jb/jb-119@1.0.0.yaml +38 -0
  456. raxe/packs/core/v1.0.0/rules/jb/jb-120@1.0.0.yaml +38 -0
  457. raxe/packs/core/v1.0.0/rules/jb/jb-121@1.0.0.yaml +38 -0
  458. raxe/packs/core/v1.0.0/rules/jb/jb-122@1.0.0.yaml +38 -0
  459. raxe/packs/core/v1.0.0/rules/jb/jb-123@1.0.0.yaml +38 -0
  460. raxe/packs/core/v1.0.0/rules/jb/jb-124@1.0.0.yaml +38 -0
  461. raxe/packs/core/v1.0.0/rules/jb/jb-125@1.0.0.yaml +38 -0
  462. raxe/packs/core/v1.0.0/rules/jb/jb-126@1.0.0.yaml +38 -0
  463. raxe/packs/core/v1.0.0/rules/jb/jb-127@1.0.0.yaml +38 -0
  464. raxe/packs/core/v1.0.0/rules/jb/jb-128@1.0.0.yaml +38 -0
  465. raxe/packs/core/v1.0.0/rules/jb/jb-129@1.0.0.yaml +38 -0
  466. raxe/packs/core/v1.0.0/rules/jb/jb-130@1.0.0.yaml +38 -0
  467. raxe/packs/core/v1.0.0/rules/jb/jb-131@1.0.0.yaml +38 -0
  468. raxe/packs/core/v1.0.0/rules/jb/jb-132@1.0.0.yaml +38 -0
  469. raxe/packs/core/v1.0.0/rules/jb/jb-133@1.0.0.yaml +38 -0
  470. raxe/packs/core/v1.0.0/rules/jb/jb-134@1.0.0.yaml +38 -0
  471. raxe/packs/core/v1.0.0/rules/jb/jb-135@1.0.0.yaml +38 -0
  472. raxe/packs/core/v1.0.0/rules/jb/jb-136@1.0.0.yaml +38 -0
  473. raxe/packs/core/v1.0.0/rules/jb/jb-137@1.0.0.yaml +38 -0
  474. raxe/packs/core/v1.0.0/rules/jb/jb-138@1.0.0.yaml +38 -0
  475. raxe/packs/core/v1.0.0/rules/jb/jb-139@1.0.0.yaml +38 -0
  476. raxe/packs/core/v1.0.0/rules/jb/jb-140@1.0.0.yaml +38 -0
  477. raxe/packs/core/v1.0.0/rules/jb/jb-141@1.0.0.yaml +38 -0
  478. raxe/packs/core/v1.0.0/rules/jb/jb-142@1.0.0.yaml +38 -0
  479. raxe/packs/core/v1.0.0/rules/jb/jb-143@1.0.0.yaml +38 -0
  480. raxe/packs/core/v1.0.0/rules/jb/jb-144@1.0.0.yaml +38 -0
  481. raxe/packs/core/v1.0.0/rules/jb/jb-145@1.0.0.yaml +38 -0
  482. raxe/packs/core/v1.0.0/rules/jb/jb-146@1.0.0.yaml +38 -0
  483. raxe/packs/core/v1.0.0/rules/jb/jb-147@1.0.0.yaml +38 -0
  484. raxe/packs/core/v1.0.0/rules/jb/jb-148@1.0.0.yaml +38 -0
  485. raxe/packs/core/v1.0.0/rules/jb/jb-149@1.0.0.yaml +38 -0
  486. raxe/packs/core/v1.0.0/rules/jb/jb-150@1.0.0.yaml +38 -0
  487. raxe/packs/core/v1.0.0/rules/jb/jb-151@1.0.0.yaml +38 -0
  488. raxe/packs/core/v1.0.0/rules/jb/jb-152@1.0.0.yaml +38 -0
  489. raxe/packs/core/v1.0.0/rules/jb/jb-153@1.0.0.yaml +38 -0
  490. raxe/packs/core/v1.0.0/rules/jb/jb-154@1.0.0.yaml +38 -0
  491. raxe/packs/core/v1.0.0/rules/jb/jb-155@1.0.0.yaml +38 -0
  492. raxe/packs/core/v1.0.0/rules/jb/jb-156@1.0.0.yaml +38 -0
  493. raxe/packs/core/v1.0.0/rules/jb/jb-157@1.0.0.yaml +38 -0
  494. raxe/packs/core/v1.0.0/rules/jb/jb-158@1.0.0.yaml +38 -0
  495. raxe/packs/core/v1.0.0/rules/jb/jb-159@1.0.0.yaml +38 -0
  496. raxe/packs/core/v1.0.0/rules/jb/jb-160@1.0.0.yaml +38 -0
  497. raxe/packs/core/v1.0.0/rules/jb/jb-161@1.0.0.yaml +38 -0
  498. raxe/packs/core/v1.0.0/rules/jb/jb-162@1.0.0.yaml +38 -0
  499. raxe/packs/core/v1.0.0/rules/jb/jb-201@1.0.0.yaml +40 -0
  500. raxe/packs/core/v1.0.0/rules/jb/jb-202@1.0.0.yaml +41 -0
  501. raxe/packs/core/v1.0.0/rules/jb/jb-203@1.0.0.yaml +51 -0
  502. raxe/packs/core/v1.0.0/rules/jb/jb-204@1.0.0.yaml +50 -0
  503. raxe/packs/core/v1.0.0/rules/jb/jb-205@1.0.0.yaml +50 -0
  504. raxe/packs/core/v1.0.0/rules/jb/jb-206@1.0.0.yaml +50 -0
  505. raxe/packs/core/v1.0.0/rules/jb/jb-207@1.0.0.yaml +49 -0
  506. raxe/packs/core/v1.0.0/rules/pii/pii-001@1.0.0.yaml +48 -0
  507. raxe/packs/core/v1.0.0/rules/pii/pii-009@1.0.0.yaml +48 -0
  508. raxe/packs/core/v1.0.0/rules/pii/pii-012@1.0.0.yaml +48 -0
  509. raxe/packs/core/v1.0.0/rules/pii/pii-017@1.0.0.yaml +48 -0
  510. raxe/packs/core/v1.0.0/rules/pii/pii-022@1.0.0.yaml +47 -0
  511. raxe/packs/core/v1.0.0/rules/pii/pii-025@1.0.0.yaml +47 -0
  512. raxe/packs/core/v1.0.0/rules/pii/pii-027@1.0.0.yaml +47 -0
  513. raxe/packs/core/v1.0.0/rules/pii/pii-028@1.0.0.yaml +47 -0
  514. raxe/packs/core/v1.0.0/rules/pii/pii-034@1.0.0.yaml +47 -0
  515. raxe/packs/core/v1.0.0/rules/pii/pii-037@1.0.0.yaml +47 -0
  516. raxe/packs/core/v1.0.0/rules/pii/pii-040@1.0.0.yaml +47 -0
  517. raxe/packs/core/v1.0.0/rules/pii/pii-041@1.0.0.yaml +47 -0
  518. raxe/packs/core/v1.0.0/rules/pii/pii-044@1.0.0.yaml +47 -0
  519. raxe/packs/core/v1.0.0/rules/pii/pii-050@1.0.0.yaml +57 -0
  520. raxe/packs/core/v1.0.0/rules/pii/pii-051@1.0.0.yaml +53 -0
  521. raxe/packs/core/v1.0.0/rules/pii/pii-052@1.0.0.yaml +52 -0
  522. raxe/packs/core/v1.0.0/rules/pii/pii-053@1.0.0.yaml +56 -0
  523. raxe/packs/core/v1.0.0/rules/pii/pii-054@1.0.0.yaml +53 -0
  524. raxe/packs/core/v1.0.0/rules/pii/pii-055@1.0.0.yaml +51 -0
  525. raxe/packs/core/v1.0.0/rules/pii/pii-056@1.0.0.yaml +51 -0
  526. raxe/packs/core/v1.0.0/rules/pii/pii-058@1.0.0.yaml +47 -0
  527. raxe/packs/core/v1.0.0/rules/pii/pii-2015@1.0.0.yaml +41 -0
  528. raxe/packs/core/v1.0.0/rules/pii/pii-2025@1.0.0.yaml +35 -0
  529. raxe/packs/core/v1.0.0/rules/pii/pii-2026@1.0.0.yaml +39 -0
  530. raxe/packs/core/v1.0.0/rules/pii/pii-2035@1.0.0.yaml +39 -0
  531. raxe/packs/core/v1.0.0/rules/pii/pii-2037@1.0.0.yaml +39 -0
  532. raxe/packs/core/v1.0.0/rules/pii/pii-2042@1.0.0.yaml +39 -0
  533. raxe/packs/core/v1.0.0/rules/pii/pii-3001@1.0.0.yaml +39 -0
  534. raxe/packs/core/v1.0.0/rules/pii/pii-3002@1.0.0.yaml +41 -0
  535. raxe/packs/core/v1.0.0/rules/pii/pii-3003@1.0.0.yaml +36 -0
  536. raxe/packs/core/v1.0.0/rules/pii/pii-3004@1.0.0.yaml +41 -0
  537. raxe/packs/core/v1.0.0/rules/pii/pii-3005@1.0.0.yaml +39 -0
  538. raxe/packs/core/v1.0.0/rules/pii/pii-3006@1.0.0.yaml +35 -0
  539. raxe/packs/core/v1.0.0/rules/pii/pii-3007@1.0.0.yaml +37 -0
  540. raxe/packs/core/v1.0.0/rules/pii/pii-3008@1.0.0.yaml +35 -0
  541. raxe/packs/core/v1.0.0/rules/pii/pii-3009@1.0.0.yaml +42 -0
  542. raxe/packs/core/v1.0.0/rules/pii/pii-3010@1.0.0.yaml +39 -0
  543. raxe/packs/core/v1.0.0/rules/pii/pii-3011@1.0.0.yaml +35 -0
  544. raxe/packs/core/v1.0.0/rules/pii/pii-3012@1.0.0.yaml +35 -0
  545. raxe/packs/core/v1.0.0/rules/pii/pii-3013@1.0.0.yaml +36 -0
  546. raxe/packs/core/v1.0.0/rules/pii/pii-3014@1.0.0.yaml +36 -0
  547. raxe/packs/core/v1.0.0/rules/pii/pii-3015@1.0.0.yaml +42 -0
  548. raxe/packs/core/v1.0.0/rules/pii/pii-3016@1.0.0.yaml +42 -0
  549. raxe/packs/core/v1.0.0/rules/pii/pii-3017@1.0.0.yaml +40 -0
  550. raxe/packs/core/v1.0.0/rules/pii/pii-3018@1.0.0.yaml +38 -0
  551. raxe/packs/core/v1.0.0/rules/pii/pii-3019@1.0.0.yaml +40 -0
  552. raxe/packs/core/v1.0.0/rules/pii/pii-3020@1.0.0.yaml +40 -0
  553. raxe/packs/core/v1.0.0/rules/pii/pii-3021@1.0.0.yaml +39 -0
  554. raxe/packs/core/v1.0.0/rules/pii/pii-3022@1.0.0.yaml +36 -0
  555. raxe/packs/core/v1.0.0/rules/pii/pii-3023@1.0.0.yaml +41 -0
  556. raxe/packs/core/v1.0.0/rules/pii/pii-3024@1.0.0.yaml +37 -0
  557. raxe/packs/core/v1.0.0/rules/pii/pii-3025@1.0.0.yaml +38 -0
  558. raxe/packs/core/v1.0.0/rules/pii/pii-3026@1.0.0.yaml +42 -0
  559. raxe/packs/core/v1.0.0/rules/pii/pii-3027@1.0.0.yaml +38 -0
  560. raxe/packs/core/v1.0.0/rules/pii/pii-3028@1.0.0.yaml +42 -0
  561. raxe/packs/core/v1.0.0/rules/pii/pii-3029@1.0.0.yaml +36 -0
  562. raxe/packs/core/v1.0.0/rules/pii/pii-3030@1.0.0.yaml +42 -0
  563. raxe/packs/core/v1.0.0/rules/pii/pii-3031@1.0.0.yaml +37 -0
  564. raxe/packs/core/v1.0.0/rules/pii/pii-3032@1.0.0.yaml +42 -0
  565. raxe/packs/core/v1.0.0/rules/pii/pii-3033@1.0.0.yaml +39 -0
  566. raxe/packs/core/v1.0.0/rules/pii/pii-3034@1.0.0.yaml +40 -0
  567. raxe/packs/core/v1.0.0/rules/pii/pii-3035@1.0.0.yaml +43 -0
  568. raxe/packs/core/v1.0.0/rules/pii/pii-3036@1.0.0.yaml +41 -0
  569. raxe/packs/core/v1.0.0/rules/pii/pii-3037@1.0.0.yaml +35 -0
  570. raxe/packs/core/v1.0.0/rules/pii/pii-3038@1.0.0.yaml +35 -0
  571. raxe/packs/core/v1.0.0/rules/pii/pii-3039@1.0.0.yaml +35 -0
  572. raxe/packs/core/v1.0.0/rules/pii/pii-3040@1.0.0.yaml +41 -0
  573. raxe/packs/core/v1.0.0/rules/pii/pii-3041@1.0.0.yaml +39 -0
  574. raxe/packs/core/v1.0.0/rules/pii/pii-3042@1.0.0.yaml +36 -0
  575. raxe/packs/core/v1.0.0/rules/pii/pii-3043@1.0.0.yaml +35 -0
  576. raxe/packs/core/v1.0.0/rules/pii/pii-3044@1.0.0.yaml +43 -0
  577. raxe/packs/core/v1.0.0/rules/pii/pii-3045@1.0.0.yaml +36 -0
  578. raxe/packs/core/v1.0.0/rules/pii/pii-3046@1.0.0.yaml +37 -0
  579. raxe/packs/core/v1.0.0/rules/pii/pii-3047@1.0.0.yaml +36 -0
  580. raxe/packs/core/v1.0.0/rules/pii/pii-3048@1.0.0.yaml +36 -0
  581. raxe/packs/core/v1.0.0/rules/pii/pii-3049@1.0.0.yaml +38 -0
  582. raxe/packs/core/v1.0.0/rules/pii/pii-3050@1.0.0.yaml +44 -0
  583. raxe/packs/core/v1.0.0/rules/pii/pii-3051@1.0.0.yaml +35 -0
  584. raxe/packs/core/v1.0.0/rules/pii/pii-3052@1.0.0.yaml +36 -0
  585. raxe/packs/core/v1.0.0/rules/pii/pii-3053@1.0.0.yaml +35 -0
  586. raxe/packs/core/v1.0.0/rules/pii/pii-3054@1.0.0.yaml +35 -0
  587. raxe/packs/core/v1.0.0/rules/pii/pii-3055@1.0.0.yaml +40 -0
  588. raxe/packs/core/v1.0.0/rules/pii/pii-3056@1.0.0.yaml +38 -0
  589. raxe/packs/core/v1.0.0/rules/pii/pii-3057@1.0.0.yaml +40 -0
  590. raxe/packs/core/v1.0.0/rules/pii/pii-3058@1.0.0.yaml +43 -0
  591. raxe/packs/core/v1.0.0/rules/pii/pii-3059@1.0.0.yaml +42 -0
  592. raxe/packs/core/v1.0.0/rules/pii/pii-3060@1.0.0.yaml +42 -0
  593. raxe/packs/core/v1.0.0/rules/pii/pii-3061@1.0.0.yaml +50 -0
  594. raxe/packs/core/v1.0.0/rules/pii/pii-3062@1.0.0.yaml +50 -0
  595. raxe/packs/core/v1.0.0/rules/pii/pii-3063@1.0.0.yaml +54 -0
  596. raxe/packs/core/v1.0.0/rules/pii/pii-3064@1.0.0.yaml +78 -0
  597. raxe/packs/core/v1.0.0/rules/pii/pii-3065@1.0.0.yaml +84 -0
  598. raxe/packs/core/v1.0.0/rules/pii/pii-3066@1.0.0.yaml +84 -0
  599. raxe/packs/core/v1.0.0/rules/pii/pii-3067@1.0.0.yaml +88 -0
  600. raxe/packs/core/v1.0.0/rules/pii/pii-3068@1.0.0.yaml +94 -0
  601. raxe/packs/core/v1.0.0/rules/pii/pii-3069@1.0.0.yaml +90 -0
  602. raxe/packs/core/v1.0.0/rules/pii/pii-3070@1.0.0.yaml +99 -0
  603. raxe/packs/core/v1.0.0/rules/pii/pii-3071@1.0.0.yaml +91 -0
  604. raxe/packs/core/v1.0.0/rules/pii/pii-3072@1.0.0.yaml +38 -0
  605. raxe/packs/core/v1.0.0/rules/pii/pii-3073@1.0.0.yaml +38 -0
  606. raxe/packs/core/v1.0.0/rules/pii/pii-3074@1.0.0.yaml +38 -0
  607. raxe/packs/core/v1.0.0/rules/pii/pii-3075@1.0.0.yaml +38 -0
  608. raxe/packs/core/v1.0.0/rules/pii/pii-3076@1.0.0.yaml +38 -0
  609. raxe/packs/core/v1.0.0/rules/pii/pii-3077@1.0.0.yaml +38 -0
  610. raxe/packs/core/v1.0.0/rules/pii/pii-3078@1.0.0.yaml +38 -0
  611. raxe/packs/core/v1.0.0/rules/pii/pii-3079@1.0.0.yaml +38 -0
  612. raxe/packs/core/v1.0.0/rules/pii/pii-3080@1.0.0.yaml +38 -0
  613. raxe/packs/core/v1.0.0/rules/pii/pii-3081@1.0.0.yaml +38 -0
  614. raxe/packs/core/v1.0.0/rules/pii/pii-3082@1.0.0.yaml +38 -0
  615. raxe/packs/core/v1.0.0/rules/pii/pii-3083@1.0.0.yaml +38 -0
  616. raxe/packs/core/v1.0.0/rules/pii/pii-3084@1.0.0.yaml +38 -0
  617. raxe/packs/core/v1.0.0/rules/pii/pii-3085@1.0.0.yaml +38 -0
  618. raxe/packs/core/v1.0.0/rules/rag/rag-016@1.0.0.yaml +47 -0
  619. raxe/packs/core/v1.0.0/rules/rag/rag-028@1.0.0.yaml +47 -0
  620. raxe/packs/core/v1.0.0/rules/rag/rag-042@1.0.0.yaml +47 -0
  621. raxe/packs/core/v1.0.0/rules/rag/rag-044@1.0.0.yaml +47 -0
  622. raxe/packs/core/v1.0.0/rules/rag/rag-045@1.0.0.yaml +47 -0
  623. raxe/packs/core/v1.0.0/rules/rag/rag-050@1.0.0.yaml +47 -0
  624. raxe/packs/core/v1.0.0/rules/rag/rag-201@1.0.0.yaml +41 -0
  625. raxe/packs/core/v1.0.0/rules/rag/rag-202@1.0.0.yaml +41 -0
  626. raxe/packs/core/v1.0.0/rules/rag/rag-3001@1.0.0.yaml +41 -0
  627. raxe/packs/core/v1.0.0/rules/rag/rag-3006@1.0.0.yaml +41 -0
  628. raxe/packs/core/v1.0.0/rules/rag/rag-3009@1.0.0.yaml +41 -0
  629. raxe/packs/core/v1.0.0/rules/rag/rag-3012@1.0.0.yaml +41 -0
  630. raxe/plugins/__init__.py +98 -0
  631. raxe/plugins/custom_rules.py +380 -0
  632. raxe/plugins/loader.py +389 -0
  633. raxe/plugins/manager.py +538 -0
  634. raxe/plugins/protocol.py +428 -0
  635. raxe/py.typed +0 -0
  636. raxe/sdk/__init__.py +77 -0
  637. raxe/sdk/agent_scanner.py +1918 -0
  638. raxe/sdk/client.py +1603 -0
  639. raxe/sdk/decorator.py +175 -0
  640. raxe/sdk/exceptions.py +859 -0
  641. raxe/sdk/integrations/__init__.py +277 -0
  642. raxe/sdk/integrations/agent_scanner.py +71 -0
  643. raxe/sdk/integrations/autogen.py +872 -0
  644. raxe/sdk/integrations/crewai.py +1368 -0
  645. raxe/sdk/integrations/dspy.py +845 -0
  646. raxe/sdk/integrations/extractors.py +363 -0
  647. raxe/sdk/integrations/huggingface.py +395 -0
  648. raxe/sdk/integrations/langchain.py +948 -0
  649. raxe/sdk/integrations/litellm.py +484 -0
  650. raxe/sdk/integrations/llamaindex.py +1049 -0
  651. raxe/sdk/integrations/portkey.py +831 -0
  652. raxe/sdk/suppression_context.py +215 -0
  653. raxe/sdk/wrappers/__init__.py +163 -0
  654. raxe/sdk/wrappers/anthropic.py +310 -0
  655. raxe/sdk/wrappers/openai.py +221 -0
  656. raxe/sdk/wrappers/vertexai.py +484 -0
  657. raxe/utils/__init__.py +12 -0
  658. raxe/utils/error_sanitizer.py +135 -0
  659. raxe/utils/logging.py +241 -0
  660. raxe/utils/performance.py +414 -0
  661. raxe/utils/profiler.py +339 -0
  662. raxe/utils/validators.py +170 -0
  663. raxe-0.4.6.dist-info/METADATA +471 -0
  664. raxe-0.4.6.dist-info/RECORD +668 -0
  665. raxe-0.4.6.dist-info/WHEEL +5 -0
  666. raxe-0.4.6.dist-info/entry_points.txt +2 -0
  667. raxe-0.4.6.dist-info/licenses/LICENSE +56 -0
  668. raxe-0.4.6.dist-info/top_level.txt +1 -0
@@ -0,0 +1,872 @@
1
+ """L2 result formatting with rich WHY explanations.
2
+
3
+ This module provides comprehensive formatting for L2 ML detection results,
4
+ including:
5
+ - Hierarchical risk scoring (0-100 scale)
6
+ - Classification levels (SAFE, FP_LIKELY, REVIEW, LIKELY_THREAT, THREAT, HIGH_THREAT)
7
+ - Final decision recommendations (ALLOW, BLOCK, BLOCK_WITH_REVIEW, etc.)
8
+ - Clear WHY explanations for each detection
9
+ - Detailed confidence breakdown (binary, family, subfamily)
10
+ - Signal quality indicators
11
+ - Matched patterns and features
12
+ - Recommended actions and remediation advice
13
+ - User-friendly threat descriptions
14
+
15
+ Usage:
16
+ from raxe.cli.l2_formatter import L2ResultFormatter
17
+
18
+ formatter = L2ResultFormatter()
19
+ formatter.format_predictions(l2_result, console, explain=False)
20
+ """
21
+
22
+ from typing import ClassVar
23
+
24
+ from rich.console import Console
25
+ from rich.panel import Panel
26
+ from rich.table import Table
27
+ from rich.text import Text
28
+
29
+ from raxe.domain.ml.protocol import L2Prediction, L2Result
30
+
31
+
32
+ class L2ResultFormatter:
33
+ """Formatter for L2 detection results with comprehensive WHY explanations."""
34
+
35
+ @staticmethod
36
+ def _get_confidence_indicator(confidence: float) -> tuple[str, str]:
37
+ """Get visual indicator for confidence level.
38
+
39
+ Args:
40
+ confidence: Confidence value (0.0-1.0)
41
+
42
+ Returns:
43
+ Tuple of (indicator emoji, strength label)
44
+ """
45
+ if confidence >= 0.8:
46
+ return ("✓", "Strong")
47
+ elif confidence >= 0.5:
48
+ return ("⚠️", "Medium")
49
+ elif confidence >= 0.3:
50
+ return ("⚠️", "Weak")
51
+ else:
52
+ return ("❌", "Very weak")
53
+
54
+ @staticmethod
55
+ def _get_classification_display(classification: str) -> tuple[str, str]:
56
+ """Get display styling for classification level.
57
+
58
+ Args:
59
+ classification: Classification level
60
+
61
+ Returns:
62
+ Tuple of (icon, color)
63
+ """
64
+ classification_map = {
65
+ "SAFE": ("🟢", "green"),
66
+ "FP_LIKELY": ("🟡", "yellow"),
67
+ "REVIEW": ("🟠", "yellow"),
68
+ "LIKELY_THREAT": ("🔶", "dark_orange"),
69
+ "THREAT": ("🔴", "red"),
70
+ "HIGH_THREAT": ("🔴", "red bold"),
71
+ }
72
+ return classification_map.get(classification, ("⚪", "white"))
73
+
74
+ @staticmethod
75
+ def _get_action_display(action: str) -> tuple[str, str]:
76
+ """Get display styling for action.
77
+
78
+ Args:
79
+ action: Action recommendation
80
+
81
+ Returns:
82
+ Tuple of (icon, color)
83
+ """
84
+ action_map = {
85
+ "ALLOW": ("✓", "green"),
86
+ "ALLOW_WITH_LOG": ("✓", "yellow"),
87
+ "MANUAL_REVIEW": ("👁️", "yellow"),
88
+ "BLOCK_WITH_REVIEW": ("🔍", "dark_orange"),
89
+ "BLOCK": ("🛡️", "red"),
90
+ "BLOCK_ALERT": ("🚨", "red bold"),
91
+ }
92
+ return action_map.get(action, ("•", "white"))
93
+
94
+ # User-friendly threat type descriptions (Gemma 5-head model)
95
+ THREAT_DESCRIPTIONS: ClassVar[dict[str, dict[str, str]]] = {
96
+ "benign": {
97
+ "title": "Benign",
98
+ "description": "No threat detected",
99
+ "icon": "✅",
100
+ },
101
+ "data_exfiltration": {
102
+ "title": "Data Exfiltration",
103
+ "description": "Attempt to extract sensitive data or information",
104
+ "icon": "📤",
105
+ },
106
+ "encoding_or_obfuscation_attack": {
107
+ "title": "Encoding/Obfuscation Attack",
108
+ "description": "Malicious content hidden using encoding or obfuscation",
109
+ "icon": "🔐",
110
+ },
111
+ "jailbreak": {
112
+ "title": "Jailbreak",
113
+ "description": "Attempt to bypass AI safety guidelines",
114
+ "icon": "🔓",
115
+ },
116
+ "other_security": {
117
+ "title": "Security Threat",
118
+ "description": "General security threat detected",
119
+ "icon": "🛡️",
120
+ },
121
+ "prompt_injection": {
122
+ "title": "Prompt Injection",
123
+ "description": "Attempt to override or manipulate system instructions",
124
+ "icon": "💉",
125
+ },
126
+ "rag_or_context_attack": {
127
+ "title": "RAG/Context Attack",
128
+ "description": "Attempt to manipulate retrieval or context",
129
+ "icon": "🎭",
130
+ },
131
+ "tool_or_command_abuse": {
132
+ "title": "Tool/Command Abuse",
133
+ "description": "Attempt to misuse tools or execute commands",
134
+ "icon": "⚙️",
135
+ },
136
+ "toxic_or_policy_violating_content": {
137
+ "title": "Toxic Content",
138
+ "description": "Content violating safety or policy guidelines",
139
+ "icon": "☠️",
140
+ },
141
+ }
142
+
143
+ # Remediation advice per threat type (Gemma model)
144
+ REMEDIATION_ADVICE: ClassVar[dict[str, str]] = {
145
+ "benign": (
146
+ "No action required. The content appears safe."
147
+ ),
148
+ "data_exfiltration": (
149
+ "Review data access controls and implement DLP policies. "
150
+ "Block requests attempting to extract sensitive information."
151
+ ),
152
+ "encoding_or_obfuscation_attack": (
153
+ "Decode and validate all user inputs before processing. "
154
+ "Block obfuscated payloads and implement input sanitization."
155
+ ),
156
+ "jailbreak": (
157
+ "Block the request and log for security review. "
158
+ "This prompt attempts to bypass AI safety guidelines."
159
+ ),
160
+ "other_security": (
161
+ "Review the prompt manually for security concerns. "
162
+ "Consider updating detection rules."
163
+ ),
164
+ "prompt_injection": (
165
+ "Block the request. This is an attempt to override system instructions. "
166
+ "Validate instruction boundaries and sanitize inputs."
167
+ ),
168
+ "rag_or_context_attack": (
169
+ "Reset conversation context and re-validate user intent. "
170
+ "Monitor for patterns of context manipulation."
171
+ ),
172
+ "tool_or_command_abuse": (
173
+ "Block tool/command execution and validate all user-provided inputs. "
174
+ "Implement strict command whitelisting."
175
+ ),
176
+ "toxic_or_policy_violating_content": (
177
+ "Block the content immediately. This violates safety policies. "
178
+ "Log for compliance review."
179
+ ),
180
+ }
181
+
182
+ # Documentation URLs per threat type (Gemma model)
183
+ DOCS_URLS: ClassVar[dict] = {
184
+ "benign": "https://docs.raxe.ai/threats/overview",
185
+ "data_exfiltration": "https://docs.raxe.ai/threats/data-exfiltration",
186
+ "encoding_or_obfuscation_attack": "https://docs.raxe.ai/threats/encoding-attacks",
187
+ "jailbreak": "https://docs.raxe.ai/threats/jailbreak",
188
+ "other_security": "https://docs.raxe.ai/threats/overview",
189
+ "prompt_injection": "https://docs.raxe.ai/threats/prompt-injection",
190
+ "rag_or_context_attack": "https://docs.raxe.ai/threats/rag-attacks",
191
+ "tool_or_command_abuse": "https://docs.raxe.ai/threats/tool-abuse",
192
+ "toxic_or_policy_violating_content": "https://docs.raxe.ai/threats/toxic-content",
193
+ }
194
+
195
+ @staticmethod
196
+ def _make_progress_bar(value: float, width: int = 20, filled: str = "█", empty: str = "░") -> str:
197
+ """Create a text-based progress bar.
198
+
199
+ Args:
200
+ value: Value between 0.0 and 1.0
201
+ width: Total width of the bar
202
+ filled: Character for filled portion
203
+ empty: Character for empty portion
204
+
205
+ Returns:
206
+ Progress bar string like "████████░░░░░░░░░░░░"
207
+ """
208
+ filled_count = int(value * width)
209
+ empty_count = width - filled_count
210
+ return filled * filled_count + empty * empty_count
211
+
212
+ @staticmethod
213
+ def format_predictions(
214
+ l2_result: L2Result,
215
+ console: Console,
216
+ *,
217
+ explain: bool = False,
218
+ show_below_threshold: bool = False,
219
+ ) -> None:
220
+ """Format all L2 predictions with rich output.
221
+
222
+ Args:
223
+ l2_result: L2 detection result
224
+ console: Rich console instance
225
+ explain: Show detailed explain mode with scoring breakdown (default: False)
226
+ show_below_threshold: Show L2 analysis even when below threshold (default: False)
227
+ """
228
+ if not l2_result:
229
+ return
230
+
231
+ # If below threshold but we want to show analysis anyway
232
+ if show_below_threshold and not l2_result.has_predictions:
233
+ L2ResultFormatter._format_below_threshold(l2_result, console)
234
+ return
235
+
236
+ if not l2_result.has_predictions:
237
+ return
238
+
239
+ # Show default or explain mode
240
+ if explain:
241
+ # Detailed explain mode with full scoring breakdown
242
+ L2ResultFormatter._format_explain_mode(l2_result, console)
243
+ else:
244
+ # Compact default mode with key metrics
245
+ L2ResultFormatter._format_default_mode(l2_result, console)
246
+
247
+ @staticmethod
248
+ def _format_below_threshold(l2_result: L2Result, console: Console) -> None:
249
+ """Format L2 analysis when below detection threshold.
250
+
251
+ Shows the ML analysis even when no detection was triggered,
252
+ useful for understanding why L2 didn't flag something.
253
+
254
+ Args:
255
+ l2_result: L2 detection result (with metadata but no predictions)
256
+ console: Rich console instance
257
+ """
258
+ console.print()
259
+ console.print("─" * 60, style="dim cyan")
260
+ console.print(" 📊 ML ANALYSIS (Below Threshold)", style="cyan")
261
+ console.print("─" * 60, style="dim cyan")
262
+ console.print()
263
+
264
+ # Get metadata from the result
265
+ metadata = l2_result.metadata or {}
266
+ classification_result = metadata.get("classification_result", {})
267
+
268
+ if not classification_result:
269
+ console.print(" No L2 analysis available", style="dim")
270
+ return
271
+
272
+ # Extract all 5 heads from classification_result
273
+ threat_prob = classification_result.get("threat_probability", 0)
274
+ classification_result.get("safe_probability", 1)
275
+ family = classification_result.get("threat_family", "unknown")
276
+ family_conf = classification_result.get("family_confidence", 0)
277
+ severity = classification_result.get("severity", "none")
278
+ severity_conf = classification_result.get("severity_confidence", 0)
279
+ technique = classification_result.get("primary_technique", "none")
280
+ technique_conf = classification_result.get("technique_confidence", 0)
281
+ harm_types = classification_result.get("harm_types", {})
282
+
283
+ # Main threat score
284
+ threat_bar = L2ResultFormatter._make_progress_bar(threat_prob, width=20)
285
+ threat_color = "yellow" if threat_prob >= 0.3 else "green"
286
+
287
+ threat_line = Text()
288
+ threat_line.append(" Threat Score ")
289
+ threat_line.append(threat_bar, style=threat_color)
290
+ threat_line.append(f" {threat_prob * 100:4.0f}% ", style=threat_color + " bold")
291
+ threat_line.append("Below threshold", style="dim")
292
+ console.print(threat_line)
293
+ console.print()
294
+
295
+ # Classification (what it would be if triggered)
296
+ console.print(" If triggered, classified as:", style="dim")
297
+ family_display = family.replace("_", " ").title()
298
+ severity_display = severity.replace("_", " ").title()
299
+ technique_display = technique.replace("_", " ").title() if technique and technique != "none" else "Unknown"
300
+
301
+ console.print(f" Family {family_display}", style="dim")
302
+ console.print(f" Severity {severity_display}", style="dim")
303
+ console.print(f" Technique {technique_display}", style="dim")
304
+ console.print()
305
+
306
+ # Confidence breakdown
307
+ console.print(" Confidence:", style="white bold")
308
+
309
+ def print_conf_row(label: str, value: float, color: str) -> None:
310
+ bar = L2ResultFormatter._make_progress_bar(value, width=12)
311
+ line = Text()
312
+ line.append(f" {label:<16}")
313
+ line.append(bar, style=color)
314
+ line.append(f" {value * 100:4.0f}%", style=color)
315
+ console.print(line)
316
+
317
+ print_conf_row("Threat", threat_prob, "yellow" if threat_prob >= 0.3 else "dim")
318
+ print_conf_row("Family", family_conf, "dim")
319
+ print_conf_row("Severity", severity_conf, "dim")
320
+ print_conf_row("Technique", technique_conf, "dim")
321
+ console.print()
322
+
323
+ # Harm Types (if any notable)
324
+ if harm_types and harm_types.get("probabilities"):
325
+ probs = harm_types.get("probabilities", {})
326
+ thresholds = harm_types.get("thresholds_used", {})
327
+ sorted_harms = sorted(probs.items(), key=lambda x: x[1], reverse=True)
328
+
329
+ # Only show if any are notable (>30%)
330
+ notable = [(h, p) for h, p in sorted_harms if p >= 0.3]
331
+ if notable:
332
+ console.print(" Notable Harm Signals:", style="white bold")
333
+ for harm_name, prob in notable[:3]:
334
+ threshold = thresholds.get(harm_name, 0.5)
335
+ bar = L2ResultFormatter._make_progress_bar(prob, width=10)
336
+ harm_display = harm_name.replace("_", " ").title()
337
+ color = "yellow" if prob >= threshold * 0.7 else "dim"
338
+ line = Text()
339
+ line.append(f" {harm_display:<24}")
340
+ line.append(bar, style=color)
341
+ line.append(f" {prob * 100:4.0f}%", style=color)
342
+ console.print(line)
343
+ console.print()
344
+
345
+ # Footer - simplified
346
+ console.print(f" Model: {l2_result.model_version}", style="dim")
347
+
348
+ @staticmethod
349
+ def _format_default_mode(l2_result: L2Result, console: Console) -> None:
350
+ """Format L2 predictions in compact default mode.
351
+
352
+ Shows a clean, minimal summary - one line per detection.
353
+
354
+ Args:
355
+ l2_result: L2 detection result
356
+ console: Rich console instance
357
+ """
358
+ console.print()
359
+
360
+ for prediction in l2_result.predictions:
361
+ # Extract scoring metadata
362
+ metadata = prediction.metadata
363
+ classification = metadata.get("classification", "THREAT")
364
+ action = metadata.get("action", "BLOCK")
365
+ hierarchical_score = metadata.get("hierarchical_score", prediction.confidence)
366
+
367
+ # Get family info for display
368
+ family = metadata.get("family", "unknown")
369
+ family_display = family.replace("_", " ").title()
370
+
371
+ # Get display styling
372
+ class_icon, class_color = L2ResultFormatter._get_classification_display(classification)
373
+ action_icon, action_color = L2ResultFormatter._get_action_display(action)
374
+
375
+ # Single clean line: 🤖 ML: Prompt Injection (55%) → MANUAL_REVIEW
376
+ ml_line = Text()
377
+ ml_line.append("🤖 ML: ", style="cyan bold")
378
+ ml_line.append(f"{family_display}", style="white bold")
379
+ ml_line.append(f" ({hierarchical_score * 100:.0f}%)", style="dim")
380
+ ml_line.append(" → ", style="dim")
381
+ ml_line.append(f"{action_icon} {action}", style=f"{action_color}")
382
+ console.print(ml_line)
383
+ console.print()
384
+
385
+ @staticmethod
386
+ def _format_explain_mode(l2_result: L2Result, console: Console) -> None:
387
+ """Format L2 predictions in detailed explain mode.
388
+
389
+ Shows:
390
+ - Complete scoring breakdown
391
+ - Classification reasoning
392
+ - Confidence signals with strength indicators
393
+ - Hierarchical score calculation
394
+ - Signal quality metrics
395
+ - Decision rationale
396
+ - Recommended actions
397
+
398
+ Args:
399
+ l2_result: L2 detection result
400
+ console: Rich console instance
401
+ """
402
+ console.print()
403
+ console.print("─" * 60, style="cyan")
404
+ console.print(" 📊 ML DETECTION ANALYSIS", style="bold cyan")
405
+ console.print("─" * 60, style="cyan")
406
+ console.print()
407
+
408
+ for prediction in l2_result.predictions:
409
+ # Extract scoring metadata
410
+ metadata = prediction.metadata
411
+ classification = metadata.get("classification", "THREAT")
412
+ action = metadata.get("action", "BLOCK")
413
+ metadata.get("risk_score", prediction.confidence * 100)
414
+ hierarchical_score = metadata.get("hierarchical_score", prediction.confidence)
415
+
416
+ # Get confidence scores
417
+ scores = metadata.get("scores", {})
418
+ binary_conf = scores.get("attack_probability", prediction.confidence)
419
+ family_conf = scores.get("family_confidence", 0.0)
420
+ subfamily_conf = scores.get("subfamily_confidence", 0.0)
421
+
422
+ # Get signal quality
423
+ is_consistent = metadata.get("is_consistent", True)
424
+ metadata.get("variance", 0.0)
425
+ metadata.get("weak_margins_count", 0)
426
+ metadata.get("margins", {})
427
+
428
+ # Get family info
429
+ family = metadata.get("family", "UNKNOWN")
430
+ subfamily = metadata.get("sub_family", "unknown")
431
+
432
+ # Classification header - simplified
433
+ class_icon, class_color = L2ResultFormatter._get_classification_display(classification)
434
+ header_line = Text()
435
+ header_line.append(f" {class_icon} ", style=class_color)
436
+ header_line.append(classification, style=f"{class_color} bold")
437
+ header_line.append(f" ({hierarchical_score * 100:.0f}% confidence)", style="dim")
438
+ console.print(header_line)
439
+ console.print()
440
+
441
+ # Why This Was Flagged - cleaner format
442
+ why_it_hit = metadata.get("why_it_hit", [])
443
+ if why_it_hit:
444
+ console.print(" Why Flagged:", style="yellow bold")
445
+ for reason_item in why_it_hit:
446
+ console.print(f" • {reason_item}", style="white")
447
+ console.print()
448
+
449
+ # ─────────────────────────────────────────────────────────────
450
+ # ML Confidence Breakdown - Clean aligned display
451
+ # ─────────────────────────────────────────────────────────────
452
+
453
+ console.print("ML Confidence:", style="cyan bold")
454
+ console.print()
455
+
456
+ # Main threat score with large progress bar
457
+ threat_bar = L2ResultFormatter._make_progress_bar(binary_conf, width=20)
458
+ threat_color = "green" if binary_conf >= 0.8 else "yellow" if binary_conf >= 0.5 else "red"
459
+ threat_label = "High" if binary_conf >= 0.8 else "Medium" if binary_conf >= 0.5 else "Low"
460
+
461
+ threat_line = Text()
462
+ threat_line.append(" Threat Score ")
463
+ threat_line.append(threat_bar, style=threat_color)
464
+ threat_line.append(f" {binary_conf * 100:4.0f}% ", style=threat_color + " bold")
465
+ threat_line.append(threat_label, style=threat_color)
466
+ console.print(threat_line)
467
+ console.print()
468
+
469
+ # Classification details in clean table format
470
+ console.print(" Classification:", style="white bold")
471
+ severity = metadata.get("severity", "unknown")
472
+ severity_conf = scores.get("severity_confidence", 0.0)
473
+
474
+ # Format family name nicely
475
+ family_display = family.replace("_", " ").title()
476
+ subfamily_display = subfamily.replace("_", " ").title() if subfamily else "Unknown"
477
+ severity_display = severity.replace("_", " ").title()
478
+
479
+ # Use consistent column widths
480
+ console.print(f" Family {family_display}", style="dim")
481
+ console.print(f" Technique {subfamily_display}", style="dim")
482
+ console.print(f" Severity {severity_display}", style="dim")
483
+ console.print()
484
+
485
+ # Confidence breakdown as simple bars (all aligned)
486
+ console.print(" Confidence Breakdown:", style="white bold")
487
+
488
+ # Helper for aligned confidence rows
489
+ def print_conf_row(label: str, value: float, color: str) -> None:
490
+ bar = L2ResultFormatter._make_progress_bar(value, width=12)
491
+ line = Text()
492
+ line.append(f" {label:<16}")
493
+ line.append(bar, style=color)
494
+ line.append(f" {value * 100:4.0f}%", style=color)
495
+ console.print(line)
496
+
497
+ # All confidence signals with consistent alignment
498
+ print_conf_row("Threat", binary_conf,
499
+ "green" if binary_conf >= 0.7 else "yellow" if binary_conf >= 0.4 else "red")
500
+ print_conf_row("Family", family_conf,
501
+ "green" if family_conf >= 0.6 else "yellow" if family_conf >= 0.3 else "dim")
502
+ print_conf_row("Severity", severity_conf,
503
+ "green" if severity_conf >= 0.6 else "yellow" if severity_conf >= 0.3 else "dim")
504
+ print_conf_row("Technique", subfamily_conf,
505
+ "green" if subfamily_conf >= 0.6 else "yellow" if subfamily_conf >= 0.3 else "dim")
506
+
507
+ console.print()
508
+
509
+ # Signal quality - simplified to single line
510
+ quality_icon = "✓" if is_consistent else "⚠"
511
+ quality_color = "green" if is_consistent else "yellow"
512
+ quality_text = "Consistent" if is_consistent else "Mixed signals"
513
+ console.print(f" Signal Quality {quality_icon} {quality_text}", style=quality_color)
514
+ console.print()
515
+
516
+ # Recommended Action - prominent display
517
+ action_icon, action_color = L2ResultFormatter._get_action_display(action)
518
+ action_line = Text()
519
+ action_line.append(" Recommended ", style="white bold")
520
+ action_line.append(f"{action_icon} {action}", style=f"{action_color} bold")
521
+ console.print(action_line)
522
+ console.print()
523
+
524
+ # Harm Types (if available) - simplified display
525
+ harm_types = metadata.get("harm_types")
526
+ if harm_types and isinstance(harm_types, dict):
527
+ active_labels = harm_types.get("active_labels", [])
528
+ probabilities = harm_types.get("probabilities", {})
529
+
530
+ if active_labels:
531
+ console.print(" Harm Categories:", style="white bold")
532
+ for label in active_labels:
533
+ prob = probabilities.get(label, 0)
534
+ bar = L2ResultFormatter._make_progress_bar(prob, width=10)
535
+ label_display = label.replace("_", " ").title()
536
+ line = Text()
537
+ line.append(f" ⚠ {label_display:<26}")
538
+ line.append(bar, style="red")
539
+ line.append(f" {prob * 100:4.0f}%", style="red")
540
+ console.print(line)
541
+ console.print()
542
+
543
+ # Separator between predictions
544
+ if len(l2_result.predictions) > 1:
545
+ console.print("─" * 80, style="dim")
546
+ console.print()
547
+
548
+ # ─────────────────────────────────────────────────────────────
549
+ # Voting Engine Breakdown (if available)
550
+ # ─────────────────────────────────────────────────────────────
551
+ voting = getattr(l2_result, "voting", None)
552
+ if voting and isinstance(voting, dict):
553
+ L2ResultFormatter._format_voting_breakdown(voting, console)
554
+
555
+ @staticmethod
556
+ def _format_voting_breakdown(voting: dict, console: Console) -> None:
557
+ """Format voting engine breakdown for --explain output.
558
+
559
+ Shows how each of the 5 heads voted and the final decision.
560
+
561
+ Args:
562
+ voting: Voting result dictionary from L2Result.voting
563
+ console: Rich console instance
564
+ """
565
+ console.print("─" * 60, style="magenta")
566
+ console.print(" 🗳️ VOTING ENGINE BREAKDOWN", style="bold magenta")
567
+ console.print("─" * 60, style="magenta")
568
+ console.print()
569
+
570
+ # Decision summary
571
+ decision = voting.get("decision", "unknown").upper()
572
+ confidence = voting.get("confidence", 0.0)
573
+ preset = voting.get("preset_used", "balanced")
574
+ rule = voting.get("decision_rule_triggered", "unknown")
575
+
576
+ # Color based on decision
577
+ decision_color = "red" if decision == "THREAT" else "yellow" if decision == "REVIEW" else "green"
578
+ decision_icon = "🔴" if decision == "THREAT" else "🟡" if decision == "REVIEW" else "🟢"
579
+
580
+ console.print(f" {decision_icon} Decision: ", style=decision_color + " bold", end="")
581
+ console.print(f"{decision}", style=decision_color + " bold", end="")
582
+ console.print(f" ({confidence * 100:.0f}% confidence)", style="dim")
583
+ console.print(f" Preset: {preset} • Rule: {rule}", style="dim")
584
+ console.print()
585
+
586
+ # Vote counts
587
+ threat_votes = voting.get("threat_vote_count", 0)
588
+ safe_votes = voting.get("safe_vote_count", 0)
589
+ abstain_votes = voting.get("abstain_vote_count", 0)
590
+
591
+ console.print(" Vote Summary:", style="white bold")
592
+ console.print(f" 🔴 THREAT: {threat_votes} • 🟢 SAFE: {safe_votes} • ⚪ ABSTAIN: {abstain_votes}", style="dim")
593
+ console.print()
594
+
595
+ # Per-head votes
596
+ per_head = voting.get("per_head_votes", {})
597
+ if per_head:
598
+ console.print(" Per-Head Votes:", style="white bold")
599
+
600
+ # Define head display order
601
+ head_order = ["binary", "family", "severity", "technique", "harm"]
602
+ head_labels = {
603
+ "binary": "Binary ",
604
+ "family": "Family ",
605
+ "severity": "Severity ",
606
+ "technique": "Technique ",
607
+ "harm": "Harm Types ",
608
+ }
609
+
610
+ for head in head_order:
611
+ vote_data = per_head.get(head, {})
612
+ if not vote_data:
613
+ continue
614
+
615
+ vote = vote_data.get("vote", "unknown")
616
+ conf = vote_data.get("confidence", 0.0)
617
+ weight = vote_data.get("weight", 1.0)
618
+ prediction = vote_data.get("prediction", "")
619
+ raw_prob = vote_data.get("raw_probability", 0.0)
620
+
621
+ # Vote icon and color
622
+ if vote == "threat":
623
+ vote_icon = "🔴"
624
+ vote_color = "red"
625
+ elif vote == "safe":
626
+ vote_icon = "🟢"
627
+ vote_color = "green"
628
+ else:
629
+ vote_icon = "⚪"
630
+ vote_color = "dim"
631
+
632
+ # Format prediction for display
633
+ pred_display = prediction.replace("_", " ").title() if prediction else "-"
634
+ if len(pred_display) > 20:
635
+ pred_display = pred_display[:18] + ".."
636
+
637
+ line = Text()
638
+ line.append(f" {head_labels.get(head, head)}")
639
+ line.append(f"{vote_icon} {vote.upper():<7}", style=vote_color + " bold")
640
+ line.append(f" w={weight:.1f} ", style="dim")
641
+ line.append(f"prob={raw_prob * 100:4.0f}% ", style="dim")
642
+ line.append(f"→ {pred_display}", style="cyan")
643
+ console.print(line)
644
+
645
+ console.print()
646
+
647
+ # Weighted scores
648
+ weighted_threat = voting.get("weighted_threat_score", 0)
649
+ weighted_safe = voting.get("weighted_safe_score", 0)
650
+ ratio = voting.get("weighted_ratio", 0) if weighted_safe > 0 else 0
651
+
652
+ console.print(" Weighted Scores:", style="white bold")
653
+ console.print(f" Threat: {weighted_threat:.1f} • Safe: {weighted_safe:.1f} • Ratio: {ratio:.2f}", style="dim")
654
+ console.print()
655
+
656
+ @staticmethod
657
+ def format_prediction_detail(
658
+ prediction: L2Prediction,
659
+ console: Console,
660
+ ) -> None:
661
+ """Format a single L2 prediction with full WHY explanation.
662
+
663
+ Args:
664
+ prediction: L2 prediction to format
665
+ console: Rich console instance
666
+ """
667
+ threat_key = prediction.threat_type.value
668
+ threat_info = L2ResultFormatter.THREAT_DESCRIPTIONS.get(
669
+ threat_key,
670
+ {
671
+ "title": threat_key.replace("_", " ").title(),
672
+ "description": "Detected threat",
673
+ "icon": "⚠️",
674
+ }
675
+ )
676
+
677
+ # Create detailed explanation panel
678
+ content = Text()
679
+
680
+ # Header with icon and title
681
+ icon = threat_info["icon"]
682
+ title = threat_info["title"]
683
+ content.append(f"\n{icon} {title}\n", style="red bold")
684
+ content.append(f"{threat_info['description']}\n\n", style="white")
685
+
686
+ # NEW: Display ML model metadata fields if available (is_attack, family, sub_family)
687
+ family = prediction.metadata.get("family")
688
+ sub_family = prediction.metadata.get("sub_family")
689
+ if family:
690
+ content.append("Attack Classification:\n", style="cyan bold")
691
+ content.append(f" Family: {family}\n", style="white")
692
+ if sub_family:
693
+ content.append(f" Sub-family: {sub_family}\n", style="white")
694
+ content.append("\n")
695
+
696
+ # Confidence with detailed scores from bundle
697
+ scores = prediction.metadata.get("scores", {})
698
+ if scores:
699
+ content.append("Confidence Scores:\n", style="cyan bold")
700
+ attack_prob = scores.get("attack_probability", prediction.confidence)
701
+ family_conf = scores.get("family_confidence")
702
+ subfamily_conf = scores.get("subfamily_confidence")
703
+
704
+ confidence_pct = f"{attack_prob * 100:.1f}%"
705
+ confidence_color = "red" if attack_prob >= 0.8 else "yellow"
706
+ content.append(" Attack Probability: ", style="white")
707
+ content.append(f"{confidence_pct}\n", style=confidence_color)
708
+
709
+ if family_conf is not None:
710
+ content.append(" Family Confidence: ", style="white")
711
+ content.append(f"{family_conf * 100:.1f}%\n", style="white")
712
+
713
+ if subfamily_conf is not None:
714
+ content.append(" Subfamily Confidence: ", style="white")
715
+ content.append(f"{subfamily_conf * 100:.1f}%\n", style="white")
716
+
717
+ content.append("\n")
718
+ else:
719
+ # Fallback to simple confidence
720
+ confidence_pct = f"{prediction.confidence * 100:.1f}%"
721
+ confidence_color = "red" if prediction.confidence >= 0.8 else "yellow"
722
+ content.append("Confidence: ", style="bold")
723
+ content.append(f"{confidence_pct}\n\n", style=confidence_color)
724
+
725
+ # WHY - Use why_it_hit from bundle if available, otherwise use explanation
726
+ why_it_hit = prediction.metadata.get("why_it_hit", [])
727
+ if why_it_hit:
728
+ content.append("Why This Was Flagged:\n", style="cyan bold")
729
+ for reason in why_it_hit:
730
+ content.append(f" • {reason}\n", style="white")
731
+ content.append("\n")
732
+ elif prediction.explanation:
733
+ content.append("Why This Was Flagged:\n", style="cyan bold")
734
+ content.append(f"{prediction.explanation}\n\n", style="white")
735
+
736
+ # Trigger matches from bundle
737
+ trigger_matches = prediction.metadata.get("trigger_matches", [])
738
+ if trigger_matches:
739
+ content.append("Trigger Matches:\n", style="yellow bold")
740
+ for trigger in trigger_matches[:5]: # Limit to top 5
741
+ content.append(f" • {trigger}\n", style="yellow")
742
+ content.append("\n")
743
+
744
+ # Features used (if available and not redundant with why_it_hit)
745
+ if prediction.features_used and not why_it_hit:
746
+ content.append("Features Detected:\n", style="cyan bold")
747
+ for feature in prediction.features_used[:5]: # Limit to top 5
748
+ content.append(f" • {feature}\n", style="white")
749
+ content.append("\n")
750
+
751
+ # Matched patterns (from metadata - legacy support)
752
+ matched_patterns = prediction.metadata.get("matched_patterns", [])
753
+ if matched_patterns and not trigger_matches:
754
+ content.append("Detected Patterns:\n", style="cyan bold")
755
+ for pattern in matched_patterns:
756
+ content.append(f" • {pattern}\n", style="white")
757
+ content.append("\n")
758
+
759
+ # Similar attacks from bundle
760
+ similar_attacks = prediction.metadata.get("similar_attacks", [])
761
+ if similar_attacks:
762
+ content.append("Similar Known Attacks:\n", style="magenta bold")
763
+ for i, attack in enumerate(similar_attacks[:3], 1): # Top 3
764
+ if isinstance(attack, dict):
765
+ text = attack.get("text", "")
766
+ similarity = attack.get("similarity", 0)
767
+ if text:
768
+ # Truncate if too long
769
+ text_preview = text[:60] + "..." if len(text) > 60 else text
770
+ content.append(f" {i}. {text_preview} ", style="white")
771
+ content.append(f"({similarity:.0%} similar)\n", style="dim")
772
+ content.append("\n")
773
+
774
+ # Recommended action from bundle (list of strings)
775
+ recommended_actions = prediction.metadata.get("recommended_action", [])
776
+ if recommended_actions:
777
+ content.append("Recommended Actions:\n", style="yellow bold")
778
+ for action in recommended_actions:
779
+ # Determine color based on action content
780
+ if "BLOCK" in action.upper() or "HIGH" in action.upper():
781
+ action_color = "red bold"
782
+ elif "WARN" in action.upper() or "MEDIUM" in action.upper():
783
+ action_color = "yellow"
784
+ else:
785
+ action_color = "green"
786
+ content.append(f" • {action}\n", style=action_color)
787
+ content.append("\n")
788
+ else:
789
+ # Fallback to legacy recommended_action
790
+ recommended_action = prediction.metadata.get("recommended_action", "review")
791
+ if isinstance(recommended_action, str):
792
+ content.append("Recommended Action: ", style="yellow bold")
793
+ action_color = "red bold" if recommended_action == "block" else "yellow"
794
+ content.append(f"{recommended_action.upper()}\n\n", style=action_color)
795
+
796
+ # Severity (if available)
797
+ severity = prediction.metadata.get("severity", "unknown")
798
+ if severity != "unknown":
799
+ content.append("Severity: ", style="yellow bold")
800
+ severity_color = "red" if severity in ("critical", "high") else "yellow"
801
+ content.append(f"{severity.upper()}\n\n", style=severity_color)
802
+
803
+ # Uncertainty flag from bundle
804
+ uncertain = prediction.metadata.get("uncertain", False)
805
+ if uncertain:
806
+ content.append("⚠️ ", style="yellow bold")
807
+ content.append("Model Uncertainty: ", style="yellow bold")
808
+ content.append("Low confidence - manual review recommended\n\n", style="yellow")
809
+
810
+ # What to do - Remediation advice
811
+ remediation = L2ResultFormatter.REMEDIATION_ADVICE.get(
812
+ threat_key,
813
+ "Review the prompt and apply appropriate security controls."
814
+ )
815
+ content.append("What To Do:\n", style="green bold")
816
+ content.append(f"{remediation}\n\n", style="white")
817
+
818
+ # Documentation link
819
+ docs_url = L2ResultFormatter.DOCS_URLS.get(threat_key, "")
820
+ if docs_url:
821
+ content.append("Learn More: ", style="blue bold")
822
+ content.append(f"{docs_url}\n", style="blue underline")
823
+
824
+ # Display panel with appropriate border color
825
+ border_color = "red" if prediction.confidence >= 0.8 else "yellow"
826
+ panel_title = "L2 ML Detection"
827
+ if family:
828
+ panel_title += f" [{family}]"
829
+
830
+ console.print(Panel(
831
+ content,
832
+ border_style=border_color,
833
+ title=panel_title,
834
+ title_align="left",
835
+ padding=(1, 2),
836
+ ))
837
+
838
+ @staticmethod
839
+ def format_prediction_compact(
840
+ prediction: L2Prediction,
841
+ console: Console,
842
+ ) -> None:
843
+ """Format a single L2 prediction in compact format (for tables).
844
+
845
+ Args:
846
+ prediction: L2 prediction to format
847
+ console: Rich console instance
848
+ """
849
+ threat_key = prediction.threat_type.value
850
+ threat_info = L2ResultFormatter.THREAT_DESCRIPTIONS.get(
851
+ threat_key,
852
+ {"title": threat_key.replace("_", " ").title(), "icon": "⚠️"}
853
+ )
854
+
855
+ icon = threat_info["icon"]
856
+ title = threat_info["title"]
857
+ confidence_pct = f"{prediction.confidence * 100:.1f}%"
858
+
859
+ # Single line format
860
+ line = Text()
861
+ line.append(f"{icon} ", style="red bold")
862
+ line.append(f"{title} ", style="red")
863
+ line.append(f"({confidence_pct})", style="yellow")
864
+
865
+ if prediction.explanation:
866
+ # Truncate explanation if too long
867
+ explanation = prediction.explanation[:60]
868
+ if len(prediction.explanation) > 60:
869
+ explanation += "..."
870
+ line.append(f" - {explanation}", style="dim")
871
+
872
+ console.print(line)