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,801 @@
|
|
|
1
|
+
"""Suppression system for managing false positives - Domain Layer (PURE).
|
|
2
|
+
|
|
3
|
+
This module provides the PURE business logic for suppressions:
|
|
4
|
+
- Value objects (Suppression, SuppressionAction, SuppressionCheckResult)
|
|
5
|
+
- Pure matching logic (check_suppressions function)
|
|
6
|
+
- Repository protocol (no implementation)
|
|
7
|
+
- Manager orchestration (accepts repositories via DI)
|
|
8
|
+
|
|
9
|
+
NO I/O operations in this layer:
|
|
10
|
+
- NO database calls
|
|
11
|
+
- NO file operations
|
|
12
|
+
- NO network requests
|
|
13
|
+
- NO logging (pass results up to application layer)
|
|
14
|
+
"""
|
|
15
|
+
import fnmatch
|
|
16
|
+
import re
|
|
17
|
+
from dataclasses import dataclass, field
|
|
18
|
+
from datetime import datetime, timedelta, timezone
|
|
19
|
+
from enum import Enum
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from typing import Any, Protocol
|
|
22
|
+
|
|
23
|
+
# =============================================================================
|
|
24
|
+
# Security Limits (HARDENED)
|
|
25
|
+
# =============================================================================
|
|
26
|
+
# These limits prevent denial-of-service and resource exhaustion attacks.
|
|
27
|
+
|
|
28
|
+
MAX_PATTERN_LENGTH: int = 256
|
|
29
|
+
"""Maximum allowed length for suppression patterns.
|
|
30
|
+
|
|
31
|
+
Prevents ReDoS attacks and excessive memory usage from maliciously long patterns.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
MAX_REASON_LENGTH: int = 500
|
|
35
|
+
"""Maximum allowed length for suppression reasons.
|
|
36
|
+
|
|
37
|
+
Prevents storage bloat and ensures reasons remain human-readable.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
MAX_SUPPRESSIONS: int = 1000
|
|
41
|
+
"""Maximum number of suppressions allowed.
|
|
42
|
+
|
|
43
|
+
Prevents performance degradation from excessive suppression rules.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class SuppressionAction(Enum):
|
|
48
|
+
"""Action to take when a suppression matches a detection.
|
|
49
|
+
|
|
50
|
+
Attributes:
|
|
51
|
+
SUPPRESS: Remove detection from results entirely (default behavior)
|
|
52
|
+
FLAG: Keep detection in results but mark it as flagged for review
|
|
53
|
+
LOG: Keep detection in results, intended for logging/metrics only
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
SUPPRESS = "SUPPRESS"
|
|
57
|
+
FLAG = "FLAG"
|
|
58
|
+
LOG = "LOG"
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
# Known valid family prefixes for wildcard validation
|
|
62
|
+
# These match the RuleFamily enum values (case-insensitive)
|
|
63
|
+
VALID_FAMILY_PREFIXES = frozenset({
|
|
64
|
+
"pi", # Prompt Injection
|
|
65
|
+
"jb", # Jailbreak
|
|
66
|
+
"pii", # PII/Data Leak
|
|
67
|
+
"cmd", # Command Injection
|
|
68
|
+
"enc", # Encoding/Obfuscation Attacks
|
|
69
|
+
"rag", # RAG-specific Attacks
|
|
70
|
+
"hc", # Harmful Content
|
|
71
|
+
"sec", # Security
|
|
72
|
+
"qual", # Quality
|
|
73
|
+
"custom", # User-defined
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class SuppressionValidationError(ValueError):
|
|
78
|
+
"""Raised when suppression validation fails.
|
|
79
|
+
|
|
80
|
+
This is a domain exception for invalid suppression configuration.
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
pass
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _validate_pattern(pattern: str) -> None:
|
|
87
|
+
"""Validate suppression pattern (PURE - raises on invalid).
|
|
88
|
+
|
|
89
|
+
Rules:
|
|
90
|
+
1. Pattern cannot be empty
|
|
91
|
+
2. Pattern cannot be bare wildcard '*' (must have family prefix)
|
|
92
|
+
3. Wildcards must have a valid family prefix (e.g., 'pi-*', not '*-injection')
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
pattern: The pattern to validate
|
|
96
|
+
|
|
97
|
+
Raises:
|
|
98
|
+
SuppressionValidationError: If pattern is invalid
|
|
99
|
+
"""
|
|
100
|
+
if not pattern:
|
|
101
|
+
raise SuppressionValidationError("Pattern cannot be empty")
|
|
102
|
+
|
|
103
|
+
# Reject bare wildcards
|
|
104
|
+
if pattern == "*":
|
|
105
|
+
raise SuppressionValidationError(
|
|
106
|
+
"Bare wildcard '*' not allowed. Use a family prefix like 'pi-*' or 'jb-*'"
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
# If pattern contains wildcard, validate it has proper structure
|
|
110
|
+
if "*" in pattern:
|
|
111
|
+
# Pattern must start with a valid family prefix before any wildcard
|
|
112
|
+
# Valid: pi-*, jb-*, pi-00*, pi-*-basic
|
|
113
|
+
# Invalid: *-injection, *pi-001, *
|
|
114
|
+
|
|
115
|
+
# Check if pattern starts with wildcard (suffix-only pattern)
|
|
116
|
+
if pattern.startswith("*"):
|
|
117
|
+
raise SuppressionValidationError(
|
|
118
|
+
f"Pattern '{pattern}' starts with wildcard. "
|
|
119
|
+
f"Wildcards must have a family prefix like 'pi-*' or 'jb-*'"
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
# Extract the prefix before the first hyphen or wildcard
|
|
123
|
+
prefix_match = re.match(r"^([a-zA-Z]+)", pattern)
|
|
124
|
+
if not prefix_match:
|
|
125
|
+
raise SuppressionValidationError(
|
|
126
|
+
f"Pattern with wildcard must start with a family prefix. "
|
|
127
|
+
f"Valid prefixes: {', '.join(sorted(VALID_FAMILY_PREFIXES))}"
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
prefix = prefix_match.group(1).lower()
|
|
131
|
+
if prefix not in VALID_FAMILY_PREFIXES:
|
|
132
|
+
raise SuppressionValidationError(
|
|
133
|
+
f"Unknown family prefix '{prefix}'. "
|
|
134
|
+
f"Valid prefixes: {', '.join(sorted(VALID_FAMILY_PREFIXES))}"
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _validate_iso_datetime(value: str, field_name: str) -> None:
|
|
139
|
+
"""Validate ISO datetime format (PURE - raises on invalid).
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
value: The datetime string to validate
|
|
143
|
+
field_name: Name of field for error messages
|
|
144
|
+
|
|
145
|
+
Raises:
|
|
146
|
+
SuppressionValidationError: If format is invalid
|
|
147
|
+
"""
|
|
148
|
+
try:
|
|
149
|
+
datetime.fromisoformat(value)
|
|
150
|
+
except ValueError as e:
|
|
151
|
+
raise SuppressionValidationError(
|
|
152
|
+
f"{field_name} must be valid ISO format, got '{value}': {e}"
|
|
153
|
+
) from e
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
@dataclass(frozen=True)
|
|
157
|
+
class Suppression:
|
|
158
|
+
"""A single suppression entry (Value Object - Immutable).
|
|
159
|
+
|
|
160
|
+
Suppressions allow managing false positives by matching detection rule IDs
|
|
161
|
+
against patterns and taking specified actions.
|
|
162
|
+
|
|
163
|
+
Validation Rules:
|
|
164
|
+
- Pattern cannot be empty
|
|
165
|
+
- Pattern cannot be bare wildcard '*'
|
|
166
|
+
- Wildcards must have valid family prefix (pi-*, jb-*, etc.)
|
|
167
|
+
- Reason is REQUIRED and cannot be empty
|
|
168
|
+
- Dates must be valid ISO format if provided
|
|
169
|
+
|
|
170
|
+
Attributes:
|
|
171
|
+
pattern: Rule ID pattern (supports wildcards: pi-*, jb-*-basic)
|
|
172
|
+
reason: Human-readable reason for suppression (REQUIRED)
|
|
173
|
+
action: What to do when suppression matches (default: SUPPRESS)
|
|
174
|
+
expires_at: When suppression expires (ISO format, optional)
|
|
175
|
+
created_at: When suppression was created (ISO format, optional)
|
|
176
|
+
created_by: Who created the suppression (optional)
|
|
177
|
+
"""
|
|
178
|
+
|
|
179
|
+
pattern: str
|
|
180
|
+
reason: str
|
|
181
|
+
action: SuppressionAction = SuppressionAction.SUPPRESS
|
|
182
|
+
expires_at: str | None = None
|
|
183
|
+
created_at: str | None = None
|
|
184
|
+
created_by: str | None = None
|
|
185
|
+
|
|
186
|
+
def __post_init__(self) -> None:
|
|
187
|
+
"""Validate suppression after construction.
|
|
188
|
+
|
|
189
|
+
Raises:
|
|
190
|
+
SuppressionValidationError: If any field fails validation
|
|
191
|
+
"""
|
|
192
|
+
# Validate pattern (no bare wildcards, must have family prefix)
|
|
193
|
+
_validate_pattern(self.pattern)
|
|
194
|
+
|
|
195
|
+
# Security: Validate pattern length limit
|
|
196
|
+
if len(self.pattern) > MAX_PATTERN_LENGTH:
|
|
197
|
+
raise SuppressionValidationError(
|
|
198
|
+
f"Pattern exceeds maximum length of {MAX_PATTERN_LENGTH}"
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
# Validate reason is not empty
|
|
202
|
+
if not self.reason or not self.reason.strip():
|
|
203
|
+
raise SuppressionValidationError("Reason cannot be empty")
|
|
204
|
+
|
|
205
|
+
# Security: Validate reason length limit
|
|
206
|
+
if len(self.reason) > MAX_REASON_LENGTH:
|
|
207
|
+
raise SuppressionValidationError(
|
|
208
|
+
f"Reason exceeds maximum length of {MAX_REASON_LENGTH}"
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
# Validate dates if present
|
|
212
|
+
if self.expires_at:
|
|
213
|
+
_validate_iso_datetime(self.expires_at, "expires_at")
|
|
214
|
+
|
|
215
|
+
if self.created_at:
|
|
216
|
+
_validate_iso_datetime(self.created_at, "created_at")
|
|
217
|
+
|
|
218
|
+
def matches(self, rule_id: str) -> bool:
|
|
219
|
+
"""Check if this suppression matches a rule ID.
|
|
220
|
+
|
|
221
|
+
Supports wildcards:
|
|
222
|
+
- pi-* matches pi-001, pi-002, etc.
|
|
223
|
+
- pi-00* matches pi-001, pi-002, etc.
|
|
224
|
+
- jb-*-basic matches jb-regex-basic, jb-pattern-basic, etc.
|
|
225
|
+
|
|
226
|
+
Note: Bare wildcards (*) and suffix-only wildcards (*-injection)
|
|
227
|
+
are not allowed and will fail validation at construction time.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
rule_id: Rule ID to check
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
True if pattern matches rule ID
|
|
234
|
+
"""
|
|
235
|
+
return fnmatch.fnmatch(rule_id, self.pattern)
|
|
236
|
+
|
|
237
|
+
def is_expired(self, *, current_time: datetime | None = None) -> bool:
|
|
238
|
+
"""Check if suppression has expired.
|
|
239
|
+
|
|
240
|
+
Security: Uses FAIL-CLOSED approach. If the expiration date cannot be
|
|
241
|
+
parsed (should not happen due to __post_init__ validation), the
|
|
242
|
+
suppression is treated as EXPIRED to prevent bypassing security controls.
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
current_time: Current time for testing (default: now)
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
True if expires_at is set and in the past, or if date is invalid
|
|
249
|
+
"""
|
|
250
|
+
if not self.expires_at:
|
|
251
|
+
return False
|
|
252
|
+
|
|
253
|
+
try:
|
|
254
|
+
expiry = datetime.fromisoformat(self.expires_at)
|
|
255
|
+
# Ensure timezone-aware comparison
|
|
256
|
+
if expiry.tzinfo is None:
|
|
257
|
+
expiry = expiry.replace(tzinfo=timezone.utc)
|
|
258
|
+
now = current_time or datetime.now(timezone.utc)
|
|
259
|
+
return now > expiry
|
|
260
|
+
except ValueError:
|
|
261
|
+
# FAIL CLOSED - invalid date = expired (security hardening)
|
|
262
|
+
# This prevents suppressions with malformed dates from being active
|
|
263
|
+
return True
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
@dataclass(frozen=True)
|
|
267
|
+
class SuppressionCheckResult:
|
|
268
|
+
"""Result of checking suppressions for a rule (Value Object - Immutable).
|
|
269
|
+
|
|
270
|
+
Used by check_suppressions() and SuppressionManager.check_suppression()
|
|
271
|
+
to return complete information about whether a rule is suppressed.
|
|
272
|
+
|
|
273
|
+
The application layer can use this to:
|
|
274
|
+
- Log warnings for expired suppressions encountered
|
|
275
|
+
- Take appropriate action based on SuppressionAction
|
|
276
|
+
- Track suppression metrics
|
|
277
|
+
|
|
278
|
+
Attributes:
|
|
279
|
+
is_suppressed: True if an active (non-expired) suppression matched
|
|
280
|
+
action: The action to take (SUPPRESS, FLAG, or LOG)
|
|
281
|
+
reason: The reason from the matching suppression
|
|
282
|
+
matched_pattern: The pattern that matched (for audit)
|
|
283
|
+
expired_matches: List of expired suppressions that matched (for logging)
|
|
284
|
+
"""
|
|
285
|
+
|
|
286
|
+
is_suppressed: bool
|
|
287
|
+
action: SuppressionAction = SuppressionAction.SUPPRESS
|
|
288
|
+
reason: str = ""
|
|
289
|
+
matched_pattern: str = ""
|
|
290
|
+
expired_matches: list[Suppression] = field(default_factory=list)
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def check_suppressions(
|
|
294
|
+
rule_id: str,
|
|
295
|
+
suppressions: list[Suppression],
|
|
296
|
+
*,
|
|
297
|
+
current_time: datetime | None = None,
|
|
298
|
+
) -> SuppressionCheckResult:
|
|
299
|
+
"""Check if a rule ID matches any suppression (PURE FUNCTION).
|
|
300
|
+
|
|
301
|
+
This is a pure function that takes data and returns data.
|
|
302
|
+
No I/O, no side effects, easily testable.
|
|
303
|
+
|
|
304
|
+
The function returns expired matches separately so the application
|
|
305
|
+
layer can log warnings about them without violating domain purity.
|
|
306
|
+
|
|
307
|
+
Args:
|
|
308
|
+
rule_id: Rule ID to check
|
|
309
|
+
suppressions: List of suppressions to check against
|
|
310
|
+
current_time: Current time for expiration check (default: now)
|
|
311
|
+
|
|
312
|
+
Returns:
|
|
313
|
+
SuppressionCheckResult with match info and any expired matches
|
|
314
|
+
"""
|
|
315
|
+
expired_matches: list[Suppression] = []
|
|
316
|
+
|
|
317
|
+
for suppression in suppressions:
|
|
318
|
+
if not suppression.matches(rule_id):
|
|
319
|
+
continue
|
|
320
|
+
|
|
321
|
+
# Pattern matched - check expiration
|
|
322
|
+
if suppression.is_expired(current_time=current_time):
|
|
323
|
+
# Track expired match for potential logging by caller
|
|
324
|
+
expired_matches.append(suppression)
|
|
325
|
+
else:
|
|
326
|
+
# Found active match - return immediately
|
|
327
|
+
return SuppressionCheckResult(
|
|
328
|
+
is_suppressed=True,
|
|
329
|
+
action=suppression.action,
|
|
330
|
+
reason=suppression.reason,
|
|
331
|
+
matched_pattern=suppression.pattern,
|
|
332
|
+
expired_matches=expired_matches,
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
# No active match found
|
|
336
|
+
return SuppressionCheckResult(
|
|
337
|
+
is_suppressed=False,
|
|
338
|
+
expired_matches=expired_matches,
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
@dataclass(frozen=True)
|
|
343
|
+
class AuditEntry:
|
|
344
|
+
"""Audit log entry for suppression actions (Value Object - Immutable).
|
|
345
|
+
|
|
346
|
+
Attributes:
|
|
347
|
+
pattern: Pattern that was added/removed/applied
|
|
348
|
+
reason: Reason for action
|
|
349
|
+
action: Action type (added, removed, applied)
|
|
350
|
+
created_at: When action occurred (ISO format)
|
|
351
|
+
scan_id: Scan ID (for applied actions, optional)
|
|
352
|
+
rule_id: Rule ID (for applied actions, optional)
|
|
353
|
+
created_by: Who performed the action (optional)
|
|
354
|
+
metadata: Additional metadata (optional)
|
|
355
|
+
"""
|
|
356
|
+
|
|
357
|
+
pattern: str
|
|
358
|
+
reason: str
|
|
359
|
+
action: str
|
|
360
|
+
created_at: str
|
|
361
|
+
scan_id: int | None = None
|
|
362
|
+
rule_id: str | None = None
|
|
363
|
+
created_by: str | None = None
|
|
364
|
+
metadata: dict[str, Any] | None = None
|
|
365
|
+
|
|
366
|
+
def __post_init__(self) -> None:
|
|
367
|
+
"""Validate audit entry."""
|
|
368
|
+
if self.action not in ("added", "removed", "applied"):
|
|
369
|
+
raise ValueError(f"Invalid action: {self.action}")
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
class SuppressionRepository(Protocol):
|
|
373
|
+
"""Repository interface for suppression persistence (PURE - NO IMPLEMENTATION).
|
|
374
|
+
|
|
375
|
+
This is a Protocol (interface) that defines what operations are needed.
|
|
376
|
+
Infrastructure layer provides concrete implementations.
|
|
377
|
+
"""
|
|
378
|
+
|
|
379
|
+
def load_suppressions(self) -> list[Suppression]:
|
|
380
|
+
"""Load all suppressions from storage.
|
|
381
|
+
|
|
382
|
+
Returns:
|
|
383
|
+
List of Suppression objects
|
|
384
|
+
"""
|
|
385
|
+
...
|
|
386
|
+
|
|
387
|
+
def save_suppression(self, suppression: Suppression) -> None:
|
|
388
|
+
"""Save a suppression to storage.
|
|
389
|
+
|
|
390
|
+
Args:
|
|
391
|
+
suppression: Suppression to save
|
|
392
|
+
"""
|
|
393
|
+
...
|
|
394
|
+
|
|
395
|
+
def remove_suppression(self, pattern: str) -> bool:
|
|
396
|
+
"""Remove a suppression from storage.
|
|
397
|
+
|
|
398
|
+
Args:
|
|
399
|
+
pattern: Pattern to remove
|
|
400
|
+
|
|
401
|
+
Returns:
|
|
402
|
+
True if removed, False if not found
|
|
403
|
+
"""
|
|
404
|
+
...
|
|
405
|
+
|
|
406
|
+
def save_all_suppressions(self, suppressions: list[Suppression]) -> None:
|
|
407
|
+
"""Replace all suppressions in storage.
|
|
408
|
+
|
|
409
|
+
Args:
|
|
410
|
+
suppressions: List of suppressions to save
|
|
411
|
+
"""
|
|
412
|
+
...
|
|
413
|
+
|
|
414
|
+
def log_audit(self, entry: AuditEntry) -> None:
|
|
415
|
+
"""Log an audit entry.
|
|
416
|
+
|
|
417
|
+
Args:
|
|
418
|
+
entry: Audit entry to log
|
|
419
|
+
"""
|
|
420
|
+
...
|
|
421
|
+
|
|
422
|
+
def get_audit_log(
|
|
423
|
+
self,
|
|
424
|
+
limit: int = 100,
|
|
425
|
+
pattern: str | None = None,
|
|
426
|
+
action: str | None = None,
|
|
427
|
+
) -> list[dict[str, Any]]:
|
|
428
|
+
"""Get audit log entries.
|
|
429
|
+
|
|
430
|
+
Args:
|
|
431
|
+
limit: Maximum entries to return
|
|
432
|
+
pattern: Filter by pattern (optional)
|
|
433
|
+
action: Filter by action (added/removed/applied, optional)
|
|
434
|
+
|
|
435
|
+
Returns:
|
|
436
|
+
List of audit log entries as dictionaries
|
|
437
|
+
"""
|
|
438
|
+
...
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
class SuppressionManager:
|
|
442
|
+
"""Manages suppressions for false positive handling (PURE ORCHESTRATION).
|
|
443
|
+
|
|
444
|
+
This class contains ONLY pure business logic - no I/O operations.
|
|
445
|
+
All persistence is delegated to the injected repository.
|
|
446
|
+
|
|
447
|
+
Features:
|
|
448
|
+
- Check if rules are suppressed with action-based handling
|
|
449
|
+
- Add/remove suppressions (delegates to repository)
|
|
450
|
+
- Track active suppressions in memory
|
|
451
|
+
- Wildcard pattern support with family prefix requirement
|
|
452
|
+
"""
|
|
453
|
+
|
|
454
|
+
def __init__(
|
|
455
|
+
self,
|
|
456
|
+
repository: SuppressionRepository,
|
|
457
|
+
*,
|
|
458
|
+
auto_load: bool = True,
|
|
459
|
+
):
|
|
460
|
+
"""Initialize suppression manager with dependency injection.
|
|
461
|
+
|
|
462
|
+
Args:
|
|
463
|
+
repository: Repository for persistence (injected)
|
|
464
|
+
auto_load: Automatically load suppressions from repository on init
|
|
465
|
+
"""
|
|
466
|
+
self._repository = repository
|
|
467
|
+
self._suppressions: dict[str, Suppression] = {}
|
|
468
|
+
|
|
469
|
+
# Auto-load from repository if requested
|
|
470
|
+
if auto_load:
|
|
471
|
+
self._load_from_repository()
|
|
472
|
+
|
|
473
|
+
def _load_from_repository(self) -> None:
|
|
474
|
+
"""Load suppressions from repository into memory."""
|
|
475
|
+
suppressions = self._repository.load_suppressions()
|
|
476
|
+
for suppression in suppressions:
|
|
477
|
+
self._suppressions[suppression.pattern] = suppression
|
|
478
|
+
|
|
479
|
+
def add_suppression(
|
|
480
|
+
self,
|
|
481
|
+
pattern: str,
|
|
482
|
+
reason: str,
|
|
483
|
+
*,
|
|
484
|
+
action: SuppressionAction = SuppressionAction.SUPPRESS,
|
|
485
|
+
created_by: str | None = None,
|
|
486
|
+
expires_at: str | None = None,
|
|
487
|
+
log_to_audit: bool = True,
|
|
488
|
+
) -> Suppression:
|
|
489
|
+
"""Add a suppression.
|
|
490
|
+
|
|
491
|
+
Args:
|
|
492
|
+
pattern: Rule ID pattern (supports wildcards with family prefix)
|
|
493
|
+
reason: Reason for suppression (REQUIRED)
|
|
494
|
+
action: What action to take when matched (default: SUPPRESS)
|
|
495
|
+
created_by: Who created it (default: "api")
|
|
496
|
+
expires_at: When it expires (ISO format, optional)
|
|
497
|
+
log_to_audit: Whether to log to audit (default: True)
|
|
498
|
+
|
|
499
|
+
Returns:
|
|
500
|
+
Created Suppression object
|
|
501
|
+
|
|
502
|
+
Raises:
|
|
503
|
+
SuppressionValidationError: If pattern or reason is invalid
|
|
504
|
+
ValueError: If maximum suppression limit is reached
|
|
505
|
+
"""
|
|
506
|
+
# Security: Check maximum suppressions limit (only for new patterns)
|
|
507
|
+
if pattern not in self._suppressions and len(self._suppressions) >= MAX_SUPPRESSIONS:
|
|
508
|
+
raise ValueError(
|
|
509
|
+
f"Maximum suppression limit ({MAX_SUPPRESSIONS}) reached"
|
|
510
|
+
)
|
|
511
|
+
|
|
512
|
+
suppression = Suppression(
|
|
513
|
+
pattern=pattern,
|
|
514
|
+
reason=reason,
|
|
515
|
+
action=action,
|
|
516
|
+
created_at=datetime.now(timezone.utc).isoformat(),
|
|
517
|
+
created_by=created_by or "api",
|
|
518
|
+
expires_at=expires_at,
|
|
519
|
+
)
|
|
520
|
+
|
|
521
|
+
# Store in memory
|
|
522
|
+
self._suppressions[pattern] = suppression
|
|
523
|
+
|
|
524
|
+
# Persist to repository
|
|
525
|
+
self._repository.save_suppression(suppression)
|
|
526
|
+
|
|
527
|
+
# Log to audit
|
|
528
|
+
if log_to_audit:
|
|
529
|
+
audit_entry = AuditEntry(
|
|
530
|
+
pattern=pattern,
|
|
531
|
+
reason=reason,
|
|
532
|
+
action="added",
|
|
533
|
+
created_at=datetime.now(timezone.utc).isoformat(),
|
|
534
|
+
created_by=created_by,
|
|
535
|
+
metadata={"suppression_action": action.value},
|
|
536
|
+
)
|
|
537
|
+
self._repository.log_audit(audit_entry)
|
|
538
|
+
|
|
539
|
+
return suppression
|
|
540
|
+
|
|
541
|
+
def remove_suppression(
|
|
542
|
+
self,
|
|
543
|
+
pattern: str,
|
|
544
|
+
*,
|
|
545
|
+
created_by: str | None = None,
|
|
546
|
+
) -> bool:
|
|
547
|
+
"""Remove a suppression.
|
|
548
|
+
|
|
549
|
+
Args:
|
|
550
|
+
pattern: Pattern to remove (exact match)
|
|
551
|
+
created_by: Who removed it (for audit)
|
|
552
|
+
|
|
553
|
+
Returns:
|
|
554
|
+
True if removed, False if not found
|
|
555
|
+
"""
|
|
556
|
+
if pattern not in self._suppressions:
|
|
557
|
+
return False
|
|
558
|
+
|
|
559
|
+
suppression = self._suppressions.pop(pattern)
|
|
560
|
+
|
|
561
|
+
# Remove from repository
|
|
562
|
+
self._repository.remove_suppression(pattern)
|
|
563
|
+
|
|
564
|
+
# Log to audit
|
|
565
|
+
audit_entry = AuditEntry(
|
|
566
|
+
pattern=pattern,
|
|
567
|
+
reason=suppression.reason,
|
|
568
|
+
action="removed",
|
|
569
|
+
created_at=datetime.now(timezone.utc).isoformat(),
|
|
570
|
+
created_by=created_by,
|
|
571
|
+
)
|
|
572
|
+
self._repository.log_audit(audit_entry)
|
|
573
|
+
|
|
574
|
+
return True
|
|
575
|
+
|
|
576
|
+
def check_suppression(
|
|
577
|
+
self,
|
|
578
|
+
rule_id: str,
|
|
579
|
+
*,
|
|
580
|
+
current_time: datetime | None = None,
|
|
581
|
+
) -> SuppressionCheckResult:
|
|
582
|
+
"""Check if a rule is suppressed and get full result info (PURE LOGIC).
|
|
583
|
+
|
|
584
|
+
This is the recommended method for checking suppressions as it returns
|
|
585
|
+
complete information including action and expired matches.
|
|
586
|
+
|
|
587
|
+
Args:
|
|
588
|
+
rule_id: Rule ID to check
|
|
589
|
+
current_time: Current time for testing (default: now)
|
|
590
|
+
|
|
591
|
+
Returns:
|
|
592
|
+
SuppressionCheckResult with full match info
|
|
593
|
+
"""
|
|
594
|
+
return check_suppressions(
|
|
595
|
+
rule_id,
|
|
596
|
+
list(self._suppressions.values()),
|
|
597
|
+
current_time=current_time,
|
|
598
|
+
)
|
|
599
|
+
|
|
600
|
+
def is_suppressed(
|
|
601
|
+
self, rule_id: str, *, current_time: datetime | None = None
|
|
602
|
+
) -> tuple[bool, str]:
|
|
603
|
+
"""Check if a rule is suppressed (PURE LOGIC).
|
|
604
|
+
|
|
605
|
+
Backward-compatible method that returns simple tuple.
|
|
606
|
+
For new code, prefer check_suppression() which returns full info.
|
|
607
|
+
|
|
608
|
+
Args:
|
|
609
|
+
rule_id: Rule ID to check
|
|
610
|
+
current_time: Current time for testing (default: now)
|
|
611
|
+
|
|
612
|
+
Returns:
|
|
613
|
+
Tuple of (is_suppressed, reason)
|
|
614
|
+
- If suppressed: (True, "Reason for suppression")
|
|
615
|
+
- If not suppressed: (False, "")
|
|
616
|
+
"""
|
|
617
|
+
result = self.check_suppression(rule_id, current_time=current_time)
|
|
618
|
+
return result.is_suppressed, result.reason
|
|
619
|
+
|
|
620
|
+
def log_suppression_applied(
|
|
621
|
+
self,
|
|
622
|
+
rule_id: str,
|
|
623
|
+
reason: str,
|
|
624
|
+
*,
|
|
625
|
+
scan_id: int | None = None,
|
|
626
|
+
action: SuppressionAction = SuppressionAction.SUPPRESS,
|
|
627
|
+
) -> None:
|
|
628
|
+
"""Log that a suppression was applied during a scan.
|
|
629
|
+
|
|
630
|
+
Args:
|
|
631
|
+
rule_id: Rule ID that was suppressed
|
|
632
|
+
reason: Reason for suppression
|
|
633
|
+
scan_id: Scan ID (if applicable)
|
|
634
|
+
action: The action that was taken
|
|
635
|
+
"""
|
|
636
|
+
audit_entry = AuditEntry(
|
|
637
|
+
pattern=rule_id,
|
|
638
|
+
reason=reason,
|
|
639
|
+
action="applied",
|
|
640
|
+
created_at=datetime.now(timezone.utc).isoformat(),
|
|
641
|
+
scan_id=scan_id,
|
|
642
|
+
rule_id=rule_id,
|
|
643
|
+
metadata={"suppression_action": action.value},
|
|
644
|
+
)
|
|
645
|
+
self._repository.log_audit(audit_entry)
|
|
646
|
+
|
|
647
|
+
def get_suppressions(
|
|
648
|
+
self, *, current_time: datetime | None = None
|
|
649
|
+
) -> list[Suppression]:
|
|
650
|
+
"""Get all active suppressions (PURE LOGIC).
|
|
651
|
+
|
|
652
|
+
Args:
|
|
653
|
+
current_time: Current time for testing (default: now)
|
|
654
|
+
|
|
655
|
+
Returns:
|
|
656
|
+
List of Suppression objects (excluding expired ones)
|
|
657
|
+
"""
|
|
658
|
+
return [
|
|
659
|
+
s
|
|
660
|
+
for s in self._suppressions.values()
|
|
661
|
+
if not s.is_expired(current_time=current_time)
|
|
662
|
+
]
|
|
663
|
+
|
|
664
|
+
def get_suppression(self, pattern: str) -> Suppression | None:
|
|
665
|
+
"""Get a specific suppression by pattern (PURE LOGIC).
|
|
666
|
+
|
|
667
|
+
Args:
|
|
668
|
+
pattern: Pattern to look up (exact match)
|
|
669
|
+
|
|
670
|
+
Returns:
|
|
671
|
+
Suppression if found, None otherwise
|
|
672
|
+
"""
|
|
673
|
+
return self._suppressions.get(pattern)
|
|
674
|
+
|
|
675
|
+
def get_audit_log(
|
|
676
|
+
self,
|
|
677
|
+
limit: int = 100,
|
|
678
|
+
pattern: str | None = None,
|
|
679
|
+
action: str | None = None,
|
|
680
|
+
) -> list[dict[str, Any]]:
|
|
681
|
+
"""Get audit log entries (delegates to repository).
|
|
682
|
+
|
|
683
|
+
Args:
|
|
684
|
+
limit: Maximum entries to return
|
|
685
|
+
pattern: Filter by pattern (optional)
|
|
686
|
+
action: Filter by action (added/removed/applied, optional)
|
|
687
|
+
|
|
688
|
+
Returns:
|
|
689
|
+
List of audit log entries
|
|
690
|
+
"""
|
|
691
|
+
return self._repository.get_audit_log(
|
|
692
|
+
limit=limit,
|
|
693
|
+
pattern=pattern,
|
|
694
|
+
action=action,
|
|
695
|
+
)
|
|
696
|
+
|
|
697
|
+
def get_statistics(
|
|
698
|
+
self, *, current_time: datetime | None = None
|
|
699
|
+
) -> dict[str, Any]:
|
|
700
|
+
"""Get suppression statistics (delegates to repository + pure logic).
|
|
701
|
+
|
|
702
|
+
Args:
|
|
703
|
+
current_time: Current time for testing (default: now)
|
|
704
|
+
|
|
705
|
+
Returns:
|
|
706
|
+
Dictionary with statistics including counts by action type
|
|
707
|
+
"""
|
|
708
|
+
active_suppressions = self.get_suppressions(current_time=current_time)
|
|
709
|
+
|
|
710
|
+
# Get audit counts from repository
|
|
711
|
+
audit_log = self._repository.get_audit_log(limit=10000) # Get all
|
|
712
|
+
|
|
713
|
+
action_counts: dict[str, int] = {}
|
|
714
|
+
for entry in audit_log:
|
|
715
|
+
audit_action = entry["action"]
|
|
716
|
+
action_counts[audit_action] = action_counts.get(audit_action, 0) + 1
|
|
717
|
+
|
|
718
|
+
# Count recent applications (last 30 days)
|
|
719
|
+
cutoff = (current_time or datetime.now(timezone.utc)) - timedelta(days=30)
|
|
720
|
+
cutoff_str = cutoff.isoformat()
|
|
721
|
+
recent_applications = sum(
|
|
722
|
+
1
|
|
723
|
+
for entry in audit_log
|
|
724
|
+
if entry["action"] == "applied" and entry["created_at"] >= cutoff_str
|
|
725
|
+
)
|
|
726
|
+
|
|
727
|
+
# Count by suppression action type
|
|
728
|
+
by_action_type: dict[str, int] = {}
|
|
729
|
+
for supp in active_suppressions:
|
|
730
|
+
by_action_type[supp.action.value] = (
|
|
731
|
+
by_action_type.get(supp.action.value, 0) + 1
|
|
732
|
+
)
|
|
733
|
+
|
|
734
|
+
return {
|
|
735
|
+
"total_active": len(active_suppressions),
|
|
736
|
+
"total_added": action_counts.get("added", 0),
|
|
737
|
+
"total_removed": action_counts.get("removed", 0),
|
|
738
|
+
"total_applied": action_counts.get("applied", 0),
|
|
739
|
+
"recent_applications_30d": recent_applications,
|
|
740
|
+
"by_action_type": by_action_type,
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
def clear_all(self, *, created_by: str | None = None) -> int:
|
|
744
|
+
"""Clear all suppressions.
|
|
745
|
+
|
|
746
|
+
Args:
|
|
747
|
+
created_by: Who cleared them (for audit)
|
|
748
|
+
|
|
749
|
+
Returns:
|
|
750
|
+
Number of suppressions cleared
|
|
751
|
+
"""
|
|
752
|
+
count = len(self._suppressions)
|
|
753
|
+
|
|
754
|
+
# Log removals
|
|
755
|
+
for pattern, suppression in list(self._suppressions.items()):
|
|
756
|
+
audit_entry = AuditEntry(
|
|
757
|
+
pattern=pattern,
|
|
758
|
+
reason=suppression.reason,
|
|
759
|
+
action="removed",
|
|
760
|
+
created_at=datetime.now(timezone.utc).isoformat(),
|
|
761
|
+
created_by=created_by,
|
|
762
|
+
)
|
|
763
|
+
self._repository.log_audit(audit_entry)
|
|
764
|
+
|
|
765
|
+
# Clear from repository
|
|
766
|
+
self._suppressions.clear()
|
|
767
|
+
self._repository.save_all_suppressions([])
|
|
768
|
+
|
|
769
|
+
return count
|
|
770
|
+
|
|
771
|
+
def reload(self) -> int:
|
|
772
|
+
"""Reload suppressions from repository.
|
|
773
|
+
|
|
774
|
+
Returns:
|
|
775
|
+
Number of suppressions loaded
|
|
776
|
+
"""
|
|
777
|
+
self._suppressions.clear()
|
|
778
|
+
self._load_from_repository()
|
|
779
|
+
return len(self._suppressions)
|
|
780
|
+
|
|
781
|
+
def save_to_file(self, path: Path | None = None) -> int:
|
|
782
|
+
"""Save current suppressions to file (backward compatibility).
|
|
783
|
+
|
|
784
|
+
Args:
|
|
785
|
+
path: Path to save to (default: repository's config path)
|
|
786
|
+
|
|
787
|
+
Returns:
|
|
788
|
+
Number of suppressions saved
|
|
789
|
+
"""
|
|
790
|
+
suppressions = list(self._suppressions.values())
|
|
791
|
+
self._repository.save_all_suppressions(suppressions)
|
|
792
|
+
return len(suppressions)
|
|
793
|
+
|
|
794
|
+
@property
|
|
795
|
+
def config_path(self) -> Path | None:
|
|
796
|
+
"""Get config path from repository (if available).
|
|
797
|
+
|
|
798
|
+
Returns:
|
|
799
|
+
Path to config file, or None if repository doesn't expose it
|
|
800
|
+
"""
|
|
801
|
+
return getattr(self._repository, "config_path", None)
|