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,947 @@
1
+ """Secure credential storage for RAXE API keys.
2
+
3
+ Provides secure storage for API credentials with:
4
+ - chmod 600 protection (owner read/write only)
5
+ - Temporary key generation for zero-friction onboarding
6
+ - Key upgrade support from temporary to permanent
7
+ - Expiry tracking for temporary keys (14 days)
8
+
9
+ File format (credentials.json):
10
+ {
11
+ "api_key": "raxe_temp_...",
12
+ "key_type": "temporary",
13
+ "installation_id": "inst_...",
14
+ "created_at": "2025-01-26T00:00:00Z",
15
+ "expires_at": "2025-02-09T00:00:00Z",
16
+ "first_seen_at": null
17
+ }
18
+
19
+ Security:
20
+ - File created with chmod 600 (Unix)
21
+ - Warns if permissions too permissive on load
22
+ - API key values never logged
23
+ """
24
+
25
+ from __future__ import annotations
26
+
27
+ import hashlib
28
+ import json
29
+ import logging
30
+ import os
31
+ import platform
32
+ import re
33
+ import secrets
34
+ import stat
35
+ from dataclasses import asdict, dataclass
36
+ from datetime import datetime, timezone
37
+ from pathlib import Path
38
+ from typing import Literal
39
+ from uuid import uuid4
40
+
41
+ logger = logging.getLogger(__name__)
42
+
43
+ # Key format validation patterns (from spec section 2.2)
44
+ # Allows alphanumeric, hyphens, and underscores in the random suffix
45
+ # (underscores are valid in base64url encoding used by backend)
46
+ KEY_PATTERNS: dict[str, re.Pattern[str]] = {
47
+ "temporary": re.compile(r"^raxe_temp_[a-zA-Z0-9_\-]{32}$"),
48
+ "live": re.compile(r"^raxe_live_[a-zA-Z0-9_\-]{32}$"),
49
+ "test": re.compile(r"^raxe_test_[a-zA-Z0-9_\-]{32}$"),
50
+ }
51
+
52
+ # Combined pattern for any valid key
53
+ ANY_KEY_PATTERN = re.compile(r"^raxe_(live|test|temp)_[a-zA-Z0-9_\-]{32}$")
54
+
55
+ # Temporary key expiry (14 days from creation)
56
+ TEMP_KEY_EXPIRY_DAYS = 14
57
+
58
+ # Default credential file location
59
+ DEFAULT_CREDENTIAL_DIR = Path.home() / ".raxe"
60
+ DEFAULT_CREDENTIAL_FILE = DEFAULT_CREDENTIAL_DIR / "credentials.json"
61
+
62
+
63
+ def compute_key_id(api_key: str) -> str:
64
+ """Compute BigQuery-compatible key ID from API key.
65
+
66
+ The key ID is a truncated SHA256 hash of the API key, prefixed with "key_".
67
+ This allows the server to link historical events across key upgrades
68
+ without storing the actual API key values.
69
+
70
+ Args:
71
+ api_key: The full API key (e.g., "raxe_temp_b32a43d2...")
72
+
73
+ Returns:
74
+ The key ID (e.g., "key_23cc2f9f21f9")
75
+
76
+ Example:
77
+ >>> compute_key_id("raxe_temp_abc123def456789012345678901234")
78
+ 'key_...' # 12 hex chars after "key_" prefix
79
+ """
80
+ key_hash = hashlib.sha256(api_key.encode()).hexdigest()
81
+ return f"key_{key_hash[:12]}"
82
+
83
+
84
+ class CredentialError(Exception):
85
+ """Error related to credential storage or validation."""
86
+
87
+
88
+ def _get_default_console_keys_url() -> str:
89
+ """Get default console keys URL from centralized endpoints."""
90
+ from raxe.infrastructure.config.endpoints import get_console_url
91
+ return f"{get_console_url()}/keys"
92
+
93
+
94
+ class CredentialExpiredError(CredentialError):
95
+ """API key has expired.
96
+
97
+ This exception is raised when a temporary API key has exceeded its
98
+ 14-day expiry period. Users should obtain a permanent key from the
99
+ RAXE console.
100
+
101
+ Attributes:
102
+ message: Human-readable message explaining the expiry.
103
+ console_url: URL where users can get a new permanent key.
104
+ days_expired: Number of days past the expiry date.
105
+ """
106
+
107
+ @staticmethod
108
+ def get_default_console_url() -> str:
109
+ """Get default console URL from centralized endpoints."""
110
+ return _get_default_console_keys_url()
111
+
112
+ def __init__(
113
+ self,
114
+ message: str,
115
+ *,
116
+ console_url: str | None = None,
117
+ days_expired: int = 0,
118
+ ) -> None:
119
+ """Initialize CredentialExpiredError.
120
+
121
+ Args:
122
+ message: Human-readable error message.
123
+ console_url: URL to get a new key (uses centralized endpoint if None).
124
+ days_expired: Number of days past expiry (0 if just expired).
125
+ """
126
+ super().__init__(message)
127
+ self.console_url = console_url or _get_default_console_keys_url()
128
+ self.days_expired = days_expired
129
+
130
+
131
+ class InvalidKeyFormatError(CredentialError):
132
+ """API key format is invalid."""
133
+
134
+
135
+ @dataclass
136
+ class KeyUpgradeInfo:
137
+ """Information about a key upgrade for telemetry event creation.
138
+
139
+ Contains both the old and new key information needed to create
140
+ a key_upgrade telemetry event with proper key IDs for server-side
141
+ event linking.
142
+
143
+ Attributes:
144
+ previous_key_id: BigQuery-compatible ID for previous key (e.g., "key_23cc2f9f21f9").
145
+ new_key_id: BigQuery-compatible ID for new key (e.g., "key_7ce219b525f1").
146
+ previous_key_type: Previous key type ("temporary", "live", or "test").
147
+ new_key_type: New key type ("live" or "test").
148
+ days_on_previous: Number of days the previous key was in use (if known).
149
+ new_credentials: The newly saved Credentials object.
150
+ """
151
+
152
+ previous_key_id: str | None
153
+ new_key_id: str
154
+ previous_key_type: str | None
155
+ new_key_type: str
156
+ days_on_previous: int | None
157
+ new_credentials: "Credentials"
158
+
159
+
160
+ @dataclass
161
+ class Credentials:
162
+ """Credential data model.
163
+
164
+ Represents stored API credentials with type and expiry information.
165
+
166
+ Attributes:
167
+ api_key: The RAXE API key (raxe_temp_*, raxe_live_*, or raxe_test_*)
168
+ key_type: Type of key - temporary, live, or test
169
+ installation_id: Unique installation identifier (inst_{hex16})
170
+ created_at: ISO 8601 timestamp when credentials were created
171
+ expires_at: ISO 8601 timestamp when key expires (temporary keys only)
172
+ first_seen_at: Server-provided timestamp of first use (optional)
173
+ can_disable_telemetry: Whether the key tier allows disabling telemetry
174
+ offline_mode: Whether offline mode is permitted for this tier
175
+ tier: Key tier (temporary, community, pro, enterprise)
176
+ last_health_check: ISO 8601 timestamp of last server health check
177
+ """
178
+
179
+ api_key: str
180
+ key_type: Literal["temporary", "live", "test"]
181
+ installation_id: str
182
+ created_at: str # ISO 8601
183
+ expires_at: str | None # For temp keys (14 days from creation)
184
+ first_seen_at: str | None # Server-provided
185
+
186
+ # Cached server permissions (from /v1/health response)
187
+ can_disable_telemetry: bool = False
188
+ offline_mode: bool = False
189
+ tier: str = "temporary"
190
+ last_health_check: str | None = None # ISO 8601 timestamp
191
+
192
+ def is_expired(self) -> bool:
193
+ """Check if the credential has expired.
194
+
195
+ Returns:
196
+ True if the key has expired, False otherwise.
197
+ Permanent keys (live/test) never expire.
198
+ """
199
+ if self.expires_at is None:
200
+ return False
201
+
202
+ try:
203
+ expiry = datetime.fromisoformat(self.expires_at.replace("Z", "+00:00"))
204
+ now = datetime.now(timezone.utc)
205
+ return now >= expiry
206
+ except (ValueError, TypeError):
207
+ # If we can't parse expiry, assume not expired
208
+ logger.warning("Could not parse expires_at, assuming not expired")
209
+ return False
210
+
211
+ def is_temporary(self) -> bool:
212
+ """Check if this is a temporary key.
213
+
214
+ Returns:
215
+ True if key_type is "temporary", False otherwise.
216
+ """
217
+ return self.key_type == "temporary"
218
+
219
+ def days_until_expiry(self) -> int | None:
220
+ """Calculate days remaining until expiry.
221
+
222
+ Returns:
223
+ Number of days until expiry (0 if expired, None if no expiry).
224
+ """
225
+ if self.expires_at is None:
226
+ return None
227
+
228
+ try:
229
+ expiry = datetime.fromisoformat(self.expires_at.replace("Z", "+00:00"))
230
+ now = datetime.now(timezone.utc)
231
+ delta = expiry - now
232
+
233
+ if delta.total_seconds() <= 0:
234
+ return 0
235
+
236
+ return delta.days
237
+ except (ValueError, TypeError):
238
+ return None
239
+
240
+ def days_since_expiry(self) -> int | None:
241
+ """Calculate days since expiry (for expired keys).
242
+
243
+ Returns:
244
+ Number of days past expiry (0 if not yet expired, None if no expiry).
245
+ """
246
+ if self.expires_at is None:
247
+ return None
248
+
249
+ try:
250
+ expiry = datetime.fromisoformat(self.expires_at.replace("Z", "+00:00"))
251
+ now = datetime.now(timezone.utc)
252
+ delta = now - expiry
253
+
254
+ if delta.total_seconds() <= 0:
255
+ return 0
256
+
257
+ return delta.days
258
+ except (ValueError, TypeError):
259
+ return None
260
+
261
+ def is_health_check_stale(self, max_age_hours: int = 24) -> bool:
262
+ """Check if the cached health check is stale.
263
+
264
+ Args:
265
+ max_age_hours: Maximum age in hours before health check is stale.
266
+
267
+ Returns:
268
+ True if health check is stale or missing, False otherwise.
269
+ """
270
+ if self.last_health_check is None:
271
+ return True
272
+
273
+ try:
274
+ from datetime import timedelta
275
+
276
+ last_check = datetime.fromisoformat(
277
+ self.last_health_check.replace("Z", "+00:00")
278
+ )
279
+ now = datetime.now(timezone.utc)
280
+ age = now - last_check
281
+
282
+ return age > timedelta(hours=max_age_hours)
283
+ except (ValueError, TypeError):
284
+ return True
285
+
286
+ def to_dict(self) -> dict[str, str | None]:
287
+ """Convert credentials to dictionary.
288
+
289
+ Returns:
290
+ Dictionary representation suitable for JSON serialization.
291
+ """
292
+ return asdict(self)
293
+
294
+ @classmethod
295
+ def from_dict(cls, data: dict[str, str | None]) -> Credentials:
296
+ """Create Credentials from dictionary.
297
+
298
+ Args:
299
+ data: Dictionary with credential fields.
300
+
301
+ Returns:
302
+ Credentials instance.
303
+
304
+ Raises:
305
+ ValueError: If required fields are missing.
306
+ """
307
+ required_fields = ["api_key", "key_type", "installation_id", "created_at"]
308
+ for field in required_fields:
309
+ if field not in data:
310
+ raise ValueError(f"Missing required field: {field}")
311
+
312
+ return cls(
313
+ api_key=data["api_key"], # type: ignore[arg-type]
314
+ key_type=data["key_type"], # type: ignore[arg-type]
315
+ installation_id=data["installation_id"], # type: ignore[arg-type]
316
+ created_at=data["created_at"], # type: ignore[arg-type]
317
+ expires_at=data.get("expires_at"),
318
+ first_seen_at=data.get("first_seen_at"),
319
+ # Server permission fields (with defaults for backward compatibility)
320
+ can_disable_telemetry=data.get("can_disable_telemetry", False), # type: ignore[arg-type]
321
+ offline_mode=data.get("offline_mode", False), # type: ignore[arg-type]
322
+ tier=data.get("tier", "temporary"), # type: ignore[arg-type]
323
+ last_health_check=data.get("last_health_check"),
324
+ )
325
+
326
+
327
+ def validate_key_format(api_key: str) -> Literal["temporary", "live", "test"]:
328
+ """Validate API key format and return type.
329
+
330
+ Args:
331
+ api_key: The API key to validate.
332
+
333
+ Returns:
334
+ The key type (temporary, live, or test).
335
+
336
+ Raises:
337
+ InvalidKeyFormatError: If the key format is invalid.
338
+ """
339
+ if not api_key:
340
+ raise InvalidKeyFormatError("API key cannot be empty")
341
+
342
+ if not ANY_KEY_PATTERN.match(api_key):
343
+ raise InvalidKeyFormatError(
344
+ "Invalid API key format. Expected: raxe_{live|test|temp}_{32 chars}"
345
+ )
346
+
347
+ # Determine key type
348
+ if api_key.startswith("raxe_temp_"):
349
+ return "temporary"
350
+ elif api_key.startswith("raxe_live_"):
351
+ return "live"
352
+ elif api_key.startswith("raxe_test_"):
353
+ return "test"
354
+ else:
355
+ raise InvalidKeyFormatError("Unknown key type prefix")
356
+
357
+
358
+ def _generate_installation_id() -> str:
359
+ """Generate a unique installation identifier.
360
+
361
+ Format: inst_{uuid4_hex[:16]}
362
+
363
+ Returns:
364
+ Installation ID string.
365
+ """
366
+ hex_id = uuid4().hex[:16]
367
+ return f"inst_{hex_id}"
368
+
369
+
370
+ def _generate_temp_key() -> str:
371
+ """Generate a temporary API key.
372
+
373
+ Format: raxe_temp_{secrets.token_hex(16)}
374
+
375
+ Returns:
376
+ Temporary API key string (32 hex chars after prefix).
377
+ """
378
+ # token_hex(16) generates 32 hex characters
379
+ return f"raxe_temp_{secrets.token_hex(16)}"
380
+
381
+
382
+ def _get_utc_now_iso() -> str:
383
+ """Get current UTC time in ISO 8601 format.
384
+
385
+ Returns:
386
+ ISO 8601 formatted timestamp with Z suffix.
387
+ """
388
+ return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
389
+
390
+
391
+ def _is_windows() -> bool:
392
+ """Check if running on Windows.
393
+
394
+ Returns:
395
+ True if running on Windows, False otherwise.
396
+ """
397
+ return platform.system() == "Windows"
398
+
399
+
400
+ def _set_secure_permissions(path: Path) -> None:
401
+ """Set secure file permissions (chmod 600).
402
+
403
+ On Unix-like systems, sets file to owner read/write only.
404
+ On Windows, this is a no-op (Windows uses ACLs).
405
+
406
+ Args:
407
+ path: Path to the file.
408
+ """
409
+ if _is_windows():
410
+ # Windows uses ACLs, skip chmod
411
+ logger.debug("Skipping chmod on Windows")
412
+ return
413
+
414
+ try:
415
+ os.chmod(path, stat.S_IRUSR | stat.S_IWUSR) # 0o600
416
+ logger.debug("Set file permissions to 600")
417
+ except OSError as e:
418
+ logger.warning(f"Failed to set file permissions: {e}")
419
+
420
+
421
+ def _check_permissions(path: Path) -> bool:
422
+ """Check if file permissions are secure.
423
+
424
+ Args:
425
+ path: Path to the file to check.
426
+
427
+ Returns:
428
+ True if permissions are secure (600 or better), False otherwise.
429
+ """
430
+ if _is_windows():
431
+ # Windows uses ACLs, assume OK
432
+ return True
433
+
434
+ try:
435
+ mode = path.stat().st_mode
436
+ # Check if group or world readable/writable
437
+ insecure_bits = (
438
+ stat.S_IRGRP # Group read
439
+ | stat.S_IWGRP # Group write
440
+ | stat.S_IROTH # World read
441
+ | stat.S_IWOTH # World write
442
+ )
443
+ return (mode & insecure_bits) == 0
444
+ except OSError:
445
+ return True # Can't check, assume OK
446
+
447
+
448
+ class CredentialStore:
449
+ """Secure credential storage with chmod 600 protection.
450
+
451
+ Manages API credentials for RAXE telemetry with secure storage
452
+ and support for temporary key generation.
453
+
454
+ Location: ~/.raxe/credentials.json (default)
455
+
456
+ Example:
457
+ >>> store = CredentialStore()
458
+ >>> creds = store.get_or_create()
459
+ >>> print(f"Using key type: {creds.key_type}")
460
+ Using key type: temporary
461
+
462
+ >>> # Upgrade to permanent key
463
+ >>> creds = store.upgrade_key("raxe_live_abc123...", "live")
464
+ >>> print(f"Upgraded to: {creds.key_type}")
465
+ Upgraded to: live
466
+ """
467
+
468
+ def __init__(self, credential_path: Path | None = None) -> None:
469
+ """Initialize credential store.
470
+
471
+ Args:
472
+ credential_path: Custom path to credentials file.
473
+ Defaults to ~/.raxe/credentials.json
474
+ """
475
+ self._credential_path = credential_path or DEFAULT_CREDENTIAL_FILE
476
+ self._cached_credentials: Credentials | None = None
477
+
478
+ @property
479
+ def credential_path(self) -> Path:
480
+ """Get the credential file path.
481
+
482
+ Returns:
483
+ Path to the credentials file.
484
+ """
485
+ return self._credential_path
486
+
487
+ def load(self) -> Credentials | None:
488
+ """Load credentials from file.
489
+
490
+ Returns:
491
+ Credentials if file exists and is valid, None otherwise.
492
+
493
+ Raises:
494
+ CredentialError: If file exists but cannot be parsed.
495
+ """
496
+ if not self._credential_path.exists():
497
+ logger.debug("Credential file does not exist")
498
+ return None
499
+
500
+ # Check permissions and warn if insecure
501
+ if not _check_permissions(self._credential_path):
502
+ logger.warning(
503
+ "Credential file has insecure permissions. Consider running: chmod 600 %s",
504
+ self._credential_path,
505
+ )
506
+
507
+ try:
508
+ with open(self._credential_path, encoding="utf-8") as f:
509
+ data = json.load(f)
510
+
511
+ credentials = Credentials.from_dict(data)
512
+
513
+ # Validate key format
514
+ try:
515
+ validate_key_format(credentials.api_key)
516
+ except InvalidKeyFormatError as e:
517
+ raise CredentialError(f"Invalid stored API key: {e}") from e
518
+
519
+ # Log without exposing key value
520
+ logger.debug(
521
+ "Loaded credentials: type=%s, installation=%s",
522
+ credentials.key_type,
523
+ credentials.installation_id,
524
+ )
525
+
526
+ self._cached_credentials = credentials
527
+ return credentials
528
+
529
+ except json.JSONDecodeError as e:
530
+ raise CredentialError(f"Invalid JSON in credential file: {e}") from e
531
+ except ValueError as e:
532
+ raise CredentialError(f"Invalid credential format: {e}") from e
533
+
534
+ def save(self, credentials: Credentials) -> None:
535
+ """Save credentials with chmod 600.
536
+
537
+ Args:
538
+ credentials: Credentials to save.
539
+
540
+ Raises:
541
+ CredentialError: If file cannot be written.
542
+ """
543
+ # Validate key format before saving
544
+ try:
545
+ validate_key_format(credentials.api_key)
546
+ except InvalidKeyFormatError as e:
547
+ raise CredentialError(f"Cannot save invalid API key: {e}") from e
548
+
549
+ # Ensure parent directory exists
550
+ self._credential_path.parent.mkdir(parents=True, exist_ok=True)
551
+
552
+ try:
553
+ # Write to file
554
+ with open(self._credential_path, "w", encoding="utf-8") as f:
555
+ json.dump(credentials.to_dict(), f, indent=2)
556
+
557
+ # Set secure permissions
558
+ _set_secure_permissions(self._credential_path)
559
+
560
+ # Log without exposing key value
561
+ logger.info(
562
+ "Saved credentials: type=%s, installation=%s",
563
+ credentials.key_type,
564
+ credentials.installation_id,
565
+ )
566
+
567
+ self._cached_credentials = credentials
568
+
569
+ except OSError as e:
570
+ raise CredentialError(f"Failed to write credential file: {e}") from e
571
+
572
+ def generate_temp_credentials(self) -> Credentials:
573
+ """Generate temporary credentials for zero-friction onboarding.
574
+
575
+ Creates new temporary credentials with:
576
+ - installation_id: inst_{uuid4_hex[:16]}
577
+ - temp key: raxe_temp_{secrets.token_hex(16)}
578
+ - expiry: now + 14 days
579
+
580
+ Returns:
581
+ Newly generated temporary credentials.
582
+ """
583
+ now = _get_utc_now_iso()
584
+ expiry = datetime.now(timezone.utc)
585
+ expiry = expiry.replace(day=expiry.day) # Keep same time
586
+
587
+ # Calculate expiry (14 days from now)
588
+ from datetime import timedelta
589
+
590
+ expiry_dt = datetime.now(timezone.utc) + timedelta(days=TEMP_KEY_EXPIRY_DAYS)
591
+ expires_at = expiry_dt.strftime("%Y-%m-%dT%H:%M:%SZ")
592
+
593
+ credentials = Credentials(
594
+ api_key=_generate_temp_key(),
595
+ key_type="temporary",
596
+ installation_id=_generate_installation_id(),
597
+ created_at=now,
598
+ expires_at=expires_at,
599
+ first_seen_at=None,
600
+ )
601
+
602
+ # Log without exposing key value
603
+ logger.info(
604
+ "Generated temporary credentials: installation=%s, expires=%s",
605
+ credentials.installation_id,
606
+ credentials.expires_at,
607
+ )
608
+
609
+ return credentials
610
+
611
+ def upgrade_key(
612
+ self,
613
+ new_api_key: str,
614
+ key_type: Literal["live", "test"],
615
+ ) -> Credentials:
616
+ """Upgrade from temporary to permanent key.
617
+
618
+ Preserves installation_id from existing credentials if available.
619
+
620
+ Args:
621
+ new_api_key: The new permanent API key.
622
+ key_type: Type of the new key (live or test).
623
+
624
+ Returns:
625
+ Updated credentials with the new key.
626
+
627
+ Raises:
628
+ InvalidKeyFormatError: If the new key format is invalid.
629
+ CredentialError: If key_type doesn't match the new key prefix.
630
+
631
+ Note:
632
+ For telemetry event creation with key IDs, use upgrade_key_with_info()
633
+ instead, which returns KeyUpgradeInfo containing both old and new key IDs.
634
+ """
635
+ upgrade_info = self.upgrade_key_with_info(new_api_key, key_type)
636
+ return upgrade_info.new_credentials
637
+
638
+ def upgrade_key_with_info(
639
+ self,
640
+ new_api_key: str,
641
+ key_type: Literal["live", "test"],
642
+ ) -> KeyUpgradeInfo:
643
+ """Upgrade from temporary to permanent key with full upgrade information.
644
+
645
+ Preserves installation_id from existing credentials if available.
646
+ Returns KeyUpgradeInfo containing both old and new key IDs for
647
+ telemetry event creation.
648
+
649
+ Args:
650
+ new_api_key: The new permanent API key.
651
+ key_type: Type of the new key (live or test).
652
+
653
+ Returns:
654
+ KeyUpgradeInfo with old/new key IDs and the new credentials.
655
+
656
+ Raises:
657
+ InvalidKeyFormatError: If the new key format is invalid.
658
+ CredentialError: If key_type doesn't match the new key prefix.
659
+
660
+ Example:
661
+ >>> store = CredentialStore()
662
+ >>> info = store.upgrade_key_with_info("raxe_live_abc...", "live")
663
+ >>> event = create_key_upgrade_event(
664
+ ... previous_key_type="temp",
665
+ ... new_key_type="community",
666
+ ... previous_key_id=info.previous_key_id,
667
+ ... new_key_id=info.new_key_id,
668
+ ... days_on_previous=info.days_on_previous,
669
+ ... )
670
+ """
671
+ # Validate key format
672
+ detected_type = validate_key_format(new_api_key)
673
+
674
+ # Check for temporary key first (cannot upgrade to temp)
675
+ if detected_type == "temporary":
676
+ raise CredentialError("Cannot upgrade to a temporary key")
677
+
678
+ if detected_type != key_type:
679
+ raise CredentialError(
680
+ f"Key type mismatch: provided '{key_type}' but key is '{detected_type}'"
681
+ )
682
+
683
+ # Load existing credentials to preserve installation_id and capture old key info
684
+ existing = self.load()
685
+ installation_id = existing.installation_id if existing else _generate_installation_id()
686
+
687
+ # Capture old key information for telemetry event
688
+ previous_key_id: str | None = None
689
+ previous_key_type: str | None = None
690
+ days_on_previous: int | None = None
691
+
692
+ if existing:
693
+ previous_key_id = compute_key_id(existing.api_key)
694
+ previous_key_type = existing.key_type
695
+ # Calculate days on previous key
696
+ try:
697
+ created = datetime.fromisoformat(
698
+ existing.created_at.replace("Z", "+00:00")
699
+ )
700
+ now = datetime.now(timezone.utc)
701
+ days_on_previous = (now - created).days
702
+ except (ValueError, TypeError):
703
+ days_on_previous = None
704
+
705
+ # Determine tier based on key type (live keys default to community tier)
706
+ tier = "community" if key_type == "live" else "test"
707
+
708
+ credentials = Credentials(
709
+ api_key=new_api_key,
710
+ key_type=key_type,
711
+ installation_id=installation_id,
712
+ created_at=_get_utc_now_iso(),
713
+ expires_at=None, # Permanent keys don't expire
714
+ first_seen_at=None,
715
+ tier=tier,
716
+ )
717
+
718
+ # Save the upgraded credentials
719
+ self.save(credentials)
720
+
721
+ # Compute new key ID
722
+ new_key_id = compute_key_id(new_api_key)
723
+
724
+ logger.info(
725
+ "Upgraded to permanent key: type=%s, installation=%s",
726
+ key_type,
727
+ installation_id,
728
+ )
729
+
730
+ return KeyUpgradeInfo(
731
+ previous_key_id=previous_key_id,
732
+ new_key_id=new_key_id,
733
+ previous_key_type=previous_key_type,
734
+ new_key_type=key_type,
735
+ days_on_previous=days_on_previous,
736
+ new_credentials=credentials,
737
+ )
738
+
739
+ def get_or_create(self, *, raise_on_expired: bool = True) -> Credentials:
740
+ """Load existing or generate new temp credentials.
741
+
742
+ Priority chain:
743
+ 1. RAXE_API_KEY environment variable (highest - explicit override)
744
+ 2. ~/.raxe/credentials.json file
745
+ 3. Generate new temp credentials (last resort)
746
+
747
+ Args:
748
+ raise_on_expired: If True, raises CredentialExpiredError when
749
+ credentials are expired. If False, returns expired credentials
750
+ for caller to handle. Defaults to True.
751
+
752
+ Returns:
753
+ Existing credentials if valid, or newly generated temp credentials.
754
+
755
+ Raises:
756
+ CredentialExpiredError: If credentials are expired and raise_on_expired is True.
757
+ """
758
+ # Priority 1: Check RAXE_API_KEY environment variable first
759
+ env_api_key = os.environ.get("RAXE_API_KEY", "").strip()
760
+ if env_api_key:
761
+ try:
762
+ key_type = validate_key_format(env_api_key)
763
+ # Create credentials from env var (don't persist to file)
764
+ # Try to preserve installation_id from existing file if available
765
+ existing = self.load()
766
+ installation_id = (
767
+ existing.installation_id if existing else _generate_installation_id()
768
+ )
769
+ return Credentials(
770
+ api_key=env_api_key,
771
+ key_type=key_type,
772
+ installation_id=installation_id,
773
+ created_at=_get_utc_now_iso(),
774
+ expires_at=None, # Env var keys don't have local expiry tracking
775
+ first_seen_at=None,
776
+ )
777
+ except InvalidKeyFormatError:
778
+ logger.warning(
779
+ "Invalid RAXE_API_KEY format in environment, falling back to file"
780
+ )
781
+
782
+ # Priority 2: Try to load from credentials.json file
783
+ try:
784
+ credentials = self.load()
785
+ if credentials is not None:
786
+ # Check if expired
787
+ if credentials.is_expired():
788
+ days_expired = credentials.days_since_expiry() or 0
789
+ console_url = CredentialExpiredError.get_default_console_url()
790
+
791
+ # Build helpful error message
792
+ if days_expired == 0:
793
+ expiry_text = "today"
794
+ elif days_expired == 1:
795
+ expiry_text = "1 day ago"
796
+ else:
797
+ expiry_text = f"{days_expired} days ago"
798
+
799
+ message = (
800
+ f"Your temporary API key expired {expiry_text}. "
801
+ f"Get a permanent key at: {console_url}\n"
802
+ "Or run: raxe auth login"
803
+ )
804
+
805
+ logger.warning(
806
+ "credentials_expired",
807
+ extra={
808
+ "days_expired": days_expired,
809
+ "key_type": credentials.key_type,
810
+ "installation_id": credentials.installation_id,
811
+ },
812
+ )
813
+
814
+ if raise_on_expired:
815
+ raise CredentialExpiredError(
816
+ message,
817
+ console_url=console_url,
818
+ days_expired=days_expired,
819
+ )
820
+ # Return expired credentials if caller wants to handle it
821
+ return credentials
822
+ except CredentialError as e:
823
+ # Re-raise CredentialExpiredError, catch other CredentialErrors
824
+ if isinstance(e, CredentialExpiredError):
825
+ raise
826
+ logger.warning("Failed to load credentials: %s", e)
827
+
828
+ # Generate new temp credentials
829
+ credentials = self.generate_temp_credentials()
830
+
831
+ # Save them
832
+ self.save(credentials)
833
+
834
+ return credentials
835
+
836
+ def delete(self) -> bool:
837
+ """Delete credentials file.
838
+
839
+ Returns:
840
+ True if file was deleted, False if it didn't exist.
841
+ """
842
+ if not self._credential_path.exists():
843
+ logger.debug("Credential file does not exist, nothing to delete")
844
+ return False
845
+
846
+ try:
847
+ self._credential_path.unlink()
848
+ self._cached_credentials = None
849
+ logger.info("Deleted credential file")
850
+ return True
851
+ except OSError as e:
852
+ logger.error("Failed to delete credential file: %s", e)
853
+ return False
854
+
855
+ def update_first_seen(self, first_seen_at: str) -> Credentials | None:
856
+ """Update the first_seen_at timestamp from server.
857
+
858
+ Args:
859
+ first_seen_at: ISO 8601 timestamp from server health check.
860
+
861
+ Returns:
862
+ Updated credentials, or None if no credentials loaded.
863
+ """
864
+ credentials = self.load()
865
+ if credentials is None:
866
+ return None
867
+
868
+ if credentials.first_seen_at is not None:
869
+ # Already set, don't overwrite
870
+ return credentials
871
+
872
+ # Create updated credentials (preserve all fields)
873
+ updated = Credentials(
874
+ api_key=credentials.api_key,
875
+ key_type=credentials.key_type,
876
+ installation_id=credentials.installation_id,
877
+ created_at=credentials.created_at,
878
+ expires_at=credentials.expires_at,
879
+ first_seen_at=first_seen_at,
880
+ can_disable_telemetry=credentials.can_disable_telemetry,
881
+ offline_mode=credentials.offline_mode,
882
+ tier=credentials.tier,
883
+ last_health_check=credentials.last_health_check,
884
+ )
885
+
886
+ self.save(updated)
887
+ return updated
888
+
889
+ def update_from_health(self, health_response: dict) -> Credentials | None:
890
+ """Update cached permissions from /v1/health response.
891
+
892
+ This method updates the server-side permission cache from a health
893
+ check response. It should be called after a successful health check
894
+ to keep the local cache in sync with server-side permissions.
895
+
896
+ Args:
897
+ health_response: Dictionary from /v1/health response containing:
898
+ - can_disable_telemetry: bool
899
+ - offline_mode: bool (optional)
900
+ - tier: str (temporary, community, pro, enterprise)
901
+ - server_time: str (ISO 8601)
902
+
903
+ Returns:
904
+ Updated credentials, or None if no credentials loaded.
905
+
906
+ Example:
907
+ >>> store = CredentialStore()
908
+ >>> health = shipper.check_health()
909
+ >>> store.update_from_health({
910
+ ... "can_disable_telemetry": True,
911
+ ... "tier": "pro",
912
+ ... "server_time": "2025-01-26T12:00:00Z"
913
+ ... })
914
+ """
915
+ credentials = self.load()
916
+ if credentials is None:
917
+ return None
918
+
919
+ # Extract permissions from health response
920
+ can_disable = health_response.get("can_disable_telemetry", False)
921
+ offline_mode = health_response.get("offline_mode", False)
922
+ tier = health_response.get("tier", credentials.tier)
923
+ server_time = health_response.get("server_time", _get_utc_now_iso())
924
+
925
+ # Create updated credentials with new permissions
926
+ updated = Credentials(
927
+ api_key=credentials.api_key,
928
+ key_type=credentials.key_type,
929
+ installation_id=credentials.installation_id,
930
+ created_at=credentials.created_at,
931
+ expires_at=credentials.expires_at,
932
+ first_seen_at=credentials.first_seen_at,
933
+ can_disable_telemetry=can_disable,
934
+ offline_mode=offline_mode,
935
+ tier=tier,
936
+ last_health_check=server_time,
937
+ )
938
+
939
+ self.save(updated)
940
+
941
+ logger.debug(
942
+ "Updated credentials from health check: tier=%s, can_disable_telemetry=%s",
943
+ tier,
944
+ can_disable,
945
+ )
946
+
947
+ return updated