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,1368 @@
1
+ """CrewAI integration for RAXE scanning.
2
+
3
+ Provides a guard wrapper for automatic RAXE scanning in CrewAI multi-agent
4
+ applications. Supports scanning of agent messages, task outputs, tool calls,
5
+ and inter-agent communications.
6
+
7
+ This integration works with:
8
+ - Crew orchestration (kickoff, step_callback, task_callback)
9
+ - Individual Agents
10
+ - Tasks and their outputs
11
+ - Tools (BaseTool subclasses)
12
+
13
+ Requires: crewai>=0.28.0
14
+
15
+ Usage:
16
+ from crewai import Crew, Agent, Task
17
+ from raxe import Raxe
18
+ from raxe.sdk.integrations import RaxeCrewGuard
19
+
20
+ # Create guard
21
+ guard = RaxeCrewGuard(Raxe())
22
+
23
+ # Wrap a crew for automatic scanning
24
+ protected_crew = guard.protect_crew(crew)
25
+ result = protected_crew.kickoff()
26
+
27
+ # Or use callbacks directly
28
+ crew = Crew(
29
+ agents=[researcher, writer],
30
+ tasks=[research_task, write_task],
31
+ step_callback=guard.step_callback,
32
+ task_callback=guard.task_callback,
33
+ )
34
+ """
35
+ from __future__ import annotations
36
+
37
+ import functools
38
+ import logging
39
+ import threading
40
+ from collections.abc import Callable
41
+ from dataclasses import dataclass, field
42
+ from typing import TYPE_CHECKING, Any, TypeVar
43
+
44
+ from raxe.sdk.agent_scanner import (
45
+ AgentScanner,
46
+ AgentScannerConfig,
47
+ AgentScanResult,
48
+ MessageType,
49
+ ScanContext,
50
+ ScanMode,
51
+ ThreatDetectedError,
52
+ create_agent_scanner,
53
+ )
54
+ from raxe.sdk.exceptions import SecurityException
55
+ from raxe.sdk.integrations.extractors import (
56
+ extract_text_from_dict,
57
+ )
58
+
59
+ if TYPE_CHECKING:
60
+ from raxe.application.scan_pipeline import ScanPipelineResult
61
+ from raxe.sdk.client import Raxe
62
+
63
+ logger = logging.getLogger(__name__)
64
+
65
+ # Type variables for generic typing
66
+ T = TypeVar("T")
67
+ CrewType = TypeVar("CrewType")
68
+ AgentType = TypeVar("AgentType")
69
+ TaskType = TypeVar("TaskType")
70
+
71
+
72
+ @dataclass
73
+ class CrewGuardConfig:
74
+ """Configuration for RaxeCrewGuard.
75
+
76
+ Extends AgentScannerConfig with CrewAI-specific options.
77
+
78
+ Attributes:
79
+ mode: How to handle detected threats (default: LOG_ONLY)
80
+ scan_step_outputs: Scan step outputs in step_callback
81
+ scan_task_outputs: Scan task outputs in task_callback
82
+ scan_tool_inputs: Scan tool inputs before execution
83
+ scan_tool_outputs: Scan tool outputs after execution
84
+ scan_agent_thoughts: Scan agent reasoning/thoughts
85
+ scan_crew_inputs: Scan inputs to crew.kickoff()
86
+ scan_crew_outputs: Scan final crew outputs
87
+ on_threat: Callback when threat detected
88
+ on_block: Callback when execution blocked
89
+ wrap_tools: Automatically wrap tools for scanning
90
+ tool_scan_mode: Scan mode specifically for tools
91
+
92
+ Example:
93
+ # Monitoring mode (default - safe for production)
94
+ config = CrewGuardConfig()
95
+
96
+ # Strict mode for development/testing
97
+ config = CrewGuardConfig(
98
+ mode=ScanMode.BLOCK_ON_HIGH,
99
+ wrap_tools=True,
100
+ )
101
+
102
+ # Custom threat handling
103
+ def alert_security(msg, result):
104
+ send_slack_alert(f"Threat: {result.severity}")
105
+
106
+ config = CrewGuardConfig(
107
+ on_threat=alert_security,
108
+ )
109
+ """
110
+
111
+ # Scanning mode
112
+ mode: ScanMode = ScanMode.LOG_ONLY
113
+
114
+ # What to scan
115
+ scan_step_outputs: bool = True
116
+ scan_task_outputs: bool = True
117
+ scan_tool_inputs: bool = True
118
+ scan_tool_outputs: bool = True
119
+ scan_agent_thoughts: bool = True
120
+ scan_crew_inputs: bool = True
121
+ scan_crew_outputs: bool = True
122
+
123
+ # Callbacks
124
+ on_threat: Callable[[str, ScanPipelineResult], None] | None = None
125
+ on_block: Callable[[str, ScanPipelineResult], None] | None = None
126
+
127
+ # Tool-specific options
128
+ wrap_tools: bool = False # Auto-wrap tools for scanning
129
+ tool_scan_mode: ScanMode | None = None # Override mode for tools
130
+
131
+ # Advanced options
132
+ include_agent_context: bool = True # Include agent name/role in scans
133
+ include_task_context: bool = True # Include task description in scans
134
+ max_thought_length: int = 5000 # Max length of thoughts to scan
135
+
136
+ def _mode_to_on_threat(self) -> str:
137
+ """Convert ScanMode to on_threat literal.
138
+
139
+ Returns:
140
+ 'log', 'block', or 'warn' based on mode
141
+ """
142
+ if self.mode == ScanMode.LOG_ONLY:
143
+ return "log"
144
+ elif self.mode in (ScanMode.BLOCK_ON_THREAT, ScanMode.BLOCK_ON_HIGH, ScanMode.BLOCK_ON_CRITICAL):
145
+ return "block"
146
+ return "log"
147
+
148
+ def _mode_to_severity_threshold(self) -> str:
149
+ """Convert ScanMode to block_severity_threshold.
150
+
151
+ Returns:
152
+ Severity threshold based on mode
153
+ """
154
+ if self.mode == ScanMode.BLOCK_ON_CRITICAL:
155
+ return "CRITICAL"
156
+ elif self.mode == ScanMode.BLOCK_ON_HIGH:
157
+ return "HIGH"
158
+ return "HIGH" # Default for BLOCK_ON_THREAT
159
+
160
+ def to_agent_scanner_config(self) -> AgentScannerConfig:
161
+ """Convert to AgentScannerConfig for base scanner.
162
+
163
+ Returns:
164
+ AgentScannerConfig with equivalent settings
165
+ """
166
+ # Create wrapper callbacks that adapt CrewAI signature to AgentScanner signature
167
+ # CrewAI: Callable[[str, ScanPipelineResult], None]
168
+ # AgentScanner: Callable[[AgentScanResult], None]
169
+ threat_callback = None
170
+ block_callback = None
171
+
172
+ if self.on_threat is not None:
173
+ original_threat = self.on_threat
174
+
175
+ def threat_callback(result: AgentScanResult) -> None:
176
+ # Adapt to CrewAI callback signature
177
+ original_threat(result.message, result.pipeline_result)
178
+
179
+ if self.on_block is not None:
180
+ original_block = self.on_block
181
+
182
+ def block_callback(result: AgentScanResult) -> None:
183
+ original_block(result.message, result.pipeline_result)
184
+
185
+ return AgentScannerConfig(
186
+ on_threat=self._mode_to_on_threat(),
187
+ block_severity_threshold=self._mode_to_severity_threshold(),
188
+ scan_prompts=self.scan_crew_inputs,
189
+ scan_system_prompts=self.scan_agent_thoughts,
190
+ scan_tool_calls=self.scan_tool_inputs,
191
+ scan_tool_results=self.scan_tool_outputs,
192
+ scan_responses=self.scan_crew_outputs,
193
+ on_threat_callback=threat_callback,
194
+ on_block_callback=block_callback,
195
+ )
196
+
197
+
198
+ @dataclass
199
+ class CrewScanStats:
200
+ """Thread-safe statistics for scans performed during crew execution.
201
+
202
+ Tracks scan counts, threats detected, and performance metrics
203
+ across a crew's lifecycle. All operations are thread-safe.
204
+
205
+ Attributes:
206
+ total_scans: Total number of scans performed
207
+ step_scans: Number of step_callback scans
208
+ task_scans: Number of task_callback scans
209
+ tool_scans: Number of tool scans
210
+ threats_detected: Total threats detected
211
+ threats_blocked: Number of times execution was blocked
212
+ highest_severity: Highest severity threat seen
213
+ scan_durations_ms: List of scan durations
214
+ """
215
+
216
+ total_scans: int = 0
217
+ step_scans: int = 0
218
+ task_scans: int = 0
219
+ tool_scans: int = 0
220
+ threats_detected: int = 0
221
+ threats_blocked: int = 0
222
+ highest_severity: str | None = None
223
+ scan_durations_ms: list[float] = field(default_factory=list)
224
+ _lock: threading.Lock = field(default_factory=threading.Lock, repr=False)
225
+
226
+ def record_scan(
227
+ self,
228
+ scan_type: str,
229
+ result: AgentScanResult,
230
+ blocked: bool = False,
231
+ ) -> None:
232
+ """Record a scan for statistics (thread-safe).
233
+
234
+ Args:
235
+ scan_type: Type of scan (step, task, tool)
236
+ result: AgentScanResult from scanner
237
+ blocked: Whether execution was blocked
238
+ """
239
+ with self._lock:
240
+ self.total_scans += 1
241
+ self.scan_durations_ms.append(result.duration_ms)
242
+
243
+ if scan_type == "step":
244
+ self.step_scans += 1
245
+ elif scan_type == "task":
246
+ self.task_scans += 1
247
+ elif scan_type == "tool":
248
+ self.tool_scans += 1
249
+
250
+ if result.has_threats:
251
+ self.threats_detected += 1
252
+ severity = result.severity
253
+ if severity:
254
+ self._update_highest_severity(severity)
255
+
256
+ if blocked:
257
+ self.threats_blocked += 1
258
+
259
+ def _update_highest_severity(self, severity: str) -> None:
260
+ """Update highest severity seen (must be called with lock held)."""
261
+ severity_order = {"CRITICAL": 4, "HIGH": 3, "MEDIUM": 2, "LOW": 1}
262
+ current_level = severity_order.get(self.highest_severity or "", 0)
263
+ new_level = severity_order.get(severity, 0)
264
+ if new_level > current_level:
265
+ self.highest_severity = severity
266
+
267
+ @property
268
+ def average_scan_duration_ms(self) -> float:
269
+ """Calculate average scan duration (thread-safe)."""
270
+ with self._lock:
271
+ if not self.scan_durations_ms:
272
+ return 0.0
273
+ return sum(self.scan_durations_ms) / len(self.scan_durations_ms)
274
+
275
+ def to_dict(self) -> dict[str, Any]:
276
+ """Convert to dictionary for serialization (thread-safe)."""
277
+ with self._lock:
278
+ return {
279
+ "total_scans": self.total_scans,
280
+ "step_scans": self.step_scans,
281
+ "task_scans": self.task_scans,
282
+ "tool_scans": self.tool_scans,
283
+ "threats_detected": self.threats_detected,
284
+ "threats_blocked": self.threats_blocked,
285
+ "highest_severity": self.highest_severity,
286
+ "average_scan_duration_ms": (
287
+ sum(self.scan_durations_ms) / len(self.scan_durations_ms)
288
+ if self.scan_durations_ms else 0.0
289
+ ),
290
+ }
291
+
292
+
293
+ class RaxeCrewGuard:
294
+ """Security guard for CrewAI multi-agent applications.
295
+
296
+ RaxeCrewGuard provides automatic security scanning for CrewAI crews
297
+ using the composition pattern. It can be used in several ways:
298
+
299
+ 1. **Callback Mode**: Pass callbacks to Crew constructor
300
+ 2. **Wrapper Mode**: Wrap a Crew for automatic protection
301
+ 3. **Manual Mode**: Call scan methods directly
302
+
303
+ The guard uses the AgentScanner base class for core scanning logic
304
+ and adds CrewAI-specific integration code.
305
+
306
+ Attributes:
307
+ raxe: RAXE client instance
308
+ config: Guard configuration
309
+ scanner: Underlying AgentScanner
310
+ stats: Scan statistics for current/last crew run
311
+
312
+ Example (Callback Mode):
313
+ >>> from crewai import Crew, Agent, Task
314
+ >>> from raxe import Raxe
315
+ >>> from raxe.sdk.integrations import RaxeCrewGuard
316
+ >>>
317
+ >>> guard = RaxeCrewGuard(Raxe())
318
+ >>>
319
+ >>> crew = Crew(
320
+ ... agents=[researcher, writer],
321
+ ... tasks=[research_task, write_task],
322
+ ... step_callback=guard.step_callback,
323
+ ... task_callback=guard.task_callback,
324
+ ... )
325
+ >>>
326
+ >>> result = crew.kickoff()
327
+ >>> print(f"Threats detected: {guard.stats.threats_detected}")
328
+
329
+ Example (Wrapper Mode):
330
+ >>> guard = RaxeCrewGuard(Raxe())
331
+ >>> protected_crew = guard.protect_crew(crew)
332
+ >>> result = protected_crew.kickoff()
333
+
334
+ Example (Strict Mode):
335
+ >>> from raxe.sdk.integrations.agent_scanner import ScanMode
336
+ >>>
337
+ >>> config = CrewGuardConfig(
338
+ ... mode=ScanMode.BLOCK_ON_HIGH,
339
+ ... wrap_tools=True,
340
+ ... )
341
+ >>> guard = RaxeCrewGuard(Raxe(), config)
342
+ """
343
+
344
+ def __init__(
345
+ self,
346
+ raxe: Raxe,
347
+ config: CrewGuardConfig | None = None,
348
+ ) -> None:
349
+ """Initialize CrewAI guard.
350
+
351
+ Args:
352
+ raxe: RAXE client instance for scanning
353
+ config: Optional guard configuration
354
+ """
355
+ self._raxe = raxe
356
+ self._config = config or CrewGuardConfig()
357
+ self._scanner = create_agent_scanner(
358
+ raxe, self._config.to_agent_scanner_config(), integration_type="crewai"
359
+ )
360
+ self._stats = CrewScanStats()
361
+
362
+ logger.debug(
363
+ "RaxeCrewGuard initialized",
364
+ extra={
365
+ "mode": self._config.mode.value,
366
+ "wrap_tools": self._config.wrap_tools,
367
+ },
368
+ )
369
+
370
+ @property
371
+ def raxe(self) -> Raxe:
372
+ """Get the RAXE client instance."""
373
+ return self._raxe
374
+
375
+ @property
376
+ def config(self) -> CrewGuardConfig:
377
+ """Get the guard configuration."""
378
+ return self._config
379
+
380
+ @property
381
+ def scanner(self) -> AgentScanner:
382
+ """Get the underlying AgentScanner."""
383
+ return self._scanner
384
+
385
+ @property
386
+ def stats(self) -> CrewScanStats:
387
+ """Get scan statistics for current/last crew run."""
388
+ return self._stats
389
+
390
+ def reset_stats(self) -> None:
391
+ """Reset scan statistics (call before new crew run)."""
392
+ self._stats = CrewScanStats()
393
+
394
+ def _raise_security_exception(self, result: AgentScanResult) -> None:
395
+ """Raise SecurityException from an AgentScanResult.
396
+
397
+ Handles both cases where pipeline_result is available or not.
398
+
399
+ Args:
400
+ result: AgentScanResult with threat details
401
+
402
+ Raises:
403
+ SecurityException: Always raises with appropriate error info
404
+ """
405
+ from raxe.sdk.exceptions import security_threat_detected_error
406
+
407
+ if result.pipeline_result is not None:
408
+ raise SecurityException(result.pipeline_result)
409
+
410
+ # Create exception from AgentScanResult data
411
+ error = security_threat_detected_error(
412
+ severity=str(result.severity or "UNKNOWN"),
413
+ detection_count=result.detection_count,
414
+ )
415
+ message = (
416
+ f"Security threat detected: {result.severity} "
417
+ f"({result.detection_count} detection(s))"
418
+ )
419
+ exc = SecurityException.__new__(SecurityException)
420
+ exc.result = None
421
+ exc.error = error
422
+ Exception.__init__(exc, message)
423
+ raise exc
424
+
425
+ def step_callback(self, step_output: Any) -> None:
426
+ """Callback for CrewAI step events.
427
+
428
+ This callback is called after each agent step (thought-action-observation).
429
+ Register with Crew(step_callback=guard.step_callback).
430
+
431
+ Args:
432
+ step_output: Step output from CrewAI (varies by version)
433
+ Can be AgentAction, ToolResult, or string output
434
+
435
+ Example:
436
+ crew = Crew(
437
+ agents=[agent],
438
+ tasks=[task],
439
+ step_callback=guard.step_callback,
440
+ )
441
+ """
442
+ if not self._config.scan_step_outputs:
443
+ return
444
+
445
+ try:
446
+ # Extract text from step output
447
+ text = self._extract_step_text(step_output)
448
+ if not text:
449
+ return
450
+
451
+ # Extract agent info if available
452
+ agent_name = self._extract_agent_name(step_output)
453
+
454
+ # Create scan context
455
+ context = ScanContext(
456
+ message_type=MessageType.AGENT_TO_AGENT,
457
+ sender_name=agent_name,
458
+ metadata={
459
+ "source": "step_callback",
460
+ "step_type": type(step_output).__name__,
461
+ },
462
+ )
463
+
464
+ # Perform scan
465
+ result = self._scanner.scan_message(text, context=context)
466
+
467
+ # Record stats
468
+ self._stats.record_scan("step", result, result.should_block)
469
+
470
+ # Handle blocking if configured
471
+ if result.should_block:
472
+ logger.warning(
473
+ "crew_step_blocked",
474
+ extra={
475
+ "agent": agent_name,
476
+ "severity": result.severity,
477
+ "prompt_hash": result.prompt_hash,
478
+ },
479
+ )
480
+ # Note: CrewAI step_callback doesn't support blocking
481
+ # The threat is logged for monitoring
482
+
483
+ except Exception as e:
484
+ # Never fail the crew due to scanning errors
485
+ logger.error(
486
+ "crew_step_scan_error",
487
+ extra={"error": str(e), "error_type": type(e).__name__},
488
+ )
489
+
490
+ def task_callback(self, task_output: Any) -> None:
491
+ """Callback for CrewAI task completion events.
492
+
493
+ This callback is called after each task completes.
494
+ Register with Crew(task_callback=guard.task_callback).
495
+
496
+ Args:
497
+ task_output: TaskOutput from CrewAI with task description and result
498
+
499
+ Example:
500
+ crew = Crew(
501
+ agents=[agent],
502
+ tasks=[task],
503
+ task_callback=guard.task_callback,
504
+ )
505
+ """
506
+ if not self._config.scan_task_outputs:
507
+ return
508
+
509
+ try:
510
+ # Extract text from task output
511
+ text = self._extract_task_text(task_output)
512
+ if not text:
513
+ return
514
+
515
+ # Extract task info
516
+ task_description = self._extract_task_description(task_output)
517
+ agent_name = self._extract_agent_from_task(task_output)
518
+
519
+ # Create scan context
520
+ context = ScanContext(
521
+ message_type=MessageType.AGENT_RESPONSE,
522
+ sender_name=agent_name,
523
+ metadata={
524
+ "source": "task_callback",
525
+ "task_description": (
526
+ task_description[:200] if task_description else None
527
+ ),
528
+ },
529
+ )
530
+
531
+ # Perform scan
532
+ result = self._scanner.scan_message(text, context=context)
533
+
534
+ # Record stats
535
+ self._stats.record_scan("task", result, result.should_block)
536
+
537
+ # Handle blocking if configured
538
+ if result.should_block:
539
+ logger.warning(
540
+ "crew_task_blocked",
541
+ extra={
542
+ "agent": agent_name,
543
+ "severity": result.severity,
544
+ "task": task_description[:100] if task_description else None,
545
+ "prompt_hash": result.prompt_hash,
546
+ },
547
+ )
548
+ # Note: CrewAI task_callback doesn't support blocking
549
+ # The threat is logged for monitoring
550
+
551
+ except Exception as e:
552
+ # Never fail the crew due to scanning errors
553
+ logger.error(
554
+ "crew_task_scan_error",
555
+ extra={"error": str(e), "error_type": type(e).__name__},
556
+ )
557
+
558
+ def before_kickoff(self, inputs: dict[str, Any]) -> dict[str, Any]:
559
+ """Callback for before crew kickoff.
560
+
561
+ Scan inputs before crew execution begins.
562
+ Use with Crew @before_kickoff decorator or before_kickoff callback.
563
+
564
+ Args:
565
+ inputs: Input dictionary to the crew
566
+
567
+ Returns:
568
+ Original inputs (or modified if implementing input sanitization)
569
+
570
+ Raises:
571
+ SecurityException: If blocking mode and threat detected
572
+
573
+ Example:
574
+ @CrewBase
575
+ class MyCrew:
576
+ @before_kickoff
577
+ def check_inputs(self, inputs):
578
+ return guard.before_kickoff(inputs)
579
+ """
580
+ if not self._config.scan_crew_inputs:
581
+ return inputs
582
+
583
+ # Reset stats for new run
584
+ self.reset_stats()
585
+
586
+ try:
587
+ # Scan all string inputs
588
+ for key, value in inputs.items():
589
+ if isinstance(value, str) and value.strip():
590
+ context = ScanContext(
591
+ message_type=MessageType.HUMAN_INPUT,
592
+ metadata={
593
+ "source": "before_kickoff",
594
+ "input_key": key,
595
+ },
596
+ )
597
+
598
+ result = self._scanner.scan_message(value, context=context)
599
+
600
+ if result.should_block:
601
+ logger.warning(
602
+ "crew_input_blocked",
603
+ extra={
604
+ "input_key": key,
605
+ "severity": result.severity,
606
+ "prompt_hash": result.prompt_hash,
607
+ },
608
+ )
609
+ self._raise_security_exception(result)
610
+
611
+ except (SecurityException, ThreatDetectedError):
612
+ raise
613
+ except Exception as e:
614
+ logger.error(
615
+ "crew_input_scan_error",
616
+ extra={"error": str(e), "error_type": type(e).__name__},
617
+ )
618
+
619
+ return inputs
620
+
621
+ def after_kickoff(self, output: Any) -> Any:
622
+ """Callback for after crew kickoff completes.
623
+
624
+ Scan final crew output.
625
+ Use with Crew @after_kickoff decorator or after_kickoff callback.
626
+
627
+ Args:
628
+ output: Final output from the crew
629
+
630
+ Returns:
631
+ Original output
632
+
633
+ Example:
634
+ @CrewBase
635
+ class MyCrew:
636
+ @after_kickoff
637
+ def check_output(self, output):
638
+ return guard.after_kickoff(output)
639
+ """
640
+ if not self._config.scan_crew_outputs:
641
+ return output
642
+
643
+ try:
644
+ # Extract text from output
645
+ text = self._extract_crew_output_text(output)
646
+ if text:
647
+ context = ScanContext(
648
+ message_type=MessageType.AGENT_RESPONSE,
649
+ metadata={"source": "after_kickoff"},
650
+ )
651
+
652
+ result = self._scanner.scan_message(text, context=context)
653
+
654
+ if result.has_threats:
655
+ logger.warning(
656
+ "crew_output_threat",
657
+ extra={
658
+ "severity": result.severity,
659
+ "prompt_hash": result.prompt_hash,
660
+ },
661
+ )
662
+
663
+ except Exception as e:
664
+ logger.error(
665
+ "crew_output_scan_error",
666
+ extra={"error": str(e), "error_type": type(e).__name__},
667
+ )
668
+
669
+ return output
670
+
671
+ def protect_crew(self, crew: CrewType) -> CrewType:
672
+ """Wrap a Crew with automatic security scanning.
673
+
674
+ This method returns a proxy that automatically applies scanning
675
+ to all crew operations.
676
+
677
+ Args:
678
+ crew: CrewAI Crew instance to protect
679
+
680
+ Returns:
681
+ Protected Crew instance with scanning enabled
682
+
683
+ Example:
684
+ >>> from crewai import Crew
685
+ >>> crew = Crew(agents=[...], tasks=[...])
686
+ >>> protected_crew = guard.protect_crew(crew)
687
+ >>> result = protected_crew.kickoff()
688
+ """
689
+ # Store original callbacks
690
+ original_step_callback = getattr(crew, "step_callback", None)
691
+ original_task_callback = getattr(crew, "task_callback", None)
692
+
693
+ # Combine with guard callbacks
694
+ def combined_step_callback(step_output: Any) -> None:
695
+ self.step_callback(step_output)
696
+ if original_step_callback:
697
+ original_step_callback(step_output)
698
+
699
+ def combined_task_callback(task_output: Any) -> None:
700
+ self.task_callback(task_output)
701
+ if original_task_callback:
702
+ original_task_callback(task_output)
703
+
704
+ # Set combined callbacks
705
+ crew.step_callback = combined_step_callback
706
+ crew.task_callback = combined_task_callback
707
+
708
+ # Optionally wrap tools
709
+ if self._config.wrap_tools:
710
+ self._wrap_crew_tools(crew)
711
+
712
+ logger.debug(
713
+ "crew_protected",
714
+ extra={
715
+ "mode": self._config.mode.value,
716
+ "wrap_tools": self._config.wrap_tools,
717
+ },
718
+ )
719
+
720
+ return crew
721
+
722
+ def wrap_tool(self, tool: T) -> T:
723
+ """Wrap a CrewAI tool with input/output scanning.
724
+
725
+ Use this to protect individual tools. For automatic wrapping
726
+ of all crew tools, use protect_crew() with wrap_tools=True.
727
+
728
+ Args:
729
+ tool: CrewAI BaseTool instance
730
+
731
+ Returns:
732
+ Wrapped tool with scanning enabled
733
+
734
+ Example:
735
+ >>> from crewai_tools import SerperDevTool
736
+ >>> search_tool = guard.wrap_tool(SerperDevTool())
737
+ >>> agent = Agent(tools=[search_tool])
738
+ """
739
+ # Get the original _run method
740
+ original_run = getattr(tool, "_run", None)
741
+ if original_run is None:
742
+ logger.warning(
743
+ "tool_wrap_failed",
744
+ extra={"reason": "no _run method", "tool": type(tool).__name__},
745
+ )
746
+ return tool
747
+
748
+ @functools.wraps(original_run)
749
+ def wrapped_run(*args: Any, **kwargs: Any) -> Any:
750
+ # Scan inputs
751
+ if self._config.scan_tool_inputs:
752
+ input_text = self._extract_tool_input(args, kwargs)
753
+ if input_text:
754
+ self._scan_tool_io(
755
+ text=input_text,
756
+ tool_name=getattr(tool, "name", type(tool).__name__),
757
+ io_type="input",
758
+ )
759
+
760
+ # Execute original
761
+ result = original_run(*args, **kwargs)
762
+
763
+ # Scan outputs
764
+ if self._config.scan_tool_outputs:
765
+ output_text = str(result) if result else None
766
+ if output_text:
767
+ self._scan_tool_io(
768
+ text=output_text,
769
+ tool_name=getattr(tool, "name", type(tool).__name__),
770
+ io_type="output",
771
+ )
772
+
773
+ return result
774
+
775
+ # Replace _run method
776
+ tool._run = wrapped_run
777
+
778
+ logger.debug(
779
+ "tool_wrapped",
780
+ extra={"tool": getattr(tool, "name", type(tool).__name__)},
781
+ )
782
+
783
+ return tool
784
+
785
+ def wrap_tools(self, tools: list[T]) -> list[T]:
786
+ """Wrap multiple CrewAI tools with input/output scanning.
787
+
788
+ Convenience method to wrap multiple tools at once.
789
+
790
+ Args:
791
+ tools: List of CrewAI BaseTool instances
792
+
793
+ Returns:
794
+ List of wrapped tools with scanning enabled
795
+
796
+ Example:
797
+ >>> from crewai_tools import SerperDevTool, FileReadTool
798
+ >>> tools = [SerperDevTool(), FileReadTool()]
799
+ >>> wrapped_tools = guard.wrap_tools(tools)
800
+ >>> agent = Agent(tools=wrapped_tools)
801
+ """
802
+ return [self.wrap_tool(tool) for tool in tools]
803
+
804
+ def _scan_tool_io(
805
+ self,
806
+ text: str,
807
+ tool_name: str,
808
+ io_type: str,
809
+ ) -> None:
810
+ """Scan tool input or output.
811
+
812
+ Args:
813
+ text: Text to scan
814
+ tool_name: Name of the tool
815
+ io_type: "input" or "output"
816
+ """
817
+ try:
818
+ message_type = (
819
+ MessageType.FUNCTION_CALL
820
+ if io_type == "input"
821
+ else MessageType.FUNCTION_RESULT
822
+ )
823
+
824
+ context = ScanContext(
825
+ message_type=message_type,
826
+ metadata={
827
+ "source": f"tool_{io_type}",
828
+ "tool_name": tool_name,
829
+ },
830
+ )
831
+
832
+ result = self._scanner.scan_message(text, context=context)
833
+
834
+ # Record stats
835
+ self._stats.record_scan("tool", result, result.should_block)
836
+
837
+ if result.has_threats:
838
+ logger.warning(
839
+ f"crew_tool_{io_type}_threat",
840
+ extra={
841
+ "tool": tool_name,
842
+ "severity": result.severity,
843
+ "prompt_hash": result.prompt_hash,
844
+ },
845
+ )
846
+
847
+ # Block if configured
848
+ if result.should_block:
849
+ self._raise_security_exception(result)
850
+
851
+ except SecurityException:
852
+ raise
853
+ except Exception as e:
854
+ logger.error(
855
+ f"crew_tool_{io_type}_scan_error",
856
+ extra={
857
+ "tool": tool_name,
858
+ "error": str(e),
859
+ "error_type": type(e).__name__,
860
+ },
861
+ )
862
+
863
+ def _wrap_crew_tools(self, crew: Any) -> None:
864
+ """Wrap all tools in a crew's agents.
865
+
866
+ Args:
867
+ crew: CrewAI Crew instance
868
+ """
869
+ agents = getattr(crew, "agents", [])
870
+ for agent in agents:
871
+ tools = getattr(agent, "tools", [])
872
+ wrapped_tools = [self.wrap_tool(tool) for tool in tools]
873
+ agent.tools = wrapped_tools
874
+
875
+ def _extract_step_text(self, step_output: Any) -> str | None:
876
+ """Extract text from step output.
877
+
878
+ Uses unified extractors where applicable, with CrewAI-specific fallbacks.
879
+
880
+ Args:
881
+ step_output: Step output from CrewAI
882
+
883
+ Returns:
884
+ Text content or None
885
+ """
886
+ if isinstance(step_output, str):
887
+ return step_output
888
+
889
+ # Handle AgentAction
890
+ if hasattr(step_output, "tool_input"):
891
+ return str(step_output.tool_input)
892
+
893
+ # Handle ToolResult
894
+ if hasattr(step_output, "result"):
895
+ return str(step_output.result)
896
+
897
+ # Handle dict using unified extractor
898
+ if isinstance(step_output, dict):
899
+ return extract_text_from_dict(step_output, ("output", "text", "result"))
900
+
901
+ # Handle thought attribute
902
+ if hasattr(step_output, "thought"):
903
+ return str(step_output.thought)
904
+
905
+ # Handle log attribute
906
+ if hasattr(step_output, "log"):
907
+ return str(step_output.log)
908
+
909
+ # Fallback to string conversion
910
+ try:
911
+ text = str(step_output)
912
+ if text and len(text) < 10000: # Sanity check
913
+ return text
914
+ except Exception:
915
+ pass
916
+
917
+ return None
918
+
919
+ def _extract_task_text(self, task_output: Any) -> str | None:
920
+ """Extract text from task output.
921
+
922
+ Uses unified extractors where applicable, with CrewAI-specific fallbacks.
923
+
924
+ Args:
925
+ task_output: TaskOutput from CrewAI
926
+
927
+ Returns:
928
+ Text content or None
929
+ """
930
+ if isinstance(task_output, str):
931
+ return task_output
932
+
933
+ # Handle TaskOutput object
934
+ if hasattr(task_output, "raw"):
935
+ return str(task_output.raw)
936
+
937
+ if hasattr(task_output, "output"):
938
+ return str(task_output.output)
939
+
940
+ if hasattr(task_output, "result"):
941
+ return str(task_output.result)
942
+
943
+ # Handle dict using unified extractor
944
+ if isinstance(task_output, dict):
945
+ return extract_text_from_dict(
946
+ task_output, ("raw", "output", "result", "text")
947
+ )
948
+
949
+ return None
950
+
951
+ def _extract_task_description(self, task_output: Any) -> str | None:
952
+ """Extract task description from task output.
953
+
954
+ Args:
955
+ task_output: TaskOutput from CrewAI
956
+
957
+ Returns:
958
+ Task description or None
959
+ """
960
+ if hasattr(task_output, "description"):
961
+ return str(task_output.description)
962
+
963
+ if hasattr(task_output, "task") and hasattr(task_output.task, "description"):
964
+ return str(task_output.task.description)
965
+
966
+ if isinstance(task_output, dict) and "description" in task_output:
967
+ return str(task_output["description"])
968
+
969
+ return None
970
+
971
+ def _extract_agent_name(self, step_output: Any) -> str | None:
972
+ """Extract agent name from step output.
973
+
974
+ Args:
975
+ step_output: Step output from CrewAI
976
+
977
+ Returns:
978
+ Agent name or None
979
+ """
980
+ # Try direct agent attribute
981
+ if hasattr(step_output, "agent"):
982
+ agent = step_output.agent
983
+ if hasattr(agent, "role"):
984
+ return str(agent.role)
985
+ if hasattr(agent, "name"):
986
+ return str(agent.name)
987
+ return str(agent)
988
+
989
+ # Try agent_name attribute
990
+ if hasattr(step_output, "agent_name"):
991
+ return str(step_output.agent_name)
992
+
993
+ # Try dict
994
+ if isinstance(step_output, dict):
995
+ if "agent" in step_output:
996
+ return str(step_output["agent"])
997
+ if "agent_name" in step_output:
998
+ return str(step_output["agent_name"])
999
+
1000
+ return None
1001
+
1002
+ def _extract_agent_from_task(self, task_output: Any) -> str | None:
1003
+ """Extract agent name from task output.
1004
+
1005
+ Args:
1006
+ task_output: TaskOutput from CrewAI
1007
+
1008
+ Returns:
1009
+ Agent name or None
1010
+ """
1011
+ if hasattr(task_output, "agent"):
1012
+ agent = task_output.agent
1013
+ if hasattr(agent, "role"):
1014
+ return str(agent.role)
1015
+ if hasattr(agent, "name"):
1016
+ return str(agent.name)
1017
+
1018
+ if hasattr(task_output, "task") and hasattr(task_output.task, "agent"):
1019
+ agent = task_output.task.agent
1020
+ if hasattr(agent, "role"):
1021
+ return str(agent.role)
1022
+
1023
+ return None
1024
+
1025
+ def _extract_crew_output_text(self, output: Any) -> str | None:
1026
+ """Extract text from crew output.
1027
+
1028
+ Uses unified extractors where applicable, with CrewAI-specific fallbacks.
1029
+
1030
+ Args:
1031
+ output: CrewOutput from CrewAI
1032
+
1033
+ Returns:
1034
+ Text content or None
1035
+ """
1036
+ if isinstance(output, str):
1037
+ return output
1038
+
1039
+ # Handle CrewOutput
1040
+ if hasattr(output, "raw"):
1041
+ return str(output.raw)
1042
+
1043
+ if hasattr(output, "result"):
1044
+ return str(output.result)
1045
+
1046
+ # Handle dict using unified extractor
1047
+ if isinstance(output, dict):
1048
+ return extract_text_from_dict(
1049
+ output, ("raw", "result", "output", "text")
1050
+ )
1051
+
1052
+ return None
1053
+
1054
+ def _extract_tool_input(
1055
+ self,
1056
+ args: tuple[Any, ...],
1057
+ kwargs: dict[str, Any],
1058
+ ) -> str | None:
1059
+ """Extract tool input text from arguments.
1060
+
1061
+ Args:
1062
+ args: Positional arguments
1063
+ kwargs: Keyword arguments
1064
+
1065
+ Returns:
1066
+ Input text or None
1067
+ """
1068
+ # Check kwargs for common input keys
1069
+ for key in ["query", "input", "text", "question", "prompt"]:
1070
+ if key in kwargs:
1071
+ return str(kwargs[key])
1072
+
1073
+ # Check first string arg
1074
+ for arg in args:
1075
+ if isinstance(arg, str):
1076
+ return arg
1077
+
1078
+ # Fallback to all kwargs as string
1079
+ if kwargs:
1080
+ return str(kwargs)
1081
+
1082
+ return None
1083
+
1084
+ def get_summary(self) -> dict[str, Any]:
1085
+ """Get summary of guard activity.
1086
+
1087
+ Returns:
1088
+ Dictionary with configuration and statistics
1089
+ """
1090
+ return {
1091
+ "config": {
1092
+ "mode": self._config.mode.value,
1093
+ "wrap_tools": self._config.wrap_tools,
1094
+ "scan_step_outputs": self._config.scan_step_outputs,
1095
+ "scan_task_outputs": self._config.scan_task_outputs,
1096
+ },
1097
+ "stats": self._stats.to_dict(),
1098
+ }
1099
+
1100
+ # =========================================================================
1101
+ # Async Callback Methods
1102
+ # =========================================================================
1103
+ # Async versions of callbacks for use in async contexts.
1104
+
1105
+ async def step_callback_async(self, step_output: Any) -> None:
1106
+ """Async callback for CrewAI step events.
1107
+
1108
+ Async version of step_callback for use in async contexts.
1109
+ Uses asyncio.get_event_loop().run_in_executor() for non-blocking scans.
1110
+
1111
+ Args:
1112
+ step_output: Step output from CrewAI
1113
+ """
1114
+ import asyncio
1115
+
1116
+ if not self._config.scan_step_outputs:
1117
+ return
1118
+
1119
+ try:
1120
+ text = self._extract_step_text(step_output)
1121
+ if not text:
1122
+ return
1123
+
1124
+ agent_name = self._extract_agent_name(step_output)
1125
+
1126
+ context = ScanContext(
1127
+ message_type=MessageType.AGENT_TO_AGENT,
1128
+ sender_name=agent_name,
1129
+ metadata={
1130
+ "source": "step_callback_async",
1131
+ "step_type": type(step_output).__name__,
1132
+ },
1133
+ )
1134
+
1135
+ result = await asyncio.get_event_loop().run_in_executor(
1136
+ None, lambda: self._scanner.scan_message(text, context=context)
1137
+ )
1138
+
1139
+ self._stats.record_scan("step", result, result.should_block)
1140
+
1141
+ if result.should_block:
1142
+ logger.warning(
1143
+ "crew_step_blocked_async",
1144
+ extra={
1145
+ "agent": agent_name,
1146
+ "severity": result.severity,
1147
+ "prompt_hash": result.prompt_hash,
1148
+ },
1149
+ )
1150
+
1151
+ except Exception as e:
1152
+ logger.error(
1153
+ "crew_step_scan_error_async",
1154
+ extra={"error": str(e), "error_type": type(e).__name__},
1155
+ )
1156
+
1157
+ async def task_callback_async(self, task_output: Any) -> None:
1158
+ """Async callback for CrewAI task completion events.
1159
+
1160
+ Async version of task_callback for use in async contexts.
1161
+
1162
+ Args:
1163
+ task_output: TaskOutput from CrewAI
1164
+ """
1165
+ import asyncio
1166
+
1167
+ if not self._config.scan_task_outputs:
1168
+ return
1169
+
1170
+ try:
1171
+ text = self._extract_task_text(task_output)
1172
+ if not text:
1173
+ return
1174
+
1175
+ task_description = self._extract_task_description(task_output)
1176
+ agent_name = self._extract_agent_from_task(task_output)
1177
+
1178
+ context = ScanContext(
1179
+ message_type=MessageType.AGENT_RESPONSE,
1180
+ sender_name=agent_name,
1181
+ metadata={
1182
+ "source": "task_callback_async",
1183
+ "task_description": (
1184
+ task_description[:200] if task_description else None
1185
+ ),
1186
+ },
1187
+ )
1188
+
1189
+ result = await asyncio.get_event_loop().run_in_executor(
1190
+ None, lambda: self._scanner.scan_message(text, context=context)
1191
+ )
1192
+
1193
+ self._stats.record_scan("task", result, result.should_block)
1194
+
1195
+ if result.should_block:
1196
+ logger.warning(
1197
+ "crew_task_blocked_async",
1198
+ extra={
1199
+ "agent": agent_name,
1200
+ "severity": result.severity,
1201
+ "task": task_description[:100] if task_description else None,
1202
+ "prompt_hash": result.prompt_hash,
1203
+ },
1204
+ )
1205
+
1206
+ except Exception as e:
1207
+ logger.error(
1208
+ "crew_task_scan_error_async",
1209
+ extra={"error": str(e), "error_type": type(e).__name__},
1210
+ )
1211
+
1212
+ async def before_kickoff_async(
1213
+ self, inputs: dict[str, Any]
1214
+ ) -> dict[str, Any]:
1215
+ """Async callback for before crew kickoff.
1216
+
1217
+ Async version of before_kickoff for use in async contexts.
1218
+
1219
+ Args:
1220
+ inputs: Input dictionary to the crew
1221
+
1222
+ Returns:
1223
+ Original inputs
1224
+
1225
+ Raises:
1226
+ SecurityException: If blocking mode and threat detected
1227
+ """
1228
+ import asyncio
1229
+
1230
+ if not self._config.scan_crew_inputs:
1231
+ return inputs
1232
+
1233
+ self.reset_stats()
1234
+
1235
+ try:
1236
+ for key, value in inputs.items():
1237
+ if isinstance(value, str) and value.strip():
1238
+ context = ScanContext(
1239
+ message_type=MessageType.HUMAN_INPUT,
1240
+ metadata={
1241
+ "source": "before_kickoff_async",
1242
+ "input_key": key,
1243
+ },
1244
+ )
1245
+
1246
+ result = await asyncio.get_event_loop().run_in_executor(
1247
+ None, lambda v=value: self._scanner.scan_message(v, context=context)
1248
+ )
1249
+
1250
+ if result.should_block:
1251
+ logger.warning(
1252
+ "crew_input_blocked_async",
1253
+ extra={
1254
+ "input_key": key,
1255
+ "severity": result.severity,
1256
+ "prompt_hash": result.prompt_hash,
1257
+ },
1258
+ )
1259
+ self._raise_security_exception(result)
1260
+
1261
+ except (SecurityException, ThreatDetectedError):
1262
+ raise
1263
+ except Exception as e:
1264
+ logger.error(
1265
+ "crew_input_scan_error_async",
1266
+ extra={"error": str(e), "error_type": type(e).__name__},
1267
+ )
1268
+
1269
+ return inputs
1270
+
1271
+ async def after_kickoff_async(self, output: Any) -> Any:
1272
+ """Async callback for after crew kickoff completes.
1273
+
1274
+ Async version of after_kickoff for use in async contexts.
1275
+
1276
+ Args:
1277
+ output: Final output from the crew
1278
+
1279
+ Returns:
1280
+ Original output
1281
+ """
1282
+ import asyncio
1283
+
1284
+ if not self._config.scan_crew_outputs:
1285
+ return output
1286
+
1287
+ try:
1288
+ text = self._extract_crew_output_text(output)
1289
+ if text:
1290
+ context = ScanContext(
1291
+ message_type=MessageType.AGENT_RESPONSE,
1292
+ metadata={"source": "after_kickoff_async"},
1293
+ )
1294
+
1295
+ result = await asyncio.get_event_loop().run_in_executor(
1296
+ None, lambda: self._scanner.scan_message(text, context=context)
1297
+ )
1298
+
1299
+ if result.has_threats:
1300
+ logger.warning(
1301
+ "crew_output_threat_async",
1302
+ extra={
1303
+ "severity": result.severity,
1304
+ "prompt_hash": result.prompt_hash,
1305
+ },
1306
+ )
1307
+
1308
+ except Exception as e:
1309
+ logger.error(
1310
+ "crew_output_scan_error_async",
1311
+ extra={"error": str(e), "error_type": type(e).__name__},
1312
+ )
1313
+
1314
+ return output
1315
+
1316
+ def __repr__(self) -> str:
1317
+ """String representation."""
1318
+ return (
1319
+ f"RaxeCrewGuard("
1320
+ f"mode={self._config.mode.value}, "
1321
+ f"scans={self._stats.total_scans}, "
1322
+ f"threats={self._stats.threats_detected})"
1323
+ )
1324
+
1325
+
1326
+ # Convenience function for quick setup
1327
+ def create_crew_guard(
1328
+ raxe: Raxe | None = None,
1329
+ mode: ScanMode = ScanMode.LOG_ONLY,
1330
+ **kwargs: Any,
1331
+ ) -> RaxeCrewGuard:
1332
+ """Create a RaxeCrewGuard with common defaults.
1333
+
1334
+ Convenience function for quick guard creation.
1335
+
1336
+ Args:
1337
+ raxe: Optional RAXE client (creates default if None)
1338
+ mode: Scan mode (default: LOG_ONLY)
1339
+ **kwargs: Additional CrewGuardConfig parameters
1340
+
1341
+ Returns:
1342
+ Configured RaxeCrewGuard instance
1343
+
1344
+ Example:
1345
+ >>> from raxe.sdk.integrations import create_crew_guard
1346
+ >>> from raxe.sdk.integrations.agent_scanner import ScanMode
1347
+ >>>
1348
+ >>> # Quick monitoring setup
1349
+ >>> guard = create_crew_guard()
1350
+ >>>
1351
+ >>> # Strict mode
1352
+ >>> guard = create_crew_guard(mode=ScanMode.BLOCK_ON_HIGH)
1353
+ """
1354
+ from raxe.sdk.client import Raxe
1355
+
1356
+ if raxe is None:
1357
+ raxe = Raxe()
1358
+
1359
+ config = CrewGuardConfig(mode=mode, **kwargs)
1360
+ return RaxeCrewGuard(raxe, config)
1361
+
1362
+
1363
+ __all__ = [
1364
+ "CrewGuardConfig",
1365
+ "CrewScanStats",
1366
+ "RaxeCrewGuard",
1367
+ "create_crew_guard",
1368
+ ]