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,921 @@
|
|
|
1
|
+
"""Gemma-based 5-head multilabel L2 detector.
|
|
2
|
+
|
|
3
|
+
This detector uses the EmbeddingGemma-300M model with 5 classifier heads:
|
|
4
|
+
1. is_threat (binary): benign vs threat
|
|
5
|
+
2. threat_family (multiclass, 9): Attack category
|
|
6
|
+
3. severity (multiclass, 5): Threat severity level
|
|
7
|
+
4. primary_technique (multiclass, 22): Specific attack technique
|
|
8
|
+
5. harm_types (multilabel, 10): Types of potential harm
|
|
9
|
+
|
|
10
|
+
Model files expected in model directory:
|
|
11
|
+
- model_int8.onnx: EmbeddingGemma-300M (INT8 quantized)
|
|
12
|
+
- classifier_is_threat_int8.onnx
|
|
13
|
+
- classifier_threat_family_int8.onnx
|
|
14
|
+
- classifier_severity_int8.onnx
|
|
15
|
+
- classifier_primary_technique_int8.onnx
|
|
16
|
+
- classifier_harm_types_int8.onnx
|
|
17
|
+
- tokenizer.json, config.json, label_config.json, model_metadata.json
|
|
18
|
+
|
|
19
|
+
NEW: Voting Engine Integration
|
|
20
|
+
The ensemble decision logic now uses VotingEngine for transparent weighted voting
|
|
21
|
+
instead of boost-based heuristics. Set L2Config.voting.enabled=False to use legacy logic.
|
|
22
|
+
"""
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import json
|
|
26
|
+
import time
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
from typing import Any
|
|
29
|
+
|
|
30
|
+
import numpy as np
|
|
31
|
+
|
|
32
|
+
from raxe.domain.engine.executor import ScanResult as L1ScanResult
|
|
33
|
+
from raxe.domain.ml.gemma_models import (
|
|
34
|
+
DEFAULT_HARM_THRESHOLDS,
|
|
35
|
+
GemmaClassificationResult,
|
|
36
|
+
HarmType,
|
|
37
|
+
MultilabelResult,
|
|
38
|
+
PrimaryTechnique,
|
|
39
|
+
Severity,
|
|
40
|
+
ThreatFamily,
|
|
41
|
+
)
|
|
42
|
+
from raxe.domain.ml.l2_config import L2Config, get_l2_config
|
|
43
|
+
from raxe.domain.ml.protocol import L2Prediction, L2Result, L2ThreatType
|
|
44
|
+
from raxe.domain.ml.voting import (
|
|
45
|
+
Decision,
|
|
46
|
+
HeadOutputs,
|
|
47
|
+
VotingEngine,
|
|
48
|
+
VotingResult,
|
|
49
|
+
)
|
|
50
|
+
from raxe.utils.logging import get_logger
|
|
51
|
+
|
|
52
|
+
logger = get_logger(__name__)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class GemmaL2Detector:
|
|
56
|
+
"""Gemma-based 5-head L2 detector.
|
|
57
|
+
|
|
58
|
+
Implements L2Detector protocol with the new Gemma multilabel architecture.
|
|
59
|
+
|
|
60
|
+
Performance targets:
|
|
61
|
+
- P95 latency: <50ms (with cache miss)
|
|
62
|
+
- P50 latency: <10ms (with cache hit)
|
|
63
|
+
- Memory: <400MB
|
|
64
|
+
|
|
65
|
+
Example:
|
|
66
|
+
detector = GemmaL2Detector(model_dir="/path/to/models")
|
|
67
|
+
result = detector.analyze(text, l1_results)
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
VERSION = "gemma-compact-v1"
|
|
71
|
+
EMBEDDING_DIM = 256 # Matryoshka truncation from 768
|
|
72
|
+
|
|
73
|
+
def __init__(
|
|
74
|
+
self,
|
|
75
|
+
model_dir: str | Path,
|
|
76
|
+
*,
|
|
77
|
+
confidence_threshold: float | None = None,
|
|
78
|
+
harm_thresholds: dict[str, float] | None = None,
|
|
79
|
+
cache_size: int = 1000,
|
|
80
|
+
scorer: Any | None = None,
|
|
81
|
+
l2_config: L2Config | None = None,
|
|
82
|
+
voting_preset: str | None = None,
|
|
83
|
+
):
|
|
84
|
+
"""Initialize Gemma L2 detector.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
model_dir: Path to directory containing ONNX models
|
|
88
|
+
confidence_threshold: Override threshold for binary threat classification
|
|
89
|
+
(uses L2Config if not provided)
|
|
90
|
+
harm_thresholds: Per-class thresholds for harm_types multilabel
|
|
91
|
+
cache_size: Size of embedding cache (0 to disable)
|
|
92
|
+
scorer: Optional HierarchicalThreatScorer instance
|
|
93
|
+
l2_config: L2 configuration (uses global config if not provided)
|
|
94
|
+
voting_preset: Voting preset override (balanced, high_security, low_fp)
|
|
95
|
+
"""
|
|
96
|
+
self.model_dir = Path(model_dir)
|
|
97
|
+
self._l2_config = l2_config or get_l2_config()
|
|
98
|
+
# Use provided threshold, or config threshold
|
|
99
|
+
self.confidence_threshold = (
|
|
100
|
+
confidence_threshold
|
|
101
|
+
if confidence_threshold is not None
|
|
102
|
+
else self._l2_config.thresholds.threat_threshold
|
|
103
|
+
)
|
|
104
|
+
# Use provided harm thresholds, or config thresholds
|
|
105
|
+
self.harm_thresholds = (
|
|
106
|
+
harm_thresholds
|
|
107
|
+
if harm_thresholds is not None
|
|
108
|
+
else self._l2_config.thresholds.harm_type_thresholds.copy()
|
|
109
|
+
)
|
|
110
|
+
self.scorer = scorer
|
|
111
|
+
|
|
112
|
+
# Initialize voting engine if enabled
|
|
113
|
+
self._voting_enabled = self._l2_config.voting.enabled
|
|
114
|
+
if self._voting_enabled:
|
|
115
|
+
# Use override preset if provided, otherwise use config preset
|
|
116
|
+
preset = voting_preset or self._l2_config.voting.preset
|
|
117
|
+
self._voting_engine = VotingEngine(preset=preset)
|
|
118
|
+
logger.info(
|
|
119
|
+
"VotingEngine initialized",
|
|
120
|
+
preset=preset,
|
|
121
|
+
enabled=True,
|
|
122
|
+
)
|
|
123
|
+
else:
|
|
124
|
+
self._voting_engine = None
|
|
125
|
+
logger.info("VotingEngine disabled, using legacy ensemble logic")
|
|
126
|
+
|
|
127
|
+
# Lazy imports for ONNX runtime
|
|
128
|
+
import onnxruntime as ort
|
|
129
|
+
from transformers import PreTrainedTokenizerFast
|
|
130
|
+
|
|
131
|
+
self._ort = ort
|
|
132
|
+
|
|
133
|
+
# Load tokenizer from tokenizer.json (portable format)
|
|
134
|
+
logger.info("Loading Gemma tokenizer", model_dir=str(self.model_dir))
|
|
135
|
+
tokenizer_path = self.model_dir / "tokenizer.json"
|
|
136
|
+
if not tokenizer_path.exists():
|
|
137
|
+
raise FileNotFoundError(f"tokenizer.json not found in {self.model_dir}")
|
|
138
|
+
self._tokenizer = PreTrainedTokenizerFast(tokenizer_file=str(tokenizer_path))
|
|
139
|
+
|
|
140
|
+
# Configure special tokens from config.json
|
|
141
|
+
config_path = self.model_dir / "config.json"
|
|
142
|
+
if config_path.exists():
|
|
143
|
+
import json
|
|
144
|
+
with open(config_path) as f:
|
|
145
|
+
config = json.load(f)
|
|
146
|
+
# Set pad_token_id (Gemma uses id 0 for padding)
|
|
147
|
+
if "pad_token_id" in config:
|
|
148
|
+
self._tokenizer.pad_token_id = config["pad_token_id"]
|
|
149
|
+
# Set pad_token if not already set
|
|
150
|
+
if self._tokenizer.pad_token is None:
|
|
151
|
+
self._tokenizer.pad_token = self._tokenizer.decode([config["pad_token_id"]])
|
|
152
|
+
if "eos_token_id" in config:
|
|
153
|
+
self._tokenizer.eos_token_id = config["eos_token_id"]
|
|
154
|
+
if "bos_token_id" in config:
|
|
155
|
+
self._tokenizer.bos_token_id = config["bos_token_id"]
|
|
156
|
+
|
|
157
|
+
# Create session options
|
|
158
|
+
sess_options = ort.SessionOptions()
|
|
159
|
+
sess_options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL
|
|
160
|
+
sess_options.log_severity_level = 3 # ERROR only
|
|
161
|
+
sess_options.intra_op_num_threads = 4
|
|
162
|
+
sess_options.inter_op_num_threads = 1
|
|
163
|
+
sess_options.enable_mem_pattern = True
|
|
164
|
+
sess_options.enable_cpu_mem_arena = True
|
|
165
|
+
|
|
166
|
+
providers = ["CPUExecutionProvider"]
|
|
167
|
+
|
|
168
|
+
# Load embedding model
|
|
169
|
+
embedding_path = self._find_model_file("model", ".onnx")
|
|
170
|
+
logger.info("Loading embedding model", path=str(embedding_path))
|
|
171
|
+
self._embedding_session = ort.InferenceSession(
|
|
172
|
+
str(embedding_path), sess_options, providers=providers
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# Load classifier heads
|
|
176
|
+
self._classifiers: dict[str, ort.InferenceSession] = {}
|
|
177
|
+
for head in ["is_threat", "threat_family", "severity", "primary_technique", "harm_types"]:
|
|
178
|
+
classifier_path = self._find_model_file(f"classifier_{head}", ".onnx")
|
|
179
|
+
logger.info(f"Loading classifier: {head}", path=str(classifier_path))
|
|
180
|
+
self._classifiers[head] = ort.InferenceSession(
|
|
181
|
+
str(classifier_path), sess_options, providers=providers
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
# Load label config
|
|
185
|
+
label_config_path = self.model_dir / "label_config.json"
|
|
186
|
+
if label_config_path.exists():
|
|
187
|
+
with open(label_config_path) as f:
|
|
188
|
+
self._label_config = json.load(f)
|
|
189
|
+
else:
|
|
190
|
+
self._label_config = {}
|
|
191
|
+
|
|
192
|
+
# Load model metadata
|
|
193
|
+
metadata_path = self.model_dir / "model_metadata.json"
|
|
194
|
+
if metadata_path.exists():
|
|
195
|
+
with open(metadata_path) as f:
|
|
196
|
+
self._model_metadata = json.load(f)
|
|
197
|
+
else:
|
|
198
|
+
self._model_metadata = {}
|
|
199
|
+
|
|
200
|
+
# Setup embedding cache
|
|
201
|
+
self._cache_enabled = cache_size > 0
|
|
202
|
+
if self._cache_enabled:
|
|
203
|
+
from raxe.domain.ml.embedding_cache import EmbeddingCache
|
|
204
|
+
self._embedding_cache = EmbeddingCache(max_size=cache_size)
|
|
205
|
+
else:
|
|
206
|
+
self._embedding_cache = None
|
|
207
|
+
|
|
208
|
+
logger.info(
|
|
209
|
+
"GemmaL2Detector initialized",
|
|
210
|
+
model_dir=str(self.model_dir),
|
|
211
|
+
embedding_dim=self.EMBEDDING_DIM,
|
|
212
|
+
cache_size=cache_size,
|
|
213
|
+
confidence_threshold=confidence_threshold,
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
def _find_model_file(self, prefix: str, suffix: str) -> Path:
|
|
217
|
+
"""Find model file with INT8 preference."""
|
|
218
|
+
# Prefer INT8 quantized version
|
|
219
|
+
int8_path = self.model_dir / f"{prefix}_int8{suffix}"
|
|
220
|
+
if int8_path.exists():
|
|
221
|
+
return int8_path
|
|
222
|
+
|
|
223
|
+
# Fall back to non-quantized
|
|
224
|
+
regular_path = self.model_dir / f"{prefix}{suffix}"
|
|
225
|
+
if regular_path.exists():
|
|
226
|
+
return regular_path
|
|
227
|
+
|
|
228
|
+
# Try glob pattern
|
|
229
|
+
matches = list(self.model_dir.glob(f"{prefix}*{suffix}"))
|
|
230
|
+
if matches:
|
|
231
|
+
# Prefer INT8 if available
|
|
232
|
+
for match in matches:
|
|
233
|
+
if "int8" in match.name:
|
|
234
|
+
return match
|
|
235
|
+
return matches[0]
|
|
236
|
+
|
|
237
|
+
raise FileNotFoundError(
|
|
238
|
+
f"No model file found for {prefix}*{suffix} in {self.model_dir}"
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
def analyze(
|
|
242
|
+
self,
|
|
243
|
+
text: str,
|
|
244
|
+
l1_results: L1ScanResult,
|
|
245
|
+
context: dict[str, Any] | None = None,
|
|
246
|
+
) -> L2Result:
|
|
247
|
+
"""Analyze text for threats using Gemma 5-head classifier.
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
text: Text to analyze
|
|
251
|
+
l1_results: Results from L1 rule-based detection
|
|
252
|
+
context: Optional context metadata
|
|
253
|
+
|
|
254
|
+
Returns:
|
|
255
|
+
L2Result with predictions from all 5 heads and voting metadata
|
|
256
|
+
"""
|
|
257
|
+
start_time = time.perf_counter()
|
|
258
|
+
|
|
259
|
+
try:
|
|
260
|
+
# Generate embeddings (with caching)
|
|
261
|
+
embeddings = self._generate_embeddings(text)
|
|
262
|
+
|
|
263
|
+
# Run classification (returns both classification and voting result)
|
|
264
|
+
classification, voting_result = self._classify(embeddings)
|
|
265
|
+
|
|
266
|
+
# Build predictions
|
|
267
|
+
predictions = self._build_predictions(classification, text, voting_result)
|
|
268
|
+
|
|
269
|
+
# Apply scorer if available
|
|
270
|
+
hierarchical_score = None
|
|
271
|
+
classification_label = None
|
|
272
|
+
recommended_action = None
|
|
273
|
+
decision_rationale = None
|
|
274
|
+
signal_quality = None
|
|
275
|
+
|
|
276
|
+
if self.scorer and classification.is_threat:
|
|
277
|
+
scoring_result = self._apply_scorer(classification, text)
|
|
278
|
+
if scoring_result:
|
|
279
|
+
hierarchical_score = scoring_result.hierarchical_score
|
|
280
|
+
classification_label = scoring_result.classification.value
|
|
281
|
+
recommended_action = scoring_result.action.value
|
|
282
|
+
decision_rationale = scoring_result.reason
|
|
283
|
+
signal_quality = {
|
|
284
|
+
"is_consistent": scoring_result.is_consistent,
|
|
285
|
+
"variance": scoring_result.variance,
|
|
286
|
+
"weak_margins_count": scoring_result.weak_margins_count,
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
# Build classification and action from voting result if available
|
|
290
|
+
if voting_result:
|
|
291
|
+
classification_label = self._voting_decision_to_classification(
|
|
292
|
+
voting_result.decision, voting_result.confidence
|
|
293
|
+
)
|
|
294
|
+
recommended_action = self._voting_decision_to_action(voting_result.decision)
|
|
295
|
+
decision_rationale = f"Voting rule: {voting_result.decision_rule_triggered}"
|
|
296
|
+
|
|
297
|
+
duration_ms = (time.perf_counter() - start_time) * 1000
|
|
298
|
+
|
|
299
|
+
# Build voting metadata for telemetry
|
|
300
|
+
voting_metadata = voting_result.to_dict() if voting_result else None
|
|
301
|
+
|
|
302
|
+
return L2Result(
|
|
303
|
+
predictions=predictions,
|
|
304
|
+
confidence=classification.threat_probability,
|
|
305
|
+
processing_time_ms=duration_ms,
|
|
306
|
+
model_version=self.VERSION,
|
|
307
|
+
features_extracted={
|
|
308
|
+
"text_length": len(text),
|
|
309
|
+
"l1_detection_count": l1_results.detection_count,
|
|
310
|
+
"embedding_dim": self.EMBEDDING_DIM,
|
|
311
|
+
},
|
|
312
|
+
metadata={
|
|
313
|
+
"detector_type": "gemma",
|
|
314
|
+
"classification_result": classification.to_dict(),
|
|
315
|
+
"voting_enabled": self._voting_enabled,
|
|
316
|
+
},
|
|
317
|
+
hierarchical_score=hierarchical_score,
|
|
318
|
+
classification=classification_label,
|
|
319
|
+
recommended_action=recommended_action,
|
|
320
|
+
decision_rationale=decision_rationale,
|
|
321
|
+
signal_quality=signal_quality,
|
|
322
|
+
voting=voting_metadata,
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
except Exception as e:
|
|
326
|
+
logger.error("Gemma detection failed", error=str(e), exc_info=True)
|
|
327
|
+
duration_ms = (time.perf_counter() - start_time) * 1000
|
|
328
|
+
return L2Result(
|
|
329
|
+
predictions=[],
|
|
330
|
+
confidence=0.0,
|
|
331
|
+
processing_time_ms=duration_ms,
|
|
332
|
+
model_version=self.VERSION,
|
|
333
|
+
metadata={"error": str(e), "detector_type": "gemma"},
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
def _voting_decision_to_classification(
|
|
337
|
+
self, decision: Decision, confidence: float
|
|
338
|
+
) -> str:
|
|
339
|
+
"""Map voting decision to classification label."""
|
|
340
|
+
if decision == Decision.THREAT:
|
|
341
|
+
if confidence >= 0.9:
|
|
342
|
+
return "HIGH_THREAT"
|
|
343
|
+
elif confidence >= 0.75:
|
|
344
|
+
return "THREAT"
|
|
345
|
+
else:
|
|
346
|
+
return "LIKELY_THREAT"
|
|
347
|
+
elif decision == Decision.REVIEW:
|
|
348
|
+
return "REVIEW"
|
|
349
|
+
else:
|
|
350
|
+
return "SAFE"
|
|
351
|
+
|
|
352
|
+
def _voting_decision_to_action(self, decision: Decision) -> str:
|
|
353
|
+
"""Map voting decision to recommended action."""
|
|
354
|
+
if decision == Decision.THREAT:
|
|
355
|
+
return "BLOCK"
|
|
356
|
+
elif decision == Decision.REVIEW:
|
|
357
|
+
return "MANUAL_REVIEW"
|
|
358
|
+
else:
|
|
359
|
+
return "ALLOW"
|
|
360
|
+
|
|
361
|
+
def _generate_embeddings(self, text: str) -> np.ndarray:
|
|
362
|
+
"""Generate embeddings with optional caching.
|
|
363
|
+
|
|
364
|
+
The EmbeddingGemma model outputs two tensors:
|
|
365
|
+
- outputs[0]: token embeddings (batch, seq_len, hidden_dim)
|
|
366
|
+
- outputs[1]: pooled embedding (batch, hidden_dim) - used directly
|
|
367
|
+
"""
|
|
368
|
+
# Check cache
|
|
369
|
+
if self._cache_enabled and self._embedding_cache:
|
|
370
|
+
cached = self._embedding_cache.get(text)
|
|
371
|
+
if cached is not None:
|
|
372
|
+
return cached
|
|
373
|
+
|
|
374
|
+
# Tokenize
|
|
375
|
+
inputs = self._tokenizer(
|
|
376
|
+
text,
|
|
377
|
+
padding=True,
|
|
378
|
+
truncation=True,
|
|
379
|
+
max_length=512,
|
|
380
|
+
return_tensors="np",
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
# Get embeddings from model
|
|
384
|
+
outputs = self._embedding_session.run(
|
|
385
|
+
None,
|
|
386
|
+
{
|
|
387
|
+
"input_ids": inputs["input_ids"].astype(np.int64),
|
|
388
|
+
"attention_mask": inputs["attention_mask"].astype(np.int64),
|
|
389
|
+
},
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
# outputs[1] is the model's pooled embedding (batch_size, hidden_dim)
|
|
393
|
+
# The model already does internal pooling, so use it directly
|
|
394
|
+
embeddings = outputs[1]
|
|
395
|
+
|
|
396
|
+
# Truncate to 256 dims (Matryoshka)
|
|
397
|
+
embeddings = embeddings[:, :self.EMBEDDING_DIM]
|
|
398
|
+
|
|
399
|
+
# L2 normalize
|
|
400
|
+
norms = np.linalg.norm(embeddings, axis=1, keepdims=True)
|
|
401
|
+
embeddings = embeddings / (norms + 1e-9)
|
|
402
|
+
|
|
403
|
+
# Cache result
|
|
404
|
+
if self._cache_enabled and self._embedding_cache:
|
|
405
|
+
self._embedding_cache.put(text, embeddings)
|
|
406
|
+
|
|
407
|
+
return embeddings
|
|
408
|
+
|
|
409
|
+
def _classify(
|
|
410
|
+
self, embeddings: np.ndarray
|
|
411
|
+
) -> tuple[GemmaClassificationResult, VotingResult | None]:
|
|
412
|
+
"""Run all 5 classifier heads with ensemble logic.
|
|
413
|
+
|
|
414
|
+
Each classifier returns 2 outputs:
|
|
415
|
+
- [0]: predicted class (int64)
|
|
416
|
+
- [1]: probabilities (float32)
|
|
417
|
+
|
|
418
|
+
Returns:
|
|
419
|
+
Tuple of (GemmaClassificationResult, VotingResult or None)
|
|
420
|
+
VotingResult is None if voting engine is disabled.
|
|
421
|
+
"""
|
|
422
|
+
embeddings_f32 = embeddings.astype(np.float32)
|
|
423
|
+
|
|
424
|
+
# ════════════════════════════════════════════════════════════════════
|
|
425
|
+
# Run all 5 classifier heads (always run all for voting engine)
|
|
426
|
+
# ════════════════════════════════════════════════════════════════════
|
|
427
|
+
|
|
428
|
+
# 1. Binary threat classification
|
|
429
|
+
is_threat_outputs = self._classifiers["is_threat"].run(
|
|
430
|
+
None, {"embeddings": embeddings_f32}
|
|
431
|
+
)
|
|
432
|
+
is_threat_proba = is_threat_outputs[1][0] # [benign_prob, threat_prob]
|
|
433
|
+
safe_prob = float(is_threat_proba[0])
|
|
434
|
+
threat_prob = float(is_threat_proba[1])
|
|
435
|
+
|
|
436
|
+
# 2. Threat family
|
|
437
|
+
family_outputs = self._classifiers["threat_family"].run(
|
|
438
|
+
None, {"embeddings": embeddings_f32}
|
|
439
|
+
)
|
|
440
|
+
family_proba = family_outputs[1][0]
|
|
441
|
+
family_idx = int(np.argmax(family_proba))
|
|
442
|
+
family_confidence = float(family_proba[family_idx])
|
|
443
|
+
threat_family = ThreatFamily.from_index(family_idx)
|
|
444
|
+
|
|
445
|
+
# 3. Severity
|
|
446
|
+
severity_outputs = self._classifiers["severity"].run(
|
|
447
|
+
None, {"embeddings": embeddings_f32}
|
|
448
|
+
)
|
|
449
|
+
severity_proba = severity_outputs[1][0]
|
|
450
|
+
severity_idx = int(np.argmax(severity_proba))
|
|
451
|
+
severity_confidence = float(severity_proba[severity_idx])
|
|
452
|
+
severity = Severity.from_index(severity_idx)
|
|
453
|
+
|
|
454
|
+
# 4. Primary technique (always run for voting engine)
|
|
455
|
+
technique_outputs = self._classifiers["primary_technique"].run(
|
|
456
|
+
None, {"embeddings": embeddings_f32}
|
|
457
|
+
)
|
|
458
|
+
technique_proba_arr = technique_outputs[1][0]
|
|
459
|
+
technique_idx = int(np.argmax(technique_proba_arr))
|
|
460
|
+
technique_confidence = float(technique_proba_arr[technique_idx])
|
|
461
|
+
primary_technique = PrimaryTechnique.from_index(technique_idx)
|
|
462
|
+
technique_proba = tuple(float(p) for p in technique_proba_arr)
|
|
463
|
+
|
|
464
|
+
# 5. Harm types (always run for voting engine)
|
|
465
|
+
harm_outputs = self._classifiers["harm_types"].run(
|
|
466
|
+
None, {"embeddings": embeddings_f32}
|
|
467
|
+
)
|
|
468
|
+
harm_proba = harm_outputs[1][0]
|
|
469
|
+
harm_types_result = self._process_multilabel_harm_types(harm_proba)
|
|
470
|
+
harm_max_prob = max(float(p) for p in harm_proba)
|
|
471
|
+
harm_active_labels = [h.value for h in harm_types_result.active_labels]
|
|
472
|
+
|
|
473
|
+
# ════════════════════════════════════════════════════════════════════
|
|
474
|
+
# DECISION LOGIC: Voting Engine or Legacy Ensemble
|
|
475
|
+
# ════════════════════════════════════════════════════════════════════
|
|
476
|
+
|
|
477
|
+
voting_result: VotingResult | None = None
|
|
478
|
+
|
|
479
|
+
if self._voting_enabled and self._voting_engine:
|
|
480
|
+
# Use new VotingEngine for transparent weighted voting
|
|
481
|
+
head_outputs = HeadOutputs(
|
|
482
|
+
binary_threat_prob=threat_prob,
|
|
483
|
+
binary_safe_prob=safe_prob,
|
|
484
|
+
family_prediction=threat_family.value,
|
|
485
|
+
family_confidence=family_confidence,
|
|
486
|
+
severity_prediction=severity.value,
|
|
487
|
+
severity_confidence=severity_confidence,
|
|
488
|
+
technique_prediction=primary_technique.value if primary_technique else None,
|
|
489
|
+
technique_confidence=technique_confidence,
|
|
490
|
+
harm_max_probability=harm_max_prob,
|
|
491
|
+
harm_active_labels=harm_active_labels,
|
|
492
|
+
)
|
|
493
|
+
|
|
494
|
+
voting_result = self._voting_engine.vote(head_outputs)
|
|
495
|
+
|
|
496
|
+
# Map voting decision to is_threat and effective probability
|
|
497
|
+
is_threat = voting_result.decision in (Decision.THREAT, Decision.REVIEW)
|
|
498
|
+
final_threat_prob = voting_result.confidence
|
|
499
|
+
family_override_triggered = False # Not used with voting engine
|
|
500
|
+
else:
|
|
501
|
+
# Legacy boost-based ensemble logic
|
|
502
|
+
is_threat, final_threat_prob, family_override_triggered = (
|
|
503
|
+
self._legacy_ensemble_logic(
|
|
504
|
+
threat_prob=threat_prob,
|
|
505
|
+
threat_family=threat_family,
|
|
506
|
+
family_confidence=family_confidence,
|
|
507
|
+
severity=severity,
|
|
508
|
+
primary_technique=primary_technique,
|
|
509
|
+
technique_confidence=technique_confidence,
|
|
510
|
+
)
|
|
511
|
+
)
|
|
512
|
+
|
|
513
|
+
# Determine harm_types for result (only include if threat)
|
|
514
|
+
harm_types = harm_types_result if is_threat else None
|
|
515
|
+
|
|
516
|
+
result = GemmaClassificationResult(
|
|
517
|
+
is_threat=is_threat,
|
|
518
|
+
threat_probability=final_threat_prob,
|
|
519
|
+
safe_probability=1.0 - final_threat_prob,
|
|
520
|
+
threat_family=threat_family,
|
|
521
|
+
family_confidence=family_confidence,
|
|
522
|
+
family_probabilities=tuple(float(p) for p in family_proba),
|
|
523
|
+
severity=severity,
|
|
524
|
+
severity_confidence=severity_confidence,
|
|
525
|
+
severity_probabilities=tuple(float(p) for p in severity_proba),
|
|
526
|
+
primary_technique=primary_technique,
|
|
527
|
+
technique_confidence=technique_confidence,
|
|
528
|
+
technique_probabilities=technique_proba,
|
|
529
|
+
harm_types=harm_types,
|
|
530
|
+
raw_threat_probability=threat_prob,
|
|
531
|
+
family_override_triggered=family_override_triggered,
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
return result, voting_result
|
|
535
|
+
|
|
536
|
+
def _legacy_ensemble_logic(
|
|
537
|
+
self,
|
|
538
|
+
threat_prob: float,
|
|
539
|
+
threat_family: ThreatFamily,
|
|
540
|
+
family_confidence: float,
|
|
541
|
+
severity: Severity,
|
|
542
|
+
primary_technique: PrimaryTechnique | None,
|
|
543
|
+
technique_confidence: float,
|
|
544
|
+
) -> tuple[bool, float, bool]:
|
|
545
|
+
"""Legacy boost-based ensemble logic (used when voting is disabled).
|
|
546
|
+
|
|
547
|
+
Returns:
|
|
548
|
+
Tuple of (is_threat, effective_threat_prob, family_override_triggered)
|
|
549
|
+
"""
|
|
550
|
+
cfg = self._l2_config
|
|
551
|
+
ensemble = cfg.ensemble
|
|
552
|
+
|
|
553
|
+
# Start with binary head decision
|
|
554
|
+
effective_threat_prob = threat_prob
|
|
555
|
+
is_threat = threat_prob >= self.confidence_threshold
|
|
556
|
+
|
|
557
|
+
# Check for family override
|
|
558
|
+
family_override_triggered = False
|
|
559
|
+
if ensemble.use_family_override:
|
|
560
|
+
family_is_benign = threat_family == ThreatFamily.BENIGN
|
|
561
|
+
family_override_threshold = cfg.thresholds.family_override_threshold
|
|
562
|
+
|
|
563
|
+
if not family_is_benign and family_confidence >= family_override_threshold:
|
|
564
|
+
family_override_triggered = True
|
|
565
|
+
is_threat = True
|
|
566
|
+
if effective_threat_prob < self.confidence_threshold:
|
|
567
|
+
effective_threat_prob = max(
|
|
568
|
+
effective_threat_prob,
|
|
569
|
+
self.confidence_threshold + 0.05
|
|
570
|
+
)
|
|
571
|
+
|
|
572
|
+
# Check for always-threat families
|
|
573
|
+
if threat_family.value in ensemble.always_threat_families:
|
|
574
|
+
is_threat = True
|
|
575
|
+
effective_threat_prob = max(effective_threat_prob, 0.75)
|
|
576
|
+
|
|
577
|
+
# Apply severity boost
|
|
578
|
+
if ensemble.use_severity_boost:
|
|
579
|
+
if severity in (Severity.HIGH, Severity.CRITICAL):
|
|
580
|
+
effective_threat_prob = min(
|
|
581
|
+
1.0, effective_threat_prob + ensemble.severity_boost_amount
|
|
582
|
+
)
|
|
583
|
+
if severity == Severity.CRITICAL and not is_threat:
|
|
584
|
+
is_threat = True
|
|
585
|
+
|
|
586
|
+
# Apply technique boost
|
|
587
|
+
if ensemble.use_technique_boost and primary_technique:
|
|
588
|
+
if (
|
|
589
|
+
technique_confidence >= ensemble.technique_boost_threshold
|
|
590
|
+
and primary_technique.value in ensemble.high_confidence_techniques
|
|
591
|
+
):
|
|
592
|
+
effective_threat_prob = min(
|
|
593
|
+
1.0, effective_threat_prob + ensemble.technique_boost_amount
|
|
594
|
+
)
|
|
595
|
+
if not is_threat:
|
|
596
|
+
is_threat = True
|
|
597
|
+
|
|
598
|
+
return is_threat, effective_threat_prob, family_override_triggered
|
|
599
|
+
|
|
600
|
+
def _process_multilabel_harm_types(
|
|
601
|
+
self, probabilities: np.ndarray
|
|
602
|
+
) -> MultilabelResult:
|
|
603
|
+
"""Process multilabel harm types with per-class thresholds."""
|
|
604
|
+
harm_classes = HarmType.all_classes()
|
|
605
|
+
active_labels = []
|
|
606
|
+
proba_dict: dict[str, float] = {}
|
|
607
|
+
thresholds_dict: dict[str, float] = {}
|
|
608
|
+
|
|
609
|
+
for i, harm_type in enumerate(harm_classes):
|
|
610
|
+
prob = float(probabilities[i])
|
|
611
|
+
threshold = self.harm_thresholds.get(harm_type.value, 0.5)
|
|
612
|
+
proba_dict[harm_type.value] = prob
|
|
613
|
+
thresholds_dict[harm_type.value] = threshold
|
|
614
|
+
|
|
615
|
+
if prob >= threshold:
|
|
616
|
+
active_labels.append(harm_type)
|
|
617
|
+
|
|
618
|
+
return MultilabelResult(
|
|
619
|
+
active_labels=tuple(active_labels),
|
|
620
|
+
probabilities=proba_dict,
|
|
621
|
+
thresholds_used=thresholds_dict,
|
|
622
|
+
)
|
|
623
|
+
|
|
624
|
+
def _build_predictions(
|
|
625
|
+
self,
|
|
626
|
+
classification: GemmaClassificationResult,
|
|
627
|
+
text: str,
|
|
628
|
+
voting_result: VotingResult | None = None,
|
|
629
|
+
) -> list[L2Prediction]:
|
|
630
|
+
"""Build L2Predictions from classification result."""
|
|
631
|
+
if not classification.is_threat:
|
|
632
|
+
return []
|
|
633
|
+
|
|
634
|
+
# Map family to L2ThreatType
|
|
635
|
+
threat_type = L2ThreatType.from_family(classification.threat_family.value)
|
|
636
|
+
|
|
637
|
+
# Build explanation
|
|
638
|
+
explanation_parts = [
|
|
639
|
+
f"Detected {classification.threat_family.value} threat",
|
|
640
|
+
f"with {classification.severity.value} severity",
|
|
641
|
+
]
|
|
642
|
+
if classification.primary_technique:
|
|
643
|
+
explanation_parts.append(
|
|
644
|
+
f"using {classification.primary_technique.value} technique"
|
|
645
|
+
)
|
|
646
|
+
if classification.harm_types and classification.harm_types.has_active_labels:
|
|
647
|
+
harm_labels = [h.value for h in classification.harm_types.active_labels]
|
|
648
|
+
explanation_parts.append(f"causing potential harm: {', '.join(harm_labels)}")
|
|
649
|
+
|
|
650
|
+
explanation = " ".join(explanation_parts)
|
|
651
|
+
|
|
652
|
+
# Build metadata
|
|
653
|
+
# Calculate classification label based on confidence
|
|
654
|
+
if classification.threat_probability >= 0.9:
|
|
655
|
+
classification_label = "HIGH_THREAT"
|
|
656
|
+
action = "BLOCK_ALERT"
|
|
657
|
+
elif classification.threat_probability >= 0.75:
|
|
658
|
+
classification_label = "THREAT"
|
|
659
|
+
action = "BLOCK"
|
|
660
|
+
elif classification.threat_probability >= 0.6:
|
|
661
|
+
classification_label = "LIKELY_THREAT"
|
|
662
|
+
action = "BLOCK_WITH_REVIEW"
|
|
663
|
+
elif classification.threat_probability >= 0.4:
|
|
664
|
+
classification_label = "REVIEW"
|
|
665
|
+
action = "MANUAL_REVIEW"
|
|
666
|
+
else:
|
|
667
|
+
classification_label = "FP_LIKELY"
|
|
668
|
+
action = "ALLOW_WITH_LOG"
|
|
669
|
+
|
|
670
|
+
metadata: dict[str, Any] = {
|
|
671
|
+
"is_attack": True,
|
|
672
|
+
"family": classification.threat_family.value,
|
|
673
|
+
"severity": classification.severity.value,
|
|
674
|
+
"classification": classification_label,
|
|
675
|
+
"action": action,
|
|
676
|
+
"risk_score": classification.threat_probability * 100,
|
|
677
|
+
"hierarchical_score": classification.threat_probability,
|
|
678
|
+
"scores": {
|
|
679
|
+
"attack_probability": classification.threat_probability,
|
|
680
|
+
"family_confidence": classification.family_confidence,
|
|
681
|
+
"severity_confidence": classification.severity_confidence,
|
|
682
|
+
"subfamily_confidence": classification.technique_confidence,
|
|
683
|
+
},
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
if classification.primary_technique:
|
|
687
|
+
metadata["primary_technique"] = classification.primary_technique.value
|
|
688
|
+
metadata["technique_confidence"] = classification.technique_confidence
|
|
689
|
+
# Alias for CLI formatter compatibility
|
|
690
|
+
metadata["sub_family"] = classification.primary_technique.value
|
|
691
|
+
|
|
692
|
+
if classification.harm_types:
|
|
693
|
+
metadata["harm_types"] = classification.harm_types.to_dict()
|
|
694
|
+
|
|
695
|
+
# Generate "why_it_hit" explanations
|
|
696
|
+
why_it_hit = self._generate_why_it_hit(classification)
|
|
697
|
+
metadata["why_it_hit"] = why_it_hit
|
|
698
|
+
|
|
699
|
+
# Generate recommended actions
|
|
700
|
+
recommended_actions = self._generate_recommended_actions(classification)
|
|
701
|
+
metadata["recommended_action"] = recommended_actions
|
|
702
|
+
|
|
703
|
+
# Uncertainty flag
|
|
704
|
+
metadata["uncertain"] = (
|
|
705
|
+
classification.family_confidence < 0.5 or
|
|
706
|
+
classification.threat_probability < 0.6
|
|
707
|
+
)
|
|
708
|
+
|
|
709
|
+
# Add voting information if available
|
|
710
|
+
if voting_result:
|
|
711
|
+
metadata["voting"] = {
|
|
712
|
+
"decision": voting_result.decision.value,
|
|
713
|
+
"confidence": voting_result.confidence,
|
|
714
|
+
"preset_used": voting_result.preset_used,
|
|
715
|
+
"decision_rule_triggered": voting_result.decision_rule_triggered,
|
|
716
|
+
"threat_vote_count": voting_result.threat_vote_count,
|
|
717
|
+
"safe_vote_count": voting_result.safe_vote_count,
|
|
718
|
+
"abstain_vote_count": voting_result.abstain_vote_count,
|
|
719
|
+
"weighted_ratio": voting_result.weighted_ratio,
|
|
720
|
+
}
|
|
721
|
+
# Update classification and action from voting
|
|
722
|
+
metadata["classification"] = self._voting_decision_to_classification(
|
|
723
|
+
voting_result.decision, voting_result.confidence
|
|
724
|
+
)
|
|
725
|
+
metadata["action"] = self._voting_decision_to_action(voting_result.decision)
|
|
726
|
+
|
|
727
|
+
return [
|
|
728
|
+
L2Prediction(
|
|
729
|
+
threat_type=threat_type,
|
|
730
|
+
confidence=classification.threat_probability,
|
|
731
|
+
explanation=explanation,
|
|
732
|
+
features_used=[
|
|
733
|
+
f"family={classification.threat_family.value}",
|
|
734
|
+
f"severity={classification.severity.value}",
|
|
735
|
+
f"technique={classification.primary_technique.value if classification.primary_technique else 'none'}", # noqa: E501
|
|
736
|
+
"model=gemma-compact",
|
|
737
|
+
],
|
|
738
|
+
metadata=metadata,
|
|
739
|
+
)
|
|
740
|
+
]
|
|
741
|
+
|
|
742
|
+
def _generate_why_it_hit(
|
|
743
|
+
self, classification: GemmaClassificationResult
|
|
744
|
+
) -> list[str]:
|
|
745
|
+
"""Generate human-readable explanations for detection."""
|
|
746
|
+
reasons = []
|
|
747
|
+
|
|
748
|
+
# Threat family reason
|
|
749
|
+
family_descriptions = {
|
|
750
|
+
"prompt_injection": "Prompt injection attempting to override instructions",
|
|
751
|
+
"jailbreak": "Jailbreak attempt to bypass safety guidelines",
|
|
752
|
+
"data_exfiltration": "Data exfiltration pattern detected",
|
|
753
|
+
"encoding_or_obfuscation_attack": "Encoded or obfuscated malicious content",
|
|
754
|
+
"rag_or_context_attack": "RAG or context manipulation attack",
|
|
755
|
+
"tool_or_command_abuse": "Tool or command abuse attempt",
|
|
756
|
+
"toxic_or_policy_violating_content": "Toxic or policy-violating content",
|
|
757
|
+
"other_security": "Security-related threat pattern detected",
|
|
758
|
+
}
|
|
759
|
+
family_desc = family_descriptions.get(
|
|
760
|
+
classification.threat_family.value,
|
|
761
|
+
f"{classification.threat_family.value} threat detected"
|
|
762
|
+
)
|
|
763
|
+
reasons.append(family_desc)
|
|
764
|
+
|
|
765
|
+
# Severity reason
|
|
766
|
+
if classification.severity in (Severity.HIGH, Severity.CRITICAL):
|
|
767
|
+
reasons.append(
|
|
768
|
+
f"{classification.severity.value.upper()} severity - immediate action recommended"
|
|
769
|
+
)
|
|
770
|
+
|
|
771
|
+
# Technique reason
|
|
772
|
+
has_technique = (
|
|
773
|
+
classification.primary_technique
|
|
774
|
+
and classification.primary_technique != PrimaryTechnique.NONE
|
|
775
|
+
)
|
|
776
|
+
if has_technique:
|
|
777
|
+
technique_desc = classification.primary_technique.value.replace("_", " ")
|
|
778
|
+
reasons.append(f"Specific attack technique: {technique_desc}")
|
|
779
|
+
|
|
780
|
+
# Harm types reason
|
|
781
|
+
if classification.harm_types and classification.harm_types.has_active_labels:
|
|
782
|
+
harm_count = classification.harm_types.active_count
|
|
783
|
+
if harm_count == 1:
|
|
784
|
+
harm_label = classification.harm_types.active_labels[0].value.replace("_", " ")
|
|
785
|
+
reasons.append(f"Potential harm type: {harm_label}")
|
|
786
|
+
else:
|
|
787
|
+
reasons.append(f"Multiple harm types detected ({harm_count} categories)")
|
|
788
|
+
|
|
789
|
+
return reasons
|
|
790
|
+
|
|
791
|
+
def _generate_recommended_actions(
|
|
792
|
+
self, classification: GemmaClassificationResult
|
|
793
|
+
) -> list[str]:
|
|
794
|
+
"""Generate recommended actions based on classification."""
|
|
795
|
+
actions = []
|
|
796
|
+
|
|
797
|
+
if classification.severity == Severity.CRITICAL:
|
|
798
|
+
actions.append("CRITICAL threat - BLOCK immediately and alert security team")
|
|
799
|
+
elif classification.severity == Severity.HIGH:
|
|
800
|
+
actions.append("HIGH severity - BLOCK and log for review")
|
|
801
|
+
elif classification.severity == Severity.MEDIUM:
|
|
802
|
+
actions.append("MEDIUM severity - Consider blocking or require additional validation")
|
|
803
|
+
elif classification.severity == Severity.LOW:
|
|
804
|
+
actions.append("LOW severity - Log and monitor, allow with caution")
|
|
805
|
+
else:
|
|
806
|
+
actions.append("Minimal severity - Allow with logging")
|
|
807
|
+
|
|
808
|
+
# Family-specific recommendations
|
|
809
|
+
if classification.threat_family == ThreatFamily.DATA_EXFILTRATION:
|
|
810
|
+
actions.append("Review data access controls and implement DLP policies")
|
|
811
|
+
elif classification.threat_family == ThreatFamily.JAILBREAK:
|
|
812
|
+
actions.append("Block and update guardrails to prevent similar attempts")
|
|
813
|
+
elif classification.threat_family == ThreatFamily.PROMPT_INJECTION:
|
|
814
|
+
actions.append("Sanitize input and validate instruction boundaries")
|
|
815
|
+
|
|
816
|
+
return actions
|
|
817
|
+
|
|
818
|
+
def _apply_scorer(
|
|
819
|
+
self, classification: GemmaClassificationResult, text: str
|
|
820
|
+
) -> Any:
|
|
821
|
+
"""Apply hierarchical threat scorer if available."""
|
|
822
|
+
if not self.scorer:
|
|
823
|
+
return None
|
|
824
|
+
|
|
825
|
+
try:
|
|
826
|
+
from raxe.domain.ml.scoring_models import ThreatScore
|
|
827
|
+
|
|
828
|
+
# Map to ThreatScore format
|
|
829
|
+
threat_score = ThreatScore(
|
|
830
|
+
binary_threat_score=classification.threat_probability,
|
|
831
|
+
binary_safe_score=classification.safe_probability,
|
|
832
|
+
family_confidence=classification.family_confidence,
|
|
833
|
+
subfamily_confidence=classification.technique_confidence,
|
|
834
|
+
binary_proba=[
|
|
835
|
+
classification.safe_probability,
|
|
836
|
+
classification.threat_probability,
|
|
837
|
+
],
|
|
838
|
+
family_proba=list(classification.family_probabilities),
|
|
839
|
+
subfamily_proba=list(classification.technique_probabilities or [0.0]),
|
|
840
|
+
family_name=classification.threat_family.value,
|
|
841
|
+
subfamily_name=(
|
|
842
|
+
classification.primary_technique.value
|
|
843
|
+
if classification.primary_technique
|
|
844
|
+
else None
|
|
845
|
+
),
|
|
846
|
+
)
|
|
847
|
+
|
|
848
|
+
return self.scorer.score(threat_score, prompt=text)
|
|
849
|
+
except Exception as e:
|
|
850
|
+
logger.warning("Scorer failed", error=str(e))
|
|
851
|
+
return None
|
|
852
|
+
|
|
853
|
+
@staticmethod
|
|
854
|
+
def _softmax(x: np.ndarray) -> np.ndarray:
|
|
855
|
+
"""Apply softmax to logits."""
|
|
856
|
+
exp_x = np.exp(x - np.max(x))
|
|
857
|
+
return exp_x / exp_x.sum()
|
|
858
|
+
|
|
859
|
+
@staticmethod
|
|
860
|
+
def _sigmoid(x: np.ndarray) -> np.ndarray:
|
|
861
|
+
"""Apply sigmoid to logits."""
|
|
862
|
+
return 1 / (1 + np.exp(-x))
|
|
863
|
+
|
|
864
|
+
@property
|
|
865
|
+
def model_info(self) -> dict[str, Any]:
|
|
866
|
+
"""Return model information."""
|
|
867
|
+
return {
|
|
868
|
+
"name": "Gemma 5-Head Multilabel Classifier",
|
|
869
|
+
"version": self.VERSION,
|
|
870
|
+
"type": "onnx",
|
|
871
|
+
"is_stub": False,
|
|
872
|
+
"size_mb": 300, # Approximate
|
|
873
|
+
"latency_p95_ms": 50,
|
|
874
|
+
"embedding_model": "google/embeddinggemma-300m",
|
|
875
|
+
"embedding_dim": self.EMBEDDING_DIM,
|
|
876
|
+
"heads": [
|
|
877
|
+
"is_threat",
|
|
878
|
+
"threat_family",
|
|
879
|
+
"severity",
|
|
880
|
+
"primary_technique",
|
|
881
|
+
"harm_types",
|
|
882
|
+
],
|
|
883
|
+
"families": [f.value for f in ThreatFamily],
|
|
884
|
+
"description": (
|
|
885
|
+
"Gemma-based 5-head multilabel classifier with EmbeddingGemma-300M "
|
|
886
|
+
"for threat detection, family classification, severity assessment, "
|
|
887
|
+
"technique identification, and harm type prediction."
|
|
888
|
+
),
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
|
|
892
|
+
def create_gemma_detector(
|
|
893
|
+
model_dir: str | Path,
|
|
894
|
+
*,
|
|
895
|
+
confidence_threshold: float = 0.5,
|
|
896
|
+
harm_thresholds: dict[str, float] | None = None,
|
|
897
|
+
cache_size: int = 1000,
|
|
898
|
+
scorer: Any | None = None,
|
|
899
|
+
voting_preset: str | None = None,
|
|
900
|
+
) -> GemmaL2Detector:
|
|
901
|
+
"""Factory function to create Gemma L2 detector.
|
|
902
|
+
|
|
903
|
+
Args:
|
|
904
|
+
model_dir: Path to directory containing ONNX models
|
|
905
|
+
confidence_threshold: Threshold for binary threat classification
|
|
906
|
+
harm_thresholds: Per-class thresholds for harm_types multilabel
|
|
907
|
+
cache_size: Size of embedding cache (0 to disable)
|
|
908
|
+
scorer: Optional HierarchicalThreatScorer instance
|
|
909
|
+
voting_preset: Voting preset override (balanced, high_security, low_fp)
|
|
910
|
+
|
|
911
|
+
Returns:
|
|
912
|
+
GemmaL2Detector instance
|
|
913
|
+
"""
|
|
914
|
+
return GemmaL2Detector(
|
|
915
|
+
model_dir=model_dir,
|
|
916
|
+
confidence_threshold=confidence_threshold,
|
|
917
|
+
harm_thresholds=harm_thresholds,
|
|
918
|
+
cache_size=cache_size,
|
|
919
|
+
scorer=scorer,
|
|
920
|
+
voting_preset=voting_preset,
|
|
921
|
+
)
|