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.
- raxe/__init__.py +101 -0
- raxe/application/__init__.py +48 -0
- raxe/application/ab_testing.py +170 -0
- raxe/application/analytics/__init__.py +30 -0
- raxe/application/analytics/achievement_service.py +444 -0
- raxe/application/analytics/repositories.py +172 -0
- raxe/application/analytics/retention_service.py +267 -0
- raxe/application/analytics/statistics_service.py +419 -0
- raxe/application/analytics/streak_service.py +283 -0
- raxe/application/apply_policy.py +291 -0
- raxe/application/eager_l2.py +503 -0
- raxe/application/preloader.py +353 -0
- raxe/application/scan_merger.py +321 -0
- raxe/application/scan_pipeline.py +1059 -0
- raxe/application/scan_pipeline_async.py +403 -0
- raxe/application/session_tracker.py +458 -0
- raxe/application/telemetry_manager.py +357 -0
- raxe/application/telemetry_orchestrator.py +1210 -0
- raxe/async_sdk/__init__.py +34 -0
- raxe/async_sdk/cache.py +286 -0
- raxe/async_sdk/client.py +556 -0
- raxe/async_sdk/wrappers/__init__.py +23 -0
- raxe/async_sdk/wrappers/openai.py +238 -0
- raxe/cli/__init__.py +21 -0
- raxe/cli/auth.py +1047 -0
- raxe/cli/branding.py +235 -0
- raxe/cli/config.py +334 -0
- raxe/cli/custom_rules.py +458 -0
- raxe/cli/doctor.py +686 -0
- raxe/cli/error_handler.py +665 -0
- raxe/cli/event.py +648 -0
- raxe/cli/exit_codes.py +57 -0
- raxe/cli/expiry_warning.py +302 -0
- raxe/cli/export.py +183 -0
- raxe/cli/history.py +247 -0
- raxe/cli/l2_formatter.py +872 -0
- raxe/cli/main.py +1137 -0
- raxe/cli/models.py +590 -0
- raxe/cli/output.py +403 -0
- raxe/cli/privacy.py +84 -0
- raxe/cli/profiler.py +262 -0
- raxe/cli/progress.py +379 -0
- raxe/cli/progress_context.py +101 -0
- raxe/cli/repl.py +394 -0
- raxe/cli/rules.py +542 -0
- raxe/cli/setup_wizard.py +721 -0
- raxe/cli/stats.py +292 -0
- raxe/cli/suppress.py +501 -0
- raxe/cli/telemetry.py +1384 -0
- raxe/cli/test.py +130 -0
- raxe/cli/tune.py +315 -0
- raxe/cli/validate.py +218 -0
- raxe/domain/__init__.py +30 -0
- raxe/domain/analytics/__init__.py +97 -0
- raxe/domain/analytics/achievements.py +306 -0
- raxe/domain/analytics/models.py +120 -0
- raxe/domain/analytics/retention.py +168 -0
- raxe/domain/analytics/statistics.py +207 -0
- raxe/domain/analytics/streaks.py +173 -0
- raxe/domain/engine/__init__.py +15 -0
- raxe/domain/engine/executor.py +396 -0
- raxe/domain/engine/matcher.py +212 -0
- raxe/domain/inline_suppression.py +176 -0
- raxe/domain/ml/__init__.py +133 -0
- raxe/domain/ml/embedding_cache.py +309 -0
- raxe/domain/ml/gemma_detector.py +921 -0
- raxe/domain/ml/gemma_models.py +346 -0
- raxe/domain/ml/l2_config.py +428 -0
- raxe/domain/ml/l2_output_schema.py +443 -0
- raxe/domain/ml/manifest_loader.py +309 -0
- raxe/domain/ml/manifest_schema.py +345 -0
- raxe/domain/ml/model_metadata.py +263 -0
- raxe/domain/ml/model_registry.py +786 -0
- raxe/domain/ml/protocol.py +282 -0
- raxe/domain/ml/scoring_models.py +419 -0
- raxe/domain/ml/stub_detector.py +397 -0
- raxe/domain/ml/threat_scorer.py +757 -0
- raxe/domain/ml/tokenizer_registry.py +372 -0
- raxe/domain/ml/voting/__init__.py +89 -0
- raxe/domain/ml/voting/config.py +595 -0
- raxe/domain/ml/voting/engine.py +465 -0
- raxe/domain/ml/voting/head_voters.py +378 -0
- raxe/domain/ml/voting/models.py +222 -0
- raxe/domain/models.py +82 -0
- raxe/domain/packs/__init__.py +17 -0
- raxe/domain/packs/models.py +304 -0
- raxe/domain/policies/__init__.py +20 -0
- raxe/domain/policies/evaluator.py +212 -0
- raxe/domain/policies/models.py +223 -0
- raxe/domain/rules/__init__.py +32 -0
- raxe/domain/rules/custom.py +286 -0
- raxe/domain/rules/models.py +273 -0
- raxe/domain/rules/schema.py +166 -0
- raxe/domain/rules/validator.py +556 -0
- raxe/domain/suppression.py +801 -0
- raxe/domain/suppression_factory.py +174 -0
- raxe/domain/telemetry/__init__.py +116 -0
- raxe/domain/telemetry/backpressure.py +424 -0
- raxe/domain/telemetry/event_creator.py +362 -0
- raxe/domain/telemetry/events.py +1282 -0
- raxe/domain/telemetry/priority.py +263 -0
- raxe/domain/telemetry/scan_telemetry_builder.py +670 -0
- raxe/infrastructure/__init__.py +25 -0
- raxe/infrastructure/analytics/__init__.py +18 -0
- raxe/infrastructure/analytics/aggregator.py +484 -0
- raxe/infrastructure/analytics/aggregator_optimized.py +184 -0
- raxe/infrastructure/analytics/engine.py +748 -0
- raxe/infrastructure/analytics/repository.py +409 -0
- raxe/infrastructure/analytics/streaks.py +467 -0
- raxe/infrastructure/analytics/views.py +178 -0
- raxe/infrastructure/cloud/__init__.py +9 -0
- raxe/infrastructure/config/__init__.py +56 -0
- raxe/infrastructure/config/endpoints.py +641 -0
- raxe/infrastructure/config/scan_config.py +352 -0
- raxe/infrastructure/config/yaml_config.py +459 -0
- raxe/infrastructure/database/__init__.py +10 -0
- raxe/infrastructure/database/connection.py +200 -0
- raxe/infrastructure/database/models.py +325 -0
- raxe/infrastructure/database/scan_history.py +764 -0
- raxe/infrastructure/ml/__init__.py +0 -0
- raxe/infrastructure/ml/download_progress.py +438 -0
- raxe/infrastructure/ml/model_downloader.py +457 -0
- raxe/infrastructure/models/__init__.py +16 -0
- raxe/infrastructure/models/discovery.py +461 -0
- raxe/infrastructure/packs/__init__.py +13 -0
- raxe/infrastructure/packs/loader.py +407 -0
- raxe/infrastructure/packs/registry.py +381 -0
- raxe/infrastructure/policies/__init__.py +16 -0
- raxe/infrastructure/policies/api_client.py +256 -0
- raxe/infrastructure/policies/validator.py +227 -0
- raxe/infrastructure/policies/yaml_loader.py +250 -0
- raxe/infrastructure/rules/__init__.py +18 -0
- raxe/infrastructure/rules/custom_loader.py +224 -0
- raxe/infrastructure/rules/versioning.py +222 -0
- raxe/infrastructure/rules/yaml_loader.py +286 -0
- raxe/infrastructure/security/__init__.py +31 -0
- raxe/infrastructure/security/auth.py +145 -0
- raxe/infrastructure/security/policy_validator.py +124 -0
- raxe/infrastructure/security/signatures.py +171 -0
- raxe/infrastructure/suppression/__init__.py +36 -0
- raxe/infrastructure/suppression/composite_repository.py +154 -0
- raxe/infrastructure/suppression/sqlite_repository.py +231 -0
- raxe/infrastructure/suppression/yaml_composite_repository.py +156 -0
- raxe/infrastructure/suppression/yaml_repository.py +510 -0
- raxe/infrastructure/telemetry/__init__.py +79 -0
- raxe/infrastructure/telemetry/acquisition.py +179 -0
- raxe/infrastructure/telemetry/config.py +254 -0
- raxe/infrastructure/telemetry/credential_store.py +947 -0
- raxe/infrastructure/telemetry/dual_queue.py +1123 -0
- raxe/infrastructure/telemetry/flush_helper.py +343 -0
- raxe/infrastructure/telemetry/flush_scheduler.py +776 -0
- raxe/infrastructure/telemetry/health_client.py +394 -0
- raxe/infrastructure/telemetry/hook.py +347 -0
- raxe/infrastructure/telemetry/queue.py +520 -0
- raxe/infrastructure/telemetry/sender.py +476 -0
- raxe/infrastructure/tracking/__init__.py +13 -0
- raxe/infrastructure/tracking/usage.py +389 -0
- raxe/integrations/__init__.py +55 -0
- raxe/integrations/availability.py +143 -0
- raxe/integrations/registry.py +122 -0
- raxe/integrations/utils.py +135 -0
- raxe/mcp/__init__.py +62 -0
- raxe/mcp/cli.py +97 -0
- raxe/mcp/server.py +409 -0
- raxe/monitoring/__init__.py +51 -0
- raxe/monitoring/metrics.py +372 -0
- raxe/monitoring/profiler.py +388 -0
- raxe/monitoring/server.py +136 -0
- raxe/packs/core/v1.0.0/pack.yaml +1394 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-001@1.0.0.yaml +49 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-006@1.0.0.yaml +48 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-014@1.0.0.yaml +54 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-017@1.0.0.yaml +52 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-022@1.0.0.yaml +67 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-023@1.0.0.yaml +91 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-024@1.0.0.yaml +80 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-025@1.0.0.yaml +81 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-026@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-027@1.0.0.yaml +77 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-028@1.0.0.yaml +52 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-029@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-030@1.0.0.yaml +55 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-033@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-034@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-035@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-046@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-047@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-048@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-049@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-050@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-068@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-078@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-2001@1.0.0.yaml +35 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-2004@1.0.0.yaml +39 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-201@1.0.0.yaml +43 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-202@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-203@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-3007@1.0.0.yaml +44 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-3016@1.0.0.yaml +44 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-3026@1.0.0.yaml +39 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-3027@1.0.0.yaml +64 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-3028@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-3029@1.0.0.yaml +53 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-3030@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-3031@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-3032@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-3033@1.0.0.yaml +56 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-3034@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-79@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-80@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-81@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-82@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-83@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-84@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-85@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-86@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-87@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-88@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-89@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-90@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-91@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-92@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-93@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-94@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-95@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-96@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-97@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-98@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-001@1.0.0.yaml +48 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-007@1.0.0.yaml +48 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-015@1.0.0.yaml +56 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-016@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-017@1.0.0.yaml +57 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-021@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-022@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-023@1.0.0.yaml +78 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-024@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-025@1.0.0.yaml +93 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-026@1.0.0.yaml +81 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-027@1.0.0.yaml +82 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-028@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-033@1.0.0.yaml +48 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-036@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-037@1.0.0.yaml +44 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-052@1.0.0.yaml +43 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-054@1.0.0.yaml +44 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-056@1.0.0.yaml +43 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-065@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-075@1.0.0.yaml +45 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-079@1.0.0.yaml +44 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-1080@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-1090@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-1104@1.0.0.yaml +44 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-1105@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-1112@1.0.0.yaml +44 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-201@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-202@1.0.0.yaml +42 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-203@1.0.0.yaml +43 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-204@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-205@1.0.0.yaml +44 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-206@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-207@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-208@1.0.0.yaml +42 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-209@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-210@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-211@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-212@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-213@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-214@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-215@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-216@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-217@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-218@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-219@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-220@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-221@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-222@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-223@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-224@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-225@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-226@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-227@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-228@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-229@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-230@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-231@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-232@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-233@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-234@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-235@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-236@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-237@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-238@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-001@1.0.0.yaml +48 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-013@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-019@1.0.0.yaml +43 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-020@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-024@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-029@1.0.0.yaml +44 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-038@1.0.0.yaml +44 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-044@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-067@1.0.0.yaml +42 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-069@1.0.0.yaml +42 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-100@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-101@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-102@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-103@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-104@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-105@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-106@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-107@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-108@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-109@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-110@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-111@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-112@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-113@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-114@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-115@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-116@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-117@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-118@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-119@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-120@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-201@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-202@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-203@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-3004@1.0.0.yaml +40 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-3006@1.0.0.yaml +40 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-3011@1.0.0.yaml +40 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-5016@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-6001@1.0.0.yaml +53 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-6002@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-70@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-71@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-72@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-73@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-74@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-75@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-76@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-77@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-78@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-79@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-80@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-81@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-82@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-83@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-84@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-85@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-86@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-87@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-88@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-89@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-90@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-91@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-92@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-93@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-94@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-95@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-96@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-97@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-98@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-99@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-001@1.0.0.yaml +73 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-002@1.0.0.yaml +71 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-003@1.0.0.yaml +65 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-004@1.0.0.yaml +73 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-101@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-102@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-103@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-104@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-105@1.0.0.yaml +48 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-106@1.0.0.yaml +40 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-107@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-108@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-109@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-110@1.0.0.yaml +56 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-111@1.0.0.yaml +49 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-112@1.0.0.yaml +53 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-113@1.0.0.yaml +52 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-114@1.0.0.yaml +52 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-115@1.0.0.yaml +52 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-116@1.0.0.yaml +53 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-117@1.0.0.yaml +54 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-118@1.0.0.yaml +52 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-119@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-120@1.0.0.yaml +52 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-121@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-122@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-123@1.0.0.yaml +52 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-124@1.0.0.yaml +53 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-125@1.0.0.yaml +53 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-126@1.0.0.yaml +53 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-127@1.0.0.yaml +53 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-128@1.0.0.yaml +53 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-129@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-130@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-131@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-132@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-133@1.0.0.yaml +53 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-134@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-135@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-136@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-137@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-138@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-139@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-140@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-141@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-142@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-143@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-144@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-145@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-146@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-147@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-148@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-149@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-150@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-151@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-152@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-153@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-154@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-155@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-156@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-157@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-158@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-159@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-160@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-161@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-001@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-009@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-020@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-021@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-022@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-028@1.0.0.yaml +43 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-033@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-034@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-036@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-039@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-056@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-066@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-076@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-098@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-103@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-104@1.0.0.yaml +52 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-105@1.0.0.yaml +56 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-110@1.0.0.yaml +56 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-111@1.0.0.yaml +57 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-112@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-113@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-114@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-115@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-116@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-117@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-118@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-119@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-120@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-121@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-122@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-123@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-124@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-125@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-126@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-127@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-128@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-129@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-130@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-131@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-132@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-133@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-134@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-135@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-136@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-137@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-138@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-139@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-140@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-141@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-142@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-143@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-144@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-145@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-146@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-147@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-148@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-149@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-150@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-151@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-152@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-153@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-154@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-155@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-156@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-157@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-158@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-159@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-160@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-161@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-162@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-201@1.0.0.yaml +40 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-202@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-203@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-204@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-205@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-206@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-207@1.0.0.yaml +49 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-001@1.0.0.yaml +48 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-009@1.0.0.yaml +48 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-012@1.0.0.yaml +48 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-017@1.0.0.yaml +48 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-022@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-025@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-027@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-028@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-034@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-037@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-040@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-041@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-044@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-050@1.0.0.yaml +57 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-051@1.0.0.yaml +53 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-052@1.0.0.yaml +52 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-053@1.0.0.yaml +56 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-054@1.0.0.yaml +53 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-055@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-056@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-058@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-2015@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-2025@1.0.0.yaml +35 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-2026@1.0.0.yaml +39 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-2035@1.0.0.yaml +39 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-2037@1.0.0.yaml +39 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-2042@1.0.0.yaml +39 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3001@1.0.0.yaml +39 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3002@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3003@1.0.0.yaml +36 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3004@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3005@1.0.0.yaml +39 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3006@1.0.0.yaml +35 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3007@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3008@1.0.0.yaml +35 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3009@1.0.0.yaml +42 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3010@1.0.0.yaml +39 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3011@1.0.0.yaml +35 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3012@1.0.0.yaml +35 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3013@1.0.0.yaml +36 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3014@1.0.0.yaml +36 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3015@1.0.0.yaml +42 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3016@1.0.0.yaml +42 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3017@1.0.0.yaml +40 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3018@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3019@1.0.0.yaml +40 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3020@1.0.0.yaml +40 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3021@1.0.0.yaml +39 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3022@1.0.0.yaml +36 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3023@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3024@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3025@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3026@1.0.0.yaml +42 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3027@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3028@1.0.0.yaml +42 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3029@1.0.0.yaml +36 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3030@1.0.0.yaml +42 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3031@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3032@1.0.0.yaml +42 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3033@1.0.0.yaml +39 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3034@1.0.0.yaml +40 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3035@1.0.0.yaml +43 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3036@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3037@1.0.0.yaml +35 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3038@1.0.0.yaml +35 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3039@1.0.0.yaml +35 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3040@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3041@1.0.0.yaml +39 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3042@1.0.0.yaml +36 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3043@1.0.0.yaml +35 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3044@1.0.0.yaml +43 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3045@1.0.0.yaml +36 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3046@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3047@1.0.0.yaml +36 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3048@1.0.0.yaml +36 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3049@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3050@1.0.0.yaml +44 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3051@1.0.0.yaml +35 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3052@1.0.0.yaml +36 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3053@1.0.0.yaml +35 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3054@1.0.0.yaml +35 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3055@1.0.0.yaml +40 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3056@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3057@1.0.0.yaml +40 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3058@1.0.0.yaml +43 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3059@1.0.0.yaml +42 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3060@1.0.0.yaml +42 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3061@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3062@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3063@1.0.0.yaml +54 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3064@1.0.0.yaml +78 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3065@1.0.0.yaml +84 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3066@1.0.0.yaml +84 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3067@1.0.0.yaml +88 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3068@1.0.0.yaml +94 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3069@1.0.0.yaml +90 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3070@1.0.0.yaml +99 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3071@1.0.0.yaml +91 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3072@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3073@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3074@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3075@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3076@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3077@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3078@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3079@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3080@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3081@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3082@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3083@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3084@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3085@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/rag/rag-016@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/rag/rag-028@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/rag/rag-042@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/rag/rag-044@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/rag/rag-045@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/rag/rag-050@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/rag/rag-201@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/rag/rag-202@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/rag/rag-3001@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/rag/rag-3006@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/rag/rag-3009@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/rag/rag-3012@1.0.0.yaml +41 -0
- raxe/plugins/__init__.py +98 -0
- raxe/plugins/custom_rules.py +380 -0
- raxe/plugins/loader.py +389 -0
- raxe/plugins/manager.py +538 -0
- raxe/plugins/protocol.py +428 -0
- raxe/py.typed +0 -0
- raxe/sdk/__init__.py +77 -0
- raxe/sdk/agent_scanner.py +1918 -0
- raxe/sdk/client.py +1603 -0
- raxe/sdk/decorator.py +175 -0
- raxe/sdk/exceptions.py +859 -0
- raxe/sdk/integrations/__init__.py +277 -0
- raxe/sdk/integrations/agent_scanner.py +71 -0
- raxe/sdk/integrations/autogen.py +872 -0
- raxe/sdk/integrations/crewai.py +1368 -0
- raxe/sdk/integrations/dspy.py +845 -0
- raxe/sdk/integrations/extractors.py +363 -0
- raxe/sdk/integrations/huggingface.py +395 -0
- raxe/sdk/integrations/langchain.py +948 -0
- raxe/sdk/integrations/litellm.py +484 -0
- raxe/sdk/integrations/llamaindex.py +1049 -0
- raxe/sdk/integrations/portkey.py +831 -0
- raxe/sdk/suppression_context.py +215 -0
- raxe/sdk/wrappers/__init__.py +163 -0
- raxe/sdk/wrappers/anthropic.py +310 -0
- raxe/sdk/wrappers/openai.py +221 -0
- raxe/sdk/wrappers/vertexai.py +484 -0
- raxe/utils/__init__.py +12 -0
- raxe/utils/error_sanitizer.py +135 -0
- raxe/utils/logging.py +241 -0
- raxe/utils/performance.py +414 -0
- raxe/utils/profiler.py +339 -0
- raxe/utils/validators.py +170 -0
- raxe-0.4.6.dist-info/METADATA +471 -0
- raxe-0.4.6.dist-info/RECORD +668 -0
- raxe-0.4.6.dist-info/WHEEL +5 -0
- raxe-0.4.6.dist-info/entry_points.txt +2 -0
- raxe-0.4.6.dist-info/licenses/LICENSE +56 -0
- raxe-0.4.6.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,748 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Analytics engine for calculating metrics and retention.
|
|
3
|
+
|
|
4
|
+
This module provides analytics calculation for:
|
|
5
|
+
- Installation metrics
|
|
6
|
+
- Usage metrics
|
|
7
|
+
- Retention analysis
|
|
8
|
+
- Performance metrics
|
|
9
|
+
- Feature adoption
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import logging
|
|
13
|
+
from dataclasses import dataclass, field
|
|
14
|
+
from datetime import date, datetime, timedelta, timezone
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
from sqlalchemy import and_, case, create_engine, func
|
|
19
|
+
from sqlalchemy.orm import Session, sessionmaker
|
|
20
|
+
|
|
21
|
+
from raxe.infrastructure.database.models import Base, TelemetryEvent
|
|
22
|
+
from raxe.utils.validators import validate_date_range
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class InstallationMetrics:
|
|
29
|
+
"""Installation-related metrics."""
|
|
30
|
+
total_installations: int = 0
|
|
31
|
+
installations_by_os: dict[str, int] = field(default_factory=dict)
|
|
32
|
+
installations_by_python: dict[str, int] = field(default_factory=dict)
|
|
33
|
+
installations_by_source: dict[str, int] = field(default_factory=dict)
|
|
34
|
+
geographic_distribution: dict[str, int] = field(default_factory=dict)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass
|
|
38
|
+
class UsageMetrics:
|
|
39
|
+
"""Usage-related metrics."""
|
|
40
|
+
total_scans: int = 0
|
|
41
|
+
scans_per_user_p50: float = 0.0
|
|
42
|
+
scans_per_user_p95: float = 0.0
|
|
43
|
+
scans_per_user_p99: float = 0.0
|
|
44
|
+
threats_detected: int = 0
|
|
45
|
+
detection_rate: float = 0.0
|
|
46
|
+
avg_scans_per_day: float = 0.0
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass
|
|
50
|
+
class RetentionMetrics:
|
|
51
|
+
"""Retention-related metrics."""
|
|
52
|
+
dau: int = 0 # Daily Active Users
|
|
53
|
+
wau: int = 0 # Weekly Active Users
|
|
54
|
+
mau: int = 0 # Monthly Active Users
|
|
55
|
+
day1_retention: float = 0.0
|
|
56
|
+
day7_retention: float = 0.0
|
|
57
|
+
day30_retention: float = 0.0
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@dataclass
|
|
61
|
+
class PerformanceMetrics:
|
|
62
|
+
"""Performance-related metrics."""
|
|
63
|
+
p50_latency_ms: float = 0.0
|
|
64
|
+
p95_latency_ms: float = 0.0
|
|
65
|
+
p99_latency_ms: float = 0.0
|
|
66
|
+
avg_l1_latency_ms: float = 0.0
|
|
67
|
+
avg_l2_latency_ms: float = 0.0
|
|
68
|
+
avg_queue_depth: float = 0.0
|
|
69
|
+
error_rate: float = 0.0
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@dataclass
|
|
73
|
+
class UserStats:
|
|
74
|
+
"""Statistics for a specific user."""
|
|
75
|
+
installation_id: str
|
|
76
|
+
installation_date: datetime | None = None
|
|
77
|
+
time_to_first_scan_seconds: float | None = None
|
|
78
|
+
total_scans: int = 0
|
|
79
|
+
threats_detected: int = 0
|
|
80
|
+
detection_rate: float = 0.0
|
|
81
|
+
last_scan: datetime | None = None
|
|
82
|
+
current_streak: int = 0
|
|
83
|
+
longest_streak: int = 0
|
|
84
|
+
avg_scan_time_ms: float = 0.0
|
|
85
|
+
l1_detections: int = 0
|
|
86
|
+
l2_detections: int = 0
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class AnalyticsEngine:
|
|
90
|
+
"""
|
|
91
|
+
Analytics calculation engine.
|
|
92
|
+
|
|
93
|
+
Calculates metrics from telemetry events stored in local SQLite database.
|
|
94
|
+
All calculations happen locally - no cloud dependency.
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
def __init__(self, db_path: Path | None = None):
|
|
98
|
+
"""
|
|
99
|
+
Initialize analytics engine.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
db_path: Path to SQLite database (defaults to ~/.raxe/telemetry.db)
|
|
103
|
+
"""
|
|
104
|
+
if db_path is None:
|
|
105
|
+
db_path = Path.home() / ".raxe" / "telemetry.db"
|
|
106
|
+
|
|
107
|
+
self.db_path = db_path
|
|
108
|
+
self.db_path.parent.mkdir(parents=True, exist_ok=True)
|
|
109
|
+
|
|
110
|
+
# Create database connection
|
|
111
|
+
self.engine = create_engine(
|
|
112
|
+
f"sqlite:///{self.db_path}",
|
|
113
|
+
echo=False,
|
|
114
|
+
connect_args={"check_same_thread": False}
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
# Create tables if they don't exist
|
|
118
|
+
Base.metadata.create_all(self.engine)
|
|
119
|
+
|
|
120
|
+
# Create session factory
|
|
121
|
+
self.SessionLocal = sessionmaker(bind=self.engine)
|
|
122
|
+
|
|
123
|
+
logger.info(f"Analytics engine initialized with database: {self.db_path}")
|
|
124
|
+
|
|
125
|
+
def _get_session(self) -> Session:
|
|
126
|
+
"""Create a new database session."""
|
|
127
|
+
return self.SessionLocal()
|
|
128
|
+
|
|
129
|
+
def calculate_retention(
|
|
130
|
+
self,
|
|
131
|
+
cohort_date: date,
|
|
132
|
+
*,
|
|
133
|
+
session: Session | None = None
|
|
134
|
+
) -> dict[str, Any]:
|
|
135
|
+
"""
|
|
136
|
+
Calculate retention for installation cohort.
|
|
137
|
+
|
|
138
|
+
DEPRECATED: This method delegates to RetentionService for Clean Architecture.
|
|
139
|
+
Maintained for backward compatibility. Use RetentionService directly in new code.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
cohort_date: Date of installation cohort
|
|
143
|
+
session: Optional database session
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
Dictionary with retention metrics for the cohort
|
|
147
|
+
"""
|
|
148
|
+
# REFACTORED: Delegate to application service instead of containing business logic
|
|
149
|
+
from raxe.application.analytics import RetentionService
|
|
150
|
+
from raxe.infrastructure.analytics.repository import SQLiteAnalyticsRepository
|
|
151
|
+
|
|
152
|
+
repository = SQLiteAnalyticsRepository(self.db_path)
|
|
153
|
+
service = RetentionService(repository)
|
|
154
|
+
|
|
155
|
+
result = service.calculate_cohort_retention_metrics(cohort_date)
|
|
156
|
+
|
|
157
|
+
# Return in legacy format for backward compatibility
|
|
158
|
+
return {
|
|
159
|
+
"cohort_date": result["cohort_date"],
|
|
160
|
+
"cohort_size": result["cohort_size"],
|
|
161
|
+
"day_1": result["day1_retention_rate"],
|
|
162
|
+
"day_7": result["day7_retention_rate"],
|
|
163
|
+
"day_30": result["day30_retention_rate"]
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
def get_user_stats(
|
|
167
|
+
self,
|
|
168
|
+
installation_id: str,
|
|
169
|
+
*,
|
|
170
|
+
session: Session | None = None
|
|
171
|
+
) -> UserStats:
|
|
172
|
+
"""
|
|
173
|
+
Get statistics for specific user using database aggregation.
|
|
174
|
+
|
|
175
|
+
Performance optimization: Uses SQL aggregation instead of loading
|
|
176
|
+
all events into memory. This is critical for users with millions of events.
|
|
177
|
+
|
|
178
|
+
UPDATED: Now queries scan_history.db (primary) and falls back to telemetry.db
|
|
179
|
+
if no scan history found. This ensures stats work after SDK integration.
|
|
180
|
+
|
|
181
|
+
Args:
|
|
182
|
+
installation_id: User's installation ID (customer_id)
|
|
183
|
+
session: Optional database session
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
UserStats object with user-specific metrics
|
|
187
|
+
"""
|
|
188
|
+
# UPDATED: Query scan_history.db first (primary source after SDK integration)
|
|
189
|
+
# NOTE: scan_history.db is NOT per-installation, it's global for this machine
|
|
190
|
+
# So we ignore installation_id and just report all scans on this machine
|
|
191
|
+
from raxe.infrastructure.database.scan_history import ScanHistoryDB
|
|
192
|
+
from raxe.infrastructure.tracking.usage import UsageTracker
|
|
193
|
+
|
|
194
|
+
try:
|
|
195
|
+
# Try scan_history.db first (created by SDK scans)
|
|
196
|
+
scan_history = ScanHistoryDB()
|
|
197
|
+
usage_tracker = UsageTracker()
|
|
198
|
+
|
|
199
|
+
# Get statistics from scan_history.db (all scans on this machine)
|
|
200
|
+
stats = scan_history.get_statistics(days=365) # Last year
|
|
201
|
+
|
|
202
|
+
# Get installation info from usage tracker
|
|
203
|
+
usage_stats = usage_tracker.get_usage_stats()
|
|
204
|
+
install_info = usage_tracker.get_install_info()
|
|
205
|
+
|
|
206
|
+
total_scans = stats.get('total_scans', 0)
|
|
207
|
+
|
|
208
|
+
# IMPORTANT: Only use scan_history if we actually have data
|
|
209
|
+
# In test environments, scan_history.db might be empty or non-existent
|
|
210
|
+
if total_scans > 0:
|
|
211
|
+
# We have scan history data - use it (global machine stats)
|
|
212
|
+
scans_with_threats = stats.get('scans_with_threats', 0)
|
|
213
|
+
detection_rate = stats.get('threat_rate', 0.0) * 100
|
|
214
|
+
|
|
215
|
+
# Get L1/L2 breakdown from recent scans
|
|
216
|
+
recent_scans = scan_history.list_scans(limit=100)
|
|
217
|
+
l1_detections = sum(s.l1_detections for s in recent_scans)
|
|
218
|
+
l2_detections = sum(s.l2_detections for s in recent_scans)
|
|
219
|
+
|
|
220
|
+
# Get installation and first scan times
|
|
221
|
+
installation_date = None
|
|
222
|
+
time_to_first_scan = None
|
|
223
|
+
if install_info:
|
|
224
|
+
installed_at_str = install_info.installed_at
|
|
225
|
+
if installed_at_str:
|
|
226
|
+
installation_date = datetime.fromisoformat(installed_at_str)
|
|
227
|
+
|
|
228
|
+
# Get last scan time
|
|
229
|
+
last_scan = None
|
|
230
|
+
if usage_stats:
|
|
231
|
+
time_to_first_scan = usage_stats.time_to_first_scan_seconds
|
|
232
|
+
last_scan_str = usage_stats.last_scan_at
|
|
233
|
+
if last_scan_str:
|
|
234
|
+
last_scan = datetime.fromisoformat(last_scan_str)
|
|
235
|
+
|
|
236
|
+
# Calculate streaks from scan history
|
|
237
|
+
all_scans = scan_history.list_scans(limit=1000)
|
|
238
|
+
scan_dates = [s.timestamp.date() for s in all_scans]
|
|
239
|
+
current_streak, longest_streak = self._calculate_streaks_from_dates(scan_dates)
|
|
240
|
+
|
|
241
|
+
# Handle None values for avg_total_duration_ms
|
|
242
|
+
avg_duration = stats.get('avg_total_duration_ms') or 0.0
|
|
243
|
+
|
|
244
|
+
result = UserStats(
|
|
245
|
+
installation_id=installation_id,
|
|
246
|
+
installation_date=installation_date,
|
|
247
|
+
time_to_first_scan_seconds=time_to_first_scan,
|
|
248
|
+
total_scans=total_scans,
|
|
249
|
+
threats_detected=scans_with_threats,
|
|
250
|
+
detection_rate=round(detection_rate, 2),
|
|
251
|
+
last_scan=last_scan,
|
|
252
|
+
current_streak=current_streak,
|
|
253
|
+
longest_streak=longest_streak,
|
|
254
|
+
avg_scan_time_ms=round(avg_duration, 2),
|
|
255
|
+
l1_detections=l1_detections,
|
|
256
|
+
l2_detections=l2_detections
|
|
257
|
+
)
|
|
258
|
+
return result
|
|
259
|
+
except Exception as e:
|
|
260
|
+
# Fall back to telemetry.db if scan_history.db has issues
|
|
261
|
+
logger.debug(f"Scan history query failed, falling back to telemetry.db: {e}")
|
|
262
|
+
|
|
263
|
+
# FALLBACK: Query telemetry.db (legacy behavior)
|
|
264
|
+
close_session = session is None
|
|
265
|
+
if session is None:
|
|
266
|
+
session = self._get_session()
|
|
267
|
+
|
|
268
|
+
try:
|
|
269
|
+
# OPTIMIZED: Use database aggregation instead of loading all events
|
|
270
|
+
stats = session.query(
|
|
271
|
+
func.count(TelemetryEvent.id).label('total_scans'),
|
|
272
|
+
func.sum(
|
|
273
|
+
case((TelemetryEvent.detection_count > 0, 1), else_=0)
|
|
274
|
+
).label('threats_detected'),
|
|
275
|
+
func.avg(TelemetryEvent.total_latency_ms).label('avg_scan_time'),
|
|
276
|
+
func.max(TelemetryEvent.timestamp).label('last_scan'),
|
|
277
|
+
func.min(TelemetryEvent.timestamp).label('installation_date'),
|
|
278
|
+
func.sum(
|
|
279
|
+
case(
|
|
280
|
+
(and_(TelemetryEvent.l2_inference_ms.isnot(None), TelemetryEvent.l2_inference_ms > 0), 1),
|
|
281
|
+
else_=0
|
|
282
|
+
)
|
|
283
|
+
).label('l2_detections'),
|
|
284
|
+
).filter(
|
|
285
|
+
TelemetryEvent.customer_id == installation_id
|
|
286
|
+
).first()
|
|
287
|
+
|
|
288
|
+
if not stats or stats.total_scans == 0:
|
|
289
|
+
return UserStats(installation_id=installation_id)
|
|
290
|
+
|
|
291
|
+
# Calculate derived metrics
|
|
292
|
+
total_scans = stats.total_scans or 0
|
|
293
|
+
threats_detected = stats.threats_detected or 0
|
|
294
|
+
detection_rate = (threats_detected / total_scans * 100) if total_scans > 0 else 0.0
|
|
295
|
+
l2_detections = stats.l2_detections or 0
|
|
296
|
+
l1_detections = total_scans - l2_detections
|
|
297
|
+
|
|
298
|
+
# Calculate time to first scan
|
|
299
|
+
# Get the two earliest events for this calculation
|
|
300
|
+
time_to_first_scan = None
|
|
301
|
+
first_two_events = session.query(TelemetryEvent.timestamp).filter(
|
|
302
|
+
TelemetryEvent.customer_id == installation_id
|
|
303
|
+
).order_by(TelemetryEvent.timestamp).limit(2).all()
|
|
304
|
+
|
|
305
|
+
if len(first_two_events) == 2:
|
|
306
|
+
time_to_first_scan = (first_two_events[1][0] - first_two_events[0][0]).total_seconds()
|
|
307
|
+
|
|
308
|
+
# Calculate streaks (still needs scan dates, but we only query dates, not full events)
|
|
309
|
+
scan_dates = session.query(
|
|
310
|
+
func.date(TelemetryEvent.timestamp).label('scan_date')
|
|
311
|
+
).filter(
|
|
312
|
+
TelemetryEvent.customer_id == installation_id
|
|
313
|
+
).distinct().order_by('scan_date').all()
|
|
314
|
+
|
|
315
|
+
# Convert to date objects for streak calculation
|
|
316
|
+
unique_dates = [d[0] for d in scan_dates]
|
|
317
|
+
current_streak, longest_streak = self._calculate_streaks_from_dates(unique_dates)
|
|
318
|
+
|
|
319
|
+
return UserStats(
|
|
320
|
+
installation_id=installation_id,
|
|
321
|
+
installation_date=stats.installation_date,
|
|
322
|
+
time_to_first_scan_seconds=time_to_first_scan,
|
|
323
|
+
total_scans=total_scans,
|
|
324
|
+
threats_detected=threats_detected,
|
|
325
|
+
detection_rate=round(detection_rate, 2),
|
|
326
|
+
last_scan=stats.last_scan,
|
|
327
|
+
current_streak=current_streak,
|
|
328
|
+
longest_streak=longest_streak,
|
|
329
|
+
avg_scan_time_ms=round(float(stats.avg_scan_time or 0), 2),
|
|
330
|
+
l1_detections=l1_detections,
|
|
331
|
+
l2_detections=l2_detections
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
finally:
|
|
335
|
+
if close_session:
|
|
336
|
+
session.close()
|
|
337
|
+
|
|
338
|
+
def _calculate_streaks(self, events: list[TelemetryEvent]) -> tuple[int, int]:
|
|
339
|
+
"""
|
|
340
|
+
Calculate current and longest streaks from events.
|
|
341
|
+
|
|
342
|
+
DEPRECATED: Use _calculate_streaks_from_dates for better performance.
|
|
343
|
+
This method is kept for backward compatibility.
|
|
344
|
+
|
|
345
|
+
Args:
|
|
346
|
+
events: List of telemetry events (must be sorted by timestamp)
|
|
347
|
+
|
|
348
|
+
Returns:
|
|
349
|
+
Tuple of (current_streak, longest_streak) in days
|
|
350
|
+
"""
|
|
351
|
+
if not events:
|
|
352
|
+
return 0, 0
|
|
353
|
+
|
|
354
|
+
# Get unique days with scans
|
|
355
|
+
scan_dates = set()
|
|
356
|
+
for event in events:
|
|
357
|
+
scan_date = event.timestamp.date()
|
|
358
|
+
scan_dates.add(scan_date)
|
|
359
|
+
|
|
360
|
+
sorted_dates = sorted(scan_dates)
|
|
361
|
+
return self._calculate_streaks_from_dates(sorted_dates)
|
|
362
|
+
|
|
363
|
+
def _calculate_streaks_from_dates(self, scan_dates: list) -> tuple[int, int]:
|
|
364
|
+
"""
|
|
365
|
+
Calculate current and longest streaks from scan dates.
|
|
366
|
+
|
|
367
|
+
Performance optimized version that works directly with dates
|
|
368
|
+
instead of full event objects.
|
|
369
|
+
|
|
370
|
+
Args:
|
|
371
|
+
scan_dates: List of date objects (can be datetime.date or string dates)
|
|
372
|
+
|
|
373
|
+
Returns:
|
|
374
|
+
Tuple of (current_streak, longest_streak) in days
|
|
375
|
+
"""
|
|
376
|
+
if not scan_dates:
|
|
377
|
+
return 0, 0
|
|
378
|
+
|
|
379
|
+
# Convert to date objects if needed
|
|
380
|
+
if scan_dates and isinstance(scan_dates[0], str):
|
|
381
|
+
from datetime import datetime as dt
|
|
382
|
+
sorted_dates = sorted([dt.fromisoformat(d).date() for d in scan_dates])
|
|
383
|
+
elif scan_dates and isinstance(scan_dates[0], datetime):
|
|
384
|
+
sorted_dates = sorted([d.date() for d in scan_dates])
|
|
385
|
+
else:
|
|
386
|
+
sorted_dates = sorted(scan_dates)
|
|
387
|
+
|
|
388
|
+
if not sorted_dates:
|
|
389
|
+
return 0, 0
|
|
390
|
+
|
|
391
|
+
# Calculate current streak
|
|
392
|
+
current_streak = 1
|
|
393
|
+
today = datetime.now(timezone.utc).date()
|
|
394
|
+
|
|
395
|
+
if sorted_dates[-1] == today or sorted_dates[-1] == today - timedelta(days=1):
|
|
396
|
+
# Active streak
|
|
397
|
+
current_date = sorted_dates[-1]
|
|
398
|
+
for i in range(len(sorted_dates) - 2, -1, -1):
|
|
399
|
+
if sorted_dates[i] == current_date - timedelta(days=1):
|
|
400
|
+
current_streak += 1
|
|
401
|
+
current_date = sorted_dates[i]
|
|
402
|
+
else:
|
|
403
|
+
break
|
|
404
|
+
else:
|
|
405
|
+
# Streak broken
|
|
406
|
+
current_streak = 0
|
|
407
|
+
|
|
408
|
+
# Calculate longest streak
|
|
409
|
+
longest_streak = 1
|
|
410
|
+
temp_streak = 1
|
|
411
|
+
|
|
412
|
+
for i in range(1, len(sorted_dates)):
|
|
413
|
+
if sorted_dates[i] == sorted_dates[i-1] + timedelta(days=1):
|
|
414
|
+
temp_streak += 1
|
|
415
|
+
longest_streak = max(longest_streak, temp_streak)
|
|
416
|
+
else:
|
|
417
|
+
temp_streak = 1
|
|
418
|
+
|
|
419
|
+
return current_streak, longest_streak
|
|
420
|
+
|
|
421
|
+
def get_global_stats(
|
|
422
|
+
self,
|
|
423
|
+
*,
|
|
424
|
+
session: Session | None = None
|
|
425
|
+
) -> dict[str, Any]:
|
|
426
|
+
"""
|
|
427
|
+
Get aggregate platform statistics.
|
|
428
|
+
|
|
429
|
+
Args:
|
|
430
|
+
session: Optional database session
|
|
431
|
+
|
|
432
|
+
Returns:
|
|
433
|
+
Dictionary with global platform metrics
|
|
434
|
+
"""
|
|
435
|
+
close_session = session is None
|
|
436
|
+
if session is None:
|
|
437
|
+
session = self._get_session()
|
|
438
|
+
|
|
439
|
+
try:
|
|
440
|
+
now = datetime.now(timezone.utc)
|
|
441
|
+
week_ago = now - timedelta(days=7)
|
|
442
|
+
|
|
443
|
+
# Total users (unique customer IDs)
|
|
444
|
+
total_users = session.query(TelemetryEvent.customer_id).distinct().count()
|
|
445
|
+
|
|
446
|
+
# Active this week
|
|
447
|
+
active_week = session.query(TelemetryEvent.customer_id).filter(
|
|
448
|
+
TelemetryEvent.timestamp >= week_ago
|
|
449
|
+
).distinct().count()
|
|
450
|
+
|
|
451
|
+
# Total scans
|
|
452
|
+
total_scans = session.query(func.count(TelemetryEvent.id)).scalar() or 0
|
|
453
|
+
|
|
454
|
+
# Total threats
|
|
455
|
+
total_threats = session.query(func.count(TelemetryEvent.id)).filter(
|
|
456
|
+
TelemetryEvent.detection_count > 0
|
|
457
|
+
).scalar() or 0
|
|
458
|
+
|
|
459
|
+
detection_rate = (total_threats / total_scans * 100) if total_scans > 0 else 0.0
|
|
460
|
+
|
|
461
|
+
# Critical threats
|
|
462
|
+
critical_threats = session.query(func.count(TelemetryEvent.id)).filter(
|
|
463
|
+
TelemetryEvent.highest_severity == "critical"
|
|
464
|
+
).scalar() or 0
|
|
465
|
+
|
|
466
|
+
# Performance metrics
|
|
467
|
+
avg_latency = session.query(func.avg(TelemetryEvent.total_latency_ms)).scalar() or 0.0
|
|
468
|
+
|
|
469
|
+
# Get P95 latency (approximate using percentile)
|
|
470
|
+
total_count = session.query(func.count(TelemetryEvent.id)).scalar() or 0
|
|
471
|
+
p95_index = int(total_count * 0.95)
|
|
472
|
+
|
|
473
|
+
p95_latency = 0.0
|
|
474
|
+
if p95_index > 0:
|
|
475
|
+
p95_event = session.query(TelemetryEvent.total_latency_ms).order_by(
|
|
476
|
+
TelemetryEvent.total_latency_ms
|
|
477
|
+
).offset(p95_index).first()
|
|
478
|
+
if p95_event:
|
|
479
|
+
p95_latency = p95_event[0]
|
|
480
|
+
|
|
481
|
+
# Top severity breakdown
|
|
482
|
+
severity_counts = {}
|
|
483
|
+
for severity in ["critical", "high", "medium", "low", "info"]:
|
|
484
|
+
count = session.query(func.count(TelemetryEvent.id)).filter(
|
|
485
|
+
TelemetryEvent.highest_severity == severity
|
|
486
|
+
).scalar() or 0
|
|
487
|
+
if count > 0:
|
|
488
|
+
severity_counts[severity] = count
|
|
489
|
+
|
|
490
|
+
return {
|
|
491
|
+
"community": {
|
|
492
|
+
"total_users": total_users,
|
|
493
|
+
"active_this_week": active_week,
|
|
494
|
+
"total_scans": total_scans
|
|
495
|
+
},
|
|
496
|
+
"threats": {
|
|
497
|
+
"total_detected": total_threats,
|
|
498
|
+
"detection_rate": round(detection_rate, 2),
|
|
499
|
+
"critical_threats": critical_threats,
|
|
500
|
+
"by_severity": severity_counts
|
|
501
|
+
},
|
|
502
|
+
"performance": {
|
|
503
|
+
"avg_scan_time_ms": round(avg_latency, 2),
|
|
504
|
+
"p95_latency_ms": round(p95_latency, 2)
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
finally:
|
|
509
|
+
if close_session:
|
|
510
|
+
session.close()
|
|
511
|
+
|
|
512
|
+
def generate_report(
|
|
513
|
+
self,
|
|
514
|
+
start_date: date,
|
|
515
|
+
end_date: date,
|
|
516
|
+
*,
|
|
517
|
+
session: Session | None = None
|
|
518
|
+
) -> dict[str, Any]:
|
|
519
|
+
"""
|
|
520
|
+
Generate analytics report for date range.
|
|
521
|
+
|
|
522
|
+
Args:
|
|
523
|
+
start_date: Report start date
|
|
524
|
+
end_date: Report end date
|
|
525
|
+
session: Optional database session
|
|
526
|
+
|
|
527
|
+
Returns:
|
|
528
|
+
Comprehensive analytics report
|
|
529
|
+
|
|
530
|
+
Raises:
|
|
531
|
+
ValidationError: If date range is invalid
|
|
532
|
+
"""
|
|
533
|
+
close_session = session is None
|
|
534
|
+
if session is None:
|
|
535
|
+
session = self._get_session()
|
|
536
|
+
|
|
537
|
+
try:
|
|
538
|
+
start_dt = datetime.combine(start_date, datetime.min.time()).replace(tzinfo=timezone.utc)
|
|
539
|
+
end_dt = datetime.combine(end_date, datetime.max.time()).replace(tzinfo=timezone.utc)
|
|
540
|
+
|
|
541
|
+
# Validate date range (security: prevent DoS and invalid inputs)
|
|
542
|
+
validate_date_range(start_dt, end_dt, max_days=730)
|
|
543
|
+
|
|
544
|
+
# Get events in date range
|
|
545
|
+
events = session.query(TelemetryEvent).filter(
|
|
546
|
+
and_(
|
|
547
|
+
TelemetryEvent.timestamp >= start_dt,
|
|
548
|
+
TelemetryEvent.timestamp <= end_dt
|
|
549
|
+
)
|
|
550
|
+
).all()
|
|
551
|
+
|
|
552
|
+
# Calculate metrics
|
|
553
|
+
total_scans = len(events)
|
|
554
|
+
unique_users = len({e.customer_id for e in events})
|
|
555
|
+
threats_detected = sum(1 for e in events if e.detection_count > 0)
|
|
556
|
+
|
|
557
|
+
# Average metrics
|
|
558
|
+
avg_latency = sum(e.total_latency_ms for e in events) / total_scans if total_scans > 0 else 0.0
|
|
559
|
+
avg_l1 = sum(e.l1_inference_ms for e in events) / total_scans if total_scans > 0 else 0.0
|
|
560
|
+
|
|
561
|
+
l2_events = [e for e in events if e.l2_inference_ms is not None]
|
|
562
|
+
avg_l2 = sum(e.l2_inference_ms for e in l2_events) / len(l2_events) if l2_events else 0.0
|
|
563
|
+
|
|
564
|
+
return {
|
|
565
|
+
"period": {
|
|
566
|
+
"start_date": start_date.isoformat(),
|
|
567
|
+
"end_date": end_date.isoformat(),
|
|
568
|
+
"days": (end_date - start_date).days + 1
|
|
569
|
+
},
|
|
570
|
+
"overview": {
|
|
571
|
+
"total_scans": total_scans,
|
|
572
|
+
"unique_users": unique_users,
|
|
573
|
+
"threats_detected": threats_detected,
|
|
574
|
+
"detection_rate": round((threats_detected / total_scans * 100) if total_scans > 0 else 0.0, 2)
|
|
575
|
+
},
|
|
576
|
+
"performance": {
|
|
577
|
+
"avg_total_latency_ms": round(avg_latency, 2),
|
|
578
|
+
"avg_l1_latency_ms": round(avg_l1, 2),
|
|
579
|
+
"avg_l2_latency_ms": round(avg_l2, 2)
|
|
580
|
+
},
|
|
581
|
+
"scans_per_day": round(total_scans / ((end_date - start_date).days + 1), 2) if total_scans > 0 else 0.0
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
finally:
|
|
585
|
+
if close_session:
|
|
586
|
+
session.close()
|
|
587
|
+
|
|
588
|
+
def get_user_events_paginated(
|
|
589
|
+
self,
|
|
590
|
+
installation_id: str,
|
|
591
|
+
*,
|
|
592
|
+
limit: int = 1000,
|
|
593
|
+
offset: int = 0,
|
|
594
|
+
session: Session | None = None
|
|
595
|
+
) -> list[TelemetryEvent]:
|
|
596
|
+
"""
|
|
597
|
+
Get user events with pagination to avoid memory issues.
|
|
598
|
+
|
|
599
|
+
Performance optimization: Fetch events in batches instead of
|
|
600
|
+
loading millions of rows at once.
|
|
601
|
+
|
|
602
|
+
Args:
|
|
603
|
+
installation_id: User's installation ID
|
|
604
|
+
limit: Maximum number of events to return
|
|
605
|
+
offset: Number of events to skip
|
|
606
|
+
session: Optional database session
|
|
607
|
+
|
|
608
|
+
Returns:
|
|
609
|
+
List of TelemetryEvent objects (up to limit)
|
|
610
|
+
"""
|
|
611
|
+
close_session = session is None
|
|
612
|
+
if session is None:
|
|
613
|
+
session = self._get_session()
|
|
614
|
+
|
|
615
|
+
try:
|
|
616
|
+
return session.query(TelemetryEvent).filter(
|
|
617
|
+
TelemetryEvent.customer_id == installation_id
|
|
618
|
+
).order_by(
|
|
619
|
+
TelemetryEvent.timestamp.desc()
|
|
620
|
+
).limit(limit).offset(offset).all()
|
|
621
|
+
|
|
622
|
+
finally:
|
|
623
|
+
if close_session:
|
|
624
|
+
session.close()
|
|
625
|
+
|
|
626
|
+
def get_scan_dates_for_user(
|
|
627
|
+
self,
|
|
628
|
+
installation_id: str,
|
|
629
|
+
start_date: date | None = None,
|
|
630
|
+
end_date: date | None = None,
|
|
631
|
+
*,
|
|
632
|
+
session: Session | None = None
|
|
633
|
+
) -> list[date]:
|
|
634
|
+
"""
|
|
635
|
+
Get unique scan dates for a user using database aggregation.
|
|
636
|
+
|
|
637
|
+
Performance optimization: Much faster than loading all events
|
|
638
|
+
into memory. Uses database DISTINCT to return only unique dates.
|
|
639
|
+
|
|
640
|
+
Args:
|
|
641
|
+
installation_id: User's installation ID
|
|
642
|
+
start_date: Optional start date filter
|
|
643
|
+
end_date: Optional end date filter
|
|
644
|
+
session: Optional database session
|
|
645
|
+
|
|
646
|
+
Returns:
|
|
647
|
+
List of unique scan dates
|
|
648
|
+
"""
|
|
649
|
+
close_session = session is None
|
|
650
|
+
if session is None:
|
|
651
|
+
session = self._get_session()
|
|
652
|
+
|
|
653
|
+
try:
|
|
654
|
+
query = session.query(
|
|
655
|
+
func.date(TelemetryEvent.timestamp).label('scan_date')
|
|
656
|
+
).filter(
|
|
657
|
+
TelemetryEvent.customer_id == installation_id
|
|
658
|
+
)
|
|
659
|
+
|
|
660
|
+
if start_date:
|
|
661
|
+
start_dt = datetime.combine(start_date, datetime.min.time()).replace(tzinfo=timezone.utc)
|
|
662
|
+
query = query.filter(TelemetryEvent.timestamp >= start_dt)
|
|
663
|
+
|
|
664
|
+
if end_date:
|
|
665
|
+
end_dt = datetime.combine(end_date, datetime.max.time()).replace(tzinfo=timezone.utc)
|
|
666
|
+
query = query.filter(TelemetryEvent.timestamp <= end_dt)
|
|
667
|
+
|
|
668
|
+
# Returns list of dates, not full event objects
|
|
669
|
+
result = query.distinct().order_by('scan_date').all()
|
|
670
|
+
return [row[0] for row in result]
|
|
671
|
+
|
|
672
|
+
finally:
|
|
673
|
+
if close_session:
|
|
674
|
+
session.close()
|
|
675
|
+
|
|
676
|
+
def calculate_retention_batch(
|
|
677
|
+
self,
|
|
678
|
+
installation_ids: list[str],
|
|
679
|
+
cohort_date: date,
|
|
680
|
+
*,
|
|
681
|
+
session: Session | None = None
|
|
682
|
+
) -> dict[str, dict[str, Any]]:
|
|
683
|
+
"""
|
|
684
|
+
Calculate retention for multiple users in one query.
|
|
685
|
+
|
|
686
|
+
Performance optimization: Avoids N+1 query problem by batching
|
|
687
|
+
all user retention calculations together.
|
|
688
|
+
|
|
689
|
+
Args:
|
|
690
|
+
installation_ids: List of installation IDs
|
|
691
|
+
cohort_date: Cohort date to analyze
|
|
692
|
+
session: Optional database session
|
|
693
|
+
|
|
694
|
+
Returns:
|
|
695
|
+
Dictionary mapping installation_id to retention metrics
|
|
696
|
+
"""
|
|
697
|
+
close_session = session is None
|
|
698
|
+
if session is None:
|
|
699
|
+
session = self._get_session()
|
|
700
|
+
|
|
701
|
+
try:
|
|
702
|
+
results = {}
|
|
703
|
+
|
|
704
|
+
# Batch query for all users
|
|
705
|
+
cohort_start = datetime.combine(cohort_date, datetime.min.time()).replace(tzinfo=timezone.utc)
|
|
706
|
+
cohort_start + timedelta(days=1)
|
|
707
|
+
|
|
708
|
+
# Get all events for these users in one query
|
|
709
|
+
events = session.query(
|
|
710
|
+
TelemetryEvent.customer_id,
|
|
711
|
+
func.date(TelemetryEvent.timestamp).label('event_date')
|
|
712
|
+
).filter(
|
|
713
|
+
TelemetryEvent.customer_id.in_(installation_ids)
|
|
714
|
+
).all()
|
|
715
|
+
|
|
716
|
+
# Group by customer
|
|
717
|
+
by_customer = {}
|
|
718
|
+
for customer_id, event_date in events:
|
|
719
|
+
if customer_id not in by_customer:
|
|
720
|
+
by_customer[customer_id] = set()
|
|
721
|
+
by_customer[customer_id].add(event_date)
|
|
722
|
+
|
|
723
|
+
# Calculate retention for each user
|
|
724
|
+
for installation_id in installation_ids:
|
|
725
|
+
user_dates = by_customer.get(installation_id, set())
|
|
726
|
+
|
|
727
|
+
# Check activity on specific days
|
|
728
|
+
day_1 = cohort_date + timedelta(days=1)
|
|
729
|
+
day_7 = cohort_date + timedelta(days=7)
|
|
730
|
+
day_30 = cohort_date + timedelta(days=30)
|
|
731
|
+
|
|
732
|
+
results[installation_id] = {
|
|
733
|
+
"day_1": day_1 in user_dates,
|
|
734
|
+
"day_7": day_7 in user_dates,
|
|
735
|
+
"day_30": day_30 in user_dates,
|
|
736
|
+
"total_active_days": len(user_dates)
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
return results
|
|
740
|
+
|
|
741
|
+
finally:
|
|
742
|
+
if close_session:
|
|
743
|
+
session.close()
|
|
744
|
+
|
|
745
|
+
def close(self) -> None:
|
|
746
|
+
"""Close database connection."""
|
|
747
|
+
self.engine.dispose()
|
|
748
|
+
logger.info("Analytics engine closed")
|