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,1368 @@
|
|
|
1
|
+
"""CrewAI integration for RAXE scanning.
|
|
2
|
+
|
|
3
|
+
Provides a guard wrapper for automatic RAXE scanning in CrewAI multi-agent
|
|
4
|
+
applications. Supports scanning of agent messages, task outputs, tool calls,
|
|
5
|
+
and inter-agent communications.
|
|
6
|
+
|
|
7
|
+
This integration works with:
|
|
8
|
+
- Crew orchestration (kickoff, step_callback, task_callback)
|
|
9
|
+
- Individual Agents
|
|
10
|
+
- Tasks and their outputs
|
|
11
|
+
- Tools (BaseTool subclasses)
|
|
12
|
+
|
|
13
|
+
Requires: crewai>=0.28.0
|
|
14
|
+
|
|
15
|
+
Usage:
|
|
16
|
+
from crewai import Crew, Agent, Task
|
|
17
|
+
from raxe import Raxe
|
|
18
|
+
from raxe.sdk.integrations import RaxeCrewGuard
|
|
19
|
+
|
|
20
|
+
# Create guard
|
|
21
|
+
guard = RaxeCrewGuard(Raxe())
|
|
22
|
+
|
|
23
|
+
# Wrap a crew for automatic scanning
|
|
24
|
+
protected_crew = guard.protect_crew(crew)
|
|
25
|
+
result = protected_crew.kickoff()
|
|
26
|
+
|
|
27
|
+
# Or use callbacks directly
|
|
28
|
+
crew = Crew(
|
|
29
|
+
agents=[researcher, writer],
|
|
30
|
+
tasks=[research_task, write_task],
|
|
31
|
+
step_callback=guard.step_callback,
|
|
32
|
+
task_callback=guard.task_callback,
|
|
33
|
+
)
|
|
34
|
+
"""
|
|
35
|
+
from __future__ import annotations
|
|
36
|
+
|
|
37
|
+
import functools
|
|
38
|
+
import logging
|
|
39
|
+
import threading
|
|
40
|
+
from collections.abc import Callable
|
|
41
|
+
from dataclasses import dataclass, field
|
|
42
|
+
from typing import TYPE_CHECKING, Any, TypeVar
|
|
43
|
+
|
|
44
|
+
from raxe.sdk.agent_scanner import (
|
|
45
|
+
AgentScanner,
|
|
46
|
+
AgentScannerConfig,
|
|
47
|
+
AgentScanResult,
|
|
48
|
+
MessageType,
|
|
49
|
+
ScanContext,
|
|
50
|
+
ScanMode,
|
|
51
|
+
ThreatDetectedError,
|
|
52
|
+
create_agent_scanner,
|
|
53
|
+
)
|
|
54
|
+
from raxe.sdk.exceptions import SecurityException
|
|
55
|
+
from raxe.sdk.integrations.extractors import (
|
|
56
|
+
extract_text_from_dict,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
if TYPE_CHECKING:
|
|
60
|
+
from raxe.application.scan_pipeline import ScanPipelineResult
|
|
61
|
+
from raxe.sdk.client import Raxe
|
|
62
|
+
|
|
63
|
+
logger = logging.getLogger(__name__)
|
|
64
|
+
|
|
65
|
+
# Type variables for generic typing
|
|
66
|
+
T = TypeVar("T")
|
|
67
|
+
CrewType = TypeVar("CrewType")
|
|
68
|
+
AgentType = TypeVar("AgentType")
|
|
69
|
+
TaskType = TypeVar("TaskType")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@dataclass
|
|
73
|
+
class CrewGuardConfig:
|
|
74
|
+
"""Configuration for RaxeCrewGuard.
|
|
75
|
+
|
|
76
|
+
Extends AgentScannerConfig with CrewAI-specific options.
|
|
77
|
+
|
|
78
|
+
Attributes:
|
|
79
|
+
mode: How to handle detected threats (default: LOG_ONLY)
|
|
80
|
+
scan_step_outputs: Scan step outputs in step_callback
|
|
81
|
+
scan_task_outputs: Scan task outputs in task_callback
|
|
82
|
+
scan_tool_inputs: Scan tool inputs before execution
|
|
83
|
+
scan_tool_outputs: Scan tool outputs after execution
|
|
84
|
+
scan_agent_thoughts: Scan agent reasoning/thoughts
|
|
85
|
+
scan_crew_inputs: Scan inputs to crew.kickoff()
|
|
86
|
+
scan_crew_outputs: Scan final crew outputs
|
|
87
|
+
on_threat: Callback when threat detected
|
|
88
|
+
on_block: Callback when execution blocked
|
|
89
|
+
wrap_tools: Automatically wrap tools for scanning
|
|
90
|
+
tool_scan_mode: Scan mode specifically for tools
|
|
91
|
+
|
|
92
|
+
Example:
|
|
93
|
+
# Monitoring mode (default - safe for production)
|
|
94
|
+
config = CrewGuardConfig()
|
|
95
|
+
|
|
96
|
+
# Strict mode for development/testing
|
|
97
|
+
config = CrewGuardConfig(
|
|
98
|
+
mode=ScanMode.BLOCK_ON_HIGH,
|
|
99
|
+
wrap_tools=True,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# Custom threat handling
|
|
103
|
+
def alert_security(msg, result):
|
|
104
|
+
send_slack_alert(f"Threat: {result.severity}")
|
|
105
|
+
|
|
106
|
+
config = CrewGuardConfig(
|
|
107
|
+
on_threat=alert_security,
|
|
108
|
+
)
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
# Scanning mode
|
|
112
|
+
mode: ScanMode = ScanMode.LOG_ONLY
|
|
113
|
+
|
|
114
|
+
# What to scan
|
|
115
|
+
scan_step_outputs: bool = True
|
|
116
|
+
scan_task_outputs: bool = True
|
|
117
|
+
scan_tool_inputs: bool = True
|
|
118
|
+
scan_tool_outputs: bool = True
|
|
119
|
+
scan_agent_thoughts: bool = True
|
|
120
|
+
scan_crew_inputs: bool = True
|
|
121
|
+
scan_crew_outputs: bool = True
|
|
122
|
+
|
|
123
|
+
# Callbacks
|
|
124
|
+
on_threat: Callable[[str, ScanPipelineResult], None] | None = None
|
|
125
|
+
on_block: Callable[[str, ScanPipelineResult], None] | None = None
|
|
126
|
+
|
|
127
|
+
# Tool-specific options
|
|
128
|
+
wrap_tools: bool = False # Auto-wrap tools for scanning
|
|
129
|
+
tool_scan_mode: ScanMode | None = None # Override mode for tools
|
|
130
|
+
|
|
131
|
+
# Advanced options
|
|
132
|
+
include_agent_context: bool = True # Include agent name/role in scans
|
|
133
|
+
include_task_context: bool = True # Include task description in scans
|
|
134
|
+
max_thought_length: int = 5000 # Max length of thoughts to scan
|
|
135
|
+
|
|
136
|
+
def _mode_to_on_threat(self) -> str:
|
|
137
|
+
"""Convert ScanMode to on_threat literal.
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
'log', 'block', or 'warn' based on mode
|
|
141
|
+
"""
|
|
142
|
+
if self.mode == ScanMode.LOG_ONLY:
|
|
143
|
+
return "log"
|
|
144
|
+
elif self.mode in (ScanMode.BLOCK_ON_THREAT, ScanMode.BLOCK_ON_HIGH, ScanMode.BLOCK_ON_CRITICAL):
|
|
145
|
+
return "block"
|
|
146
|
+
return "log"
|
|
147
|
+
|
|
148
|
+
def _mode_to_severity_threshold(self) -> str:
|
|
149
|
+
"""Convert ScanMode to block_severity_threshold.
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
Severity threshold based on mode
|
|
153
|
+
"""
|
|
154
|
+
if self.mode == ScanMode.BLOCK_ON_CRITICAL:
|
|
155
|
+
return "CRITICAL"
|
|
156
|
+
elif self.mode == ScanMode.BLOCK_ON_HIGH:
|
|
157
|
+
return "HIGH"
|
|
158
|
+
return "HIGH" # Default for BLOCK_ON_THREAT
|
|
159
|
+
|
|
160
|
+
def to_agent_scanner_config(self) -> AgentScannerConfig:
|
|
161
|
+
"""Convert to AgentScannerConfig for base scanner.
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
AgentScannerConfig with equivalent settings
|
|
165
|
+
"""
|
|
166
|
+
# Create wrapper callbacks that adapt CrewAI signature to AgentScanner signature
|
|
167
|
+
# CrewAI: Callable[[str, ScanPipelineResult], None]
|
|
168
|
+
# AgentScanner: Callable[[AgentScanResult], None]
|
|
169
|
+
threat_callback = None
|
|
170
|
+
block_callback = None
|
|
171
|
+
|
|
172
|
+
if self.on_threat is not None:
|
|
173
|
+
original_threat = self.on_threat
|
|
174
|
+
|
|
175
|
+
def threat_callback(result: AgentScanResult) -> None:
|
|
176
|
+
# Adapt to CrewAI callback signature
|
|
177
|
+
original_threat(result.message, result.pipeline_result)
|
|
178
|
+
|
|
179
|
+
if self.on_block is not None:
|
|
180
|
+
original_block = self.on_block
|
|
181
|
+
|
|
182
|
+
def block_callback(result: AgentScanResult) -> None:
|
|
183
|
+
original_block(result.message, result.pipeline_result)
|
|
184
|
+
|
|
185
|
+
return AgentScannerConfig(
|
|
186
|
+
on_threat=self._mode_to_on_threat(),
|
|
187
|
+
block_severity_threshold=self._mode_to_severity_threshold(),
|
|
188
|
+
scan_prompts=self.scan_crew_inputs,
|
|
189
|
+
scan_system_prompts=self.scan_agent_thoughts,
|
|
190
|
+
scan_tool_calls=self.scan_tool_inputs,
|
|
191
|
+
scan_tool_results=self.scan_tool_outputs,
|
|
192
|
+
scan_responses=self.scan_crew_outputs,
|
|
193
|
+
on_threat_callback=threat_callback,
|
|
194
|
+
on_block_callback=block_callback,
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
@dataclass
|
|
199
|
+
class CrewScanStats:
|
|
200
|
+
"""Thread-safe statistics for scans performed during crew execution.
|
|
201
|
+
|
|
202
|
+
Tracks scan counts, threats detected, and performance metrics
|
|
203
|
+
across a crew's lifecycle. All operations are thread-safe.
|
|
204
|
+
|
|
205
|
+
Attributes:
|
|
206
|
+
total_scans: Total number of scans performed
|
|
207
|
+
step_scans: Number of step_callback scans
|
|
208
|
+
task_scans: Number of task_callback scans
|
|
209
|
+
tool_scans: Number of tool scans
|
|
210
|
+
threats_detected: Total threats detected
|
|
211
|
+
threats_blocked: Number of times execution was blocked
|
|
212
|
+
highest_severity: Highest severity threat seen
|
|
213
|
+
scan_durations_ms: List of scan durations
|
|
214
|
+
"""
|
|
215
|
+
|
|
216
|
+
total_scans: int = 0
|
|
217
|
+
step_scans: int = 0
|
|
218
|
+
task_scans: int = 0
|
|
219
|
+
tool_scans: int = 0
|
|
220
|
+
threats_detected: int = 0
|
|
221
|
+
threats_blocked: int = 0
|
|
222
|
+
highest_severity: str | None = None
|
|
223
|
+
scan_durations_ms: list[float] = field(default_factory=list)
|
|
224
|
+
_lock: threading.Lock = field(default_factory=threading.Lock, repr=False)
|
|
225
|
+
|
|
226
|
+
def record_scan(
|
|
227
|
+
self,
|
|
228
|
+
scan_type: str,
|
|
229
|
+
result: AgentScanResult,
|
|
230
|
+
blocked: bool = False,
|
|
231
|
+
) -> None:
|
|
232
|
+
"""Record a scan for statistics (thread-safe).
|
|
233
|
+
|
|
234
|
+
Args:
|
|
235
|
+
scan_type: Type of scan (step, task, tool)
|
|
236
|
+
result: AgentScanResult from scanner
|
|
237
|
+
blocked: Whether execution was blocked
|
|
238
|
+
"""
|
|
239
|
+
with self._lock:
|
|
240
|
+
self.total_scans += 1
|
|
241
|
+
self.scan_durations_ms.append(result.duration_ms)
|
|
242
|
+
|
|
243
|
+
if scan_type == "step":
|
|
244
|
+
self.step_scans += 1
|
|
245
|
+
elif scan_type == "task":
|
|
246
|
+
self.task_scans += 1
|
|
247
|
+
elif scan_type == "tool":
|
|
248
|
+
self.tool_scans += 1
|
|
249
|
+
|
|
250
|
+
if result.has_threats:
|
|
251
|
+
self.threats_detected += 1
|
|
252
|
+
severity = result.severity
|
|
253
|
+
if severity:
|
|
254
|
+
self._update_highest_severity(severity)
|
|
255
|
+
|
|
256
|
+
if blocked:
|
|
257
|
+
self.threats_blocked += 1
|
|
258
|
+
|
|
259
|
+
def _update_highest_severity(self, severity: str) -> None:
|
|
260
|
+
"""Update highest severity seen (must be called with lock held)."""
|
|
261
|
+
severity_order = {"CRITICAL": 4, "HIGH": 3, "MEDIUM": 2, "LOW": 1}
|
|
262
|
+
current_level = severity_order.get(self.highest_severity or "", 0)
|
|
263
|
+
new_level = severity_order.get(severity, 0)
|
|
264
|
+
if new_level > current_level:
|
|
265
|
+
self.highest_severity = severity
|
|
266
|
+
|
|
267
|
+
@property
|
|
268
|
+
def average_scan_duration_ms(self) -> float:
|
|
269
|
+
"""Calculate average scan duration (thread-safe)."""
|
|
270
|
+
with self._lock:
|
|
271
|
+
if not self.scan_durations_ms:
|
|
272
|
+
return 0.0
|
|
273
|
+
return sum(self.scan_durations_ms) / len(self.scan_durations_ms)
|
|
274
|
+
|
|
275
|
+
def to_dict(self) -> dict[str, Any]:
|
|
276
|
+
"""Convert to dictionary for serialization (thread-safe)."""
|
|
277
|
+
with self._lock:
|
|
278
|
+
return {
|
|
279
|
+
"total_scans": self.total_scans,
|
|
280
|
+
"step_scans": self.step_scans,
|
|
281
|
+
"task_scans": self.task_scans,
|
|
282
|
+
"tool_scans": self.tool_scans,
|
|
283
|
+
"threats_detected": self.threats_detected,
|
|
284
|
+
"threats_blocked": self.threats_blocked,
|
|
285
|
+
"highest_severity": self.highest_severity,
|
|
286
|
+
"average_scan_duration_ms": (
|
|
287
|
+
sum(self.scan_durations_ms) / len(self.scan_durations_ms)
|
|
288
|
+
if self.scan_durations_ms else 0.0
|
|
289
|
+
),
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
class RaxeCrewGuard:
|
|
294
|
+
"""Security guard for CrewAI multi-agent applications.
|
|
295
|
+
|
|
296
|
+
RaxeCrewGuard provides automatic security scanning for CrewAI crews
|
|
297
|
+
using the composition pattern. It can be used in several ways:
|
|
298
|
+
|
|
299
|
+
1. **Callback Mode**: Pass callbacks to Crew constructor
|
|
300
|
+
2. **Wrapper Mode**: Wrap a Crew for automatic protection
|
|
301
|
+
3. **Manual Mode**: Call scan methods directly
|
|
302
|
+
|
|
303
|
+
The guard uses the AgentScanner base class for core scanning logic
|
|
304
|
+
and adds CrewAI-specific integration code.
|
|
305
|
+
|
|
306
|
+
Attributes:
|
|
307
|
+
raxe: RAXE client instance
|
|
308
|
+
config: Guard configuration
|
|
309
|
+
scanner: Underlying AgentScanner
|
|
310
|
+
stats: Scan statistics for current/last crew run
|
|
311
|
+
|
|
312
|
+
Example (Callback Mode):
|
|
313
|
+
>>> from crewai import Crew, Agent, Task
|
|
314
|
+
>>> from raxe import Raxe
|
|
315
|
+
>>> from raxe.sdk.integrations import RaxeCrewGuard
|
|
316
|
+
>>>
|
|
317
|
+
>>> guard = RaxeCrewGuard(Raxe())
|
|
318
|
+
>>>
|
|
319
|
+
>>> crew = Crew(
|
|
320
|
+
... agents=[researcher, writer],
|
|
321
|
+
... tasks=[research_task, write_task],
|
|
322
|
+
... step_callback=guard.step_callback,
|
|
323
|
+
... task_callback=guard.task_callback,
|
|
324
|
+
... )
|
|
325
|
+
>>>
|
|
326
|
+
>>> result = crew.kickoff()
|
|
327
|
+
>>> print(f"Threats detected: {guard.stats.threats_detected}")
|
|
328
|
+
|
|
329
|
+
Example (Wrapper Mode):
|
|
330
|
+
>>> guard = RaxeCrewGuard(Raxe())
|
|
331
|
+
>>> protected_crew = guard.protect_crew(crew)
|
|
332
|
+
>>> result = protected_crew.kickoff()
|
|
333
|
+
|
|
334
|
+
Example (Strict Mode):
|
|
335
|
+
>>> from raxe.sdk.integrations.agent_scanner import ScanMode
|
|
336
|
+
>>>
|
|
337
|
+
>>> config = CrewGuardConfig(
|
|
338
|
+
... mode=ScanMode.BLOCK_ON_HIGH,
|
|
339
|
+
... wrap_tools=True,
|
|
340
|
+
... )
|
|
341
|
+
>>> guard = RaxeCrewGuard(Raxe(), config)
|
|
342
|
+
"""
|
|
343
|
+
|
|
344
|
+
def __init__(
|
|
345
|
+
self,
|
|
346
|
+
raxe: Raxe,
|
|
347
|
+
config: CrewGuardConfig | None = None,
|
|
348
|
+
) -> None:
|
|
349
|
+
"""Initialize CrewAI guard.
|
|
350
|
+
|
|
351
|
+
Args:
|
|
352
|
+
raxe: RAXE client instance for scanning
|
|
353
|
+
config: Optional guard configuration
|
|
354
|
+
"""
|
|
355
|
+
self._raxe = raxe
|
|
356
|
+
self._config = config or CrewGuardConfig()
|
|
357
|
+
self._scanner = create_agent_scanner(
|
|
358
|
+
raxe, self._config.to_agent_scanner_config(), integration_type="crewai"
|
|
359
|
+
)
|
|
360
|
+
self._stats = CrewScanStats()
|
|
361
|
+
|
|
362
|
+
logger.debug(
|
|
363
|
+
"RaxeCrewGuard initialized",
|
|
364
|
+
extra={
|
|
365
|
+
"mode": self._config.mode.value,
|
|
366
|
+
"wrap_tools": self._config.wrap_tools,
|
|
367
|
+
},
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
@property
|
|
371
|
+
def raxe(self) -> Raxe:
|
|
372
|
+
"""Get the RAXE client instance."""
|
|
373
|
+
return self._raxe
|
|
374
|
+
|
|
375
|
+
@property
|
|
376
|
+
def config(self) -> CrewGuardConfig:
|
|
377
|
+
"""Get the guard configuration."""
|
|
378
|
+
return self._config
|
|
379
|
+
|
|
380
|
+
@property
|
|
381
|
+
def scanner(self) -> AgentScanner:
|
|
382
|
+
"""Get the underlying AgentScanner."""
|
|
383
|
+
return self._scanner
|
|
384
|
+
|
|
385
|
+
@property
|
|
386
|
+
def stats(self) -> CrewScanStats:
|
|
387
|
+
"""Get scan statistics for current/last crew run."""
|
|
388
|
+
return self._stats
|
|
389
|
+
|
|
390
|
+
def reset_stats(self) -> None:
|
|
391
|
+
"""Reset scan statistics (call before new crew run)."""
|
|
392
|
+
self._stats = CrewScanStats()
|
|
393
|
+
|
|
394
|
+
def _raise_security_exception(self, result: AgentScanResult) -> None:
|
|
395
|
+
"""Raise SecurityException from an AgentScanResult.
|
|
396
|
+
|
|
397
|
+
Handles both cases where pipeline_result is available or not.
|
|
398
|
+
|
|
399
|
+
Args:
|
|
400
|
+
result: AgentScanResult with threat details
|
|
401
|
+
|
|
402
|
+
Raises:
|
|
403
|
+
SecurityException: Always raises with appropriate error info
|
|
404
|
+
"""
|
|
405
|
+
from raxe.sdk.exceptions import security_threat_detected_error
|
|
406
|
+
|
|
407
|
+
if result.pipeline_result is not None:
|
|
408
|
+
raise SecurityException(result.pipeline_result)
|
|
409
|
+
|
|
410
|
+
# Create exception from AgentScanResult data
|
|
411
|
+
error = security_threat_detected_error(
|
|
412
|
+
severity=str(result.severity or "UNKNOWN"),
|
|
413
|
+
detection_count=result.detection_count,
|
|
414
|
+
)
|
|
415
|
+
message = (
|
|
416
|
+
f"Security threat detected: {result.severity} "
|
|
417
|
+
f"({result.detection_count} detection(s))"
|
|
418
|
+
)
|
|
419
|
+
exc = SecurityException.__new__(SecurityException)
|
|
420
|
+
exc.result = None
|
|
421
|
+
exc.error = error
|
|
422
|
+
Exception.__init__(exc, message)
|
|
423
|
+
raise exc
|
|
424
|
+
|
|
425
|
+
def step_callback(self, step_output: Any) -> None:
|
|
426
|
+
"""Callback for CrewAI step events.
|
|
427
|
+
|
|
428
|
+
This callback is called after each agent step (thought-action-observation).
|
|
429
|
+
Register with Crew(step_callback=guard.step_callback).
|
|
430
|
+
|
|
431
|
+
Args:
|
|
432
|
+
step_output: Step output from CrewAI (varies by version)
|
|
433
|
+
Can be AgentAction, ToolResult, or string output
|
|
434
|
+
|
|
435
|
+
Example:
|
|
436
|
+
crew = Crew(
|
|
437
|
+
agents=[agent],
|
|
438
|
+
tasks=[task],
|
|
439
|
+
step_callback=guard.step_callback,
|
|
440
|
+
)
|
|
441
|
+
"""
|
|
442
|
+
if not self._config.scan_step_outputs:
|
|
443
|
+
return
|
|
444
|
+
|
|
445
|
+
try:
|
|
446
|
+
# Extract text from step output
|
|
447
|
+
text = self._extract_step_text(step_output)
|
|
448
|
+
if not text:
|
|
449
|
+
return
|
|
450
|
+
|
|
451
|
+
# Extract agent info if available
|
|
452
|
+
agent_name = self._extract_agent_name(step_output)
|
|
453
|
+
|
|
454
|
+
# Create scan context
|
|
455
|
+
context = ScanContext(
|
|
456
|
+
message_type=MessageType.AGENT_TO_AGENT,
|
|
457
|
+
sender_name=agent_name,
|
|
458
|
+
metadata={
|
|
459
|
+
"source": "step_callback",
|
|
460
|
+
"step_type": type(step_output).__name__,
|
|
461
|
+
},
|
|
462
|
+
)
|
|
463
|
+
|
|
464
|
+
# Perform scan
|
|
465
|
+
result = self._scanner.scan_message(text, context=context)
|
|
466
|
+
|
|
467
|
+
# Record stats
|
|
468
|
+
self._stats.record_scan("step", result, result.should_block)
|
|
469
|
+
|
|
470
|
+
# Handle blocking if configured
|
|
471
|
+
if result.should_block:
|
|
472
|
+
logger.warning(
|
|
473
|
+
"crew_step_blocked",
|
|
474
|
+
extra={
|
|
475
|
+
"agent": agent_name,
|
|
476
|
+
"severity": result.severity,
|
|
477
|
+
"prompt_hash": result.prompt_hash,
|
|
478
|
+
},
|
|
479
|
+
)
|
|
480
|
+
# Note: CrewAI step_callback doesn't support blocking
|
|
481
|
+
# The threat is logged for monitoring
|
|
482
|
+
|
|
483
|
+
except Exception as e:
|
|
484
|
+
# Never fail the crew due to scanning errors
|
|
485
|
+
logger.error(
|
|
486
|
+
"crew_step_scan_error",
|
|
487
|
+
extra={"error": str(e), "error_type": type(e).__name__},
|
|
488
|
+
)
|
|
489
|
+
|
|
490
|
+
def task_callback(self, task_output: Any) -> None:
|
|
491
|
+
"""Callback for CrewAI task completion events.
|
|
492
|
+
|
|
493
|
+
This callback is called after each task completes.
|
|
494
|
+
Register with Crew(task_callback=guard.task_callback).
|
|
495
|
+
|
|
496
|
+
Args:
|
|
497
|
+
task_output: TaskOutput from CrewAI with task description and result
|
|
498
|
+
|
|
499
|
+
Example:
|
|
500
|
+
crew = Crew(
|
|
501
|
+
agents=[agent],
|
|
502
|
+
tasks=[task],
|
|
503
|
+
task_callback=guard.task_callback,
|
|
504
|
+
)
|
|
505
|
+
"""
|
|
506
|
+
if not self._config.scan_task_outputs:
|
|
507
|
+
return
|
|
508
|
+
|
|
509
|
+
try:
|
|
510
|
+
# Extract text from task output
|
|
511
|
+
text = self._extract_task_text(task_output)
|
|
512
|
+
if not text:
|
|
513
|
+
return
|
|
514
|
+
|
|
515
|
+
# Extract task info
|
|
516
|
+
task_description = self._extract_task_description(task_output)
|
|
517
|
+
agent_name = self._extract_agent_from_task(task_output)
|
|
518
|
+
|
|
519
|
+
# Create scan context
|
|
520
|
+
context = ScanContext(
|
|
521
|
+
message_type=MessageType.AGENT_RESPONSE,
|
|
522
|
+
sender_name=agent_name,
|
|
523
|
+
metadata={
|
|
524
|
+
"source": "task_callback",
|
|
525
|
+
"task_description": (
|
|
526
|
+
task_description[:200] if task_description else None
|
|
527
|
+
),
|
|
528
|
+
},
|
|
529
|
+
)
|
|
530
|
+
|
|
531
|
+
# Perform scan
|
|
532
|
+
result = self._scanner.scan_message(text, context=context)
|
|
533
|
+
|
|
534
|
+
# Record stats
|
|
535
|
+
self._stats.record_scan("task", result, result.should_block)
|
|
536
|
+
|
|
537
|
+
# Handle blocking if configured
|
|
538
|
+
if result.should_block:
|
|
539
|
+
logger.warning(
|
|
540
|
+
"crew_task_blocked",
|
|
541
|
+
extra={
|
|
542
|
+
"agent": agent_name,
|
|
543
|
+
"severity": result.severity,
|
|
544
|
+
"task": task_description[:100] if task_description else None,
|
|
545
|
+
"prompt_hash": result.prompt_hash,
|
|
546
|
+
},
|
|
547
|
+
)
|
|
548
|
+
# Note: CrewAI task_callback doesn't support blocking
|
|
549
|
+
# The threat is logged for monitoring
|
|
550
|
+
|
|
551
|
+
except Exception as e:
|
|
552
|
+
# Never fail the crew due to scanning errors
|
|
553
|
+
logger.error(
|
|
554
|
+
"crew_task_scan_error",
|
|
555
|
+
extra={"error": str(e), "error_type": type(e).__name__},
|
|
556
|
+
)
|
|
557
|
+
|
|
558
|
+
def before_kickoff(self, inputs: dict[str, Any]) -> dict[str, Any]:
|
|
559
|
+
"""Callback for before crew kickoff.
|
|
560
|
+
|
|
561
|
+
Scan inputs before crew execution begins.
|
|
562
|
+
Use with Crew @before_kickoff decorator or before_kickoff callback.
|
|
563
|
+
|
|
564
|
+
Args:
|
|
565
|
+
inputs: Input dictionary to the crew
|
|
566
|
+
|
|
567
|
+
Returns:
|
|
568
|
+
Original inputs (or modified if implementing input sanitization)
|
|
569
|
+
|
|
570
|
+
Raises:
|
|
571
|
+
SecurityException: If blocking mode and threat detected
|
|
572
|
+
|
|
573
|
+
Example:
|
|
574
|
+
@CrewBase
|
|
575
|
+
class MyCrew:
|
|
576
|
+
@before_kickoff
|
|
577
|
+
def check_inputs(self, inputs):
|
|
578
|
+
return guard.before_kickoff(inputs)
|
|
579
|
+
"""
|
|
580
|
+
if not self._config.scan_crew_inputs:
|
|
581
|
+
return inputs
|
|
582
|
+
|
|
583
|
+
# Reset stats for new run
|
|
584
|
+
self.reset_stats()
|
|
585
|
+
|
|
586
|
+
try:
|
|
587
|
+
# Scan all string inputs
|
|
588
|
+
for key, value in inputs.items():
|
|
589
|
+
if isinstance(value, str) and value.strip():
|
|
590
|
+
context = ScanContext(
|
|
591
|
+
message_type=MessageType.HUMAN_INPUT,
|
|
592
|
+
metadata={
|
|
593
|
+
"source": "before_kickoff",
|
|
594
|
+
"input_key": key,
|
|
595
|
+
},
|
|
596
|
+
)
|
|
597
|
+
|
|
598
|
+
result = self._scanner.scan_message(value, context=context)
|
|
599
|
+
|
|
600
|
+
if result.should_block:
|
|
601
|
+
logger.warning(
|
|
602
|
+
"crew_input_blocked",
|
|
603
|
+
extra={
|
|
604
|
+
"input_key": key,
|
|
605
|
+
"severity": result.severity,
|
|
606
|
+
"prompt_hash": result.prompt_hash,
|
|
607
|
+
},
|
|
608
|
+
)
|
|
609
|
+
self._raise_security_exception(result)
|
|
610
|
+
|
|
611
|
+
except (SecurityException, ThreatDetectedError):
|
|
612
|
+
raise
|
|
613
|
+
except Exception as e:
|
|
614
|
+
logger.error(
|
|
615
|
+
"crew_input_scan_error",
|
|
616
|
+
extra={"error": str(e), "error_type": type(e).__name__},
|
|
617
|
+
)
|
|
618
|
+
|
|
619
|
+
return inputs
|
|
620
|
+
|
|
621
|
+
def after_kickoff(self, output: Any) -> Any:
|
|
622
|
+
"""Callback for after crew kickoff completes.
|
|
623
|
+
|
|
624
|
+
Scan final crew output.
|
|
625
|
+
Use with Crew @after_kickoff decorator or after_kickoff callback.
|
|
626
|
+
|
|
627
|
+
Args:
|
|
628
|
+
output: Final output from the crew
|
|
629
|
+
|
|
630
|
+
Returns:
|
|
631
|
+
Original output
|
|
632
|
+
|
|
633
|
+
Example:
|
|
634
|
+
@CrewBase
|
|
635
|
+
class MyCrew:
|
|
636
|
+
@after_kickoff
|
|
637
|
+
def check_output(self, output):
|
|
638
|
+
return guard.after_kickoff(output)
|
|
639
|
+
"""
|
|
640
|
+
if not self._config.scan_crew_outputs:
|
|
641
|
+
return output
|
|
642
|
+
|
|
643
|
+
try:
|
|
644
|
+
# Extract text from output
|
|
645
|
+
text = self._extract_crew_output_text(output)
|
|
646
|
+
if text:
|
|
647
|
+
context = ScanContext(
|
|
648
|
+
message_type=MessageType.AGENT_RESPONSE,
|
|
649
|
+
metadata={"source": "after_kickoff"},
|
|
650
|
+
)
|
|
651
|
+
|
|
652
|
+
result = self._scanner.scan_message(text, context=context)
|
|
653
|
+
|
|
654
|
+
if result.has_threats:
|
|
655
|
+
logger.warning(
|
|
656
|
+
"crew_output_threat",
|
|
657
|
+
extra={
|
|
658
|
+
"severity": result.severity,
|
|
659
|
+
"prompt_hash": result.prompt_hash,
|
|
660
|
+
},
|
|
661
|
+
)
|
|
662
|
+
|
|
663
|
+
except Exception as e:
|
|
664
|
+
logger.error(
|
|
665
|
+
"crew_output_scan_error",
|
|
666
|
+
extra={"error": str(e), "error_type": type(e).__name__},
|
|
667
|
+
)
|
|
668
|
+
|
|
669
|
+
return output
|
|
670
|
+
|
|
671
|
+
def protect_crew(self, crew: CrewType) -> CrewType:
|
|
672
|
+
"""Wrap a Crew with automatic security scanning.
|
|
673
|
+
|
|
674
|
+
This method returns a proxy that automatically applies scanning
|
|
675
|
+
to all crew operations.
|
|
676
|
+
|
|
677
|
+
Args:
|
|
678
|
+
crew: CrewAI Crew instance to protect
|
|
679
|
+
|
|
680
|
+
Returns:
|
|
681
|
+
Protected Crew instance with scanning enabled
|
|
682
|
+
|
|
683
|
+
Example:
|
|
684
|
+
>>> from crewai import Crew
|
|
685
|
+
>>> crew = Crew(agents=[...], tasks=[...])
|
|
686
|
+
>>> protected_crew = guard.protect_crew(crew)
|
|
687
|
+
>>> result = protected_crew.kickoff()
|
|
688
|
+
"""
|
|
689
|
+
# Store original callbacks
|
|
690
|
+
original_step_callback = getattr(crew, "step_callback", None)
|
|
691
|
+
original_task_callback = getattr(crew, "task_callback", None)
|
|
692
|
+
|
|
693
|
+
# Combine with guard callbacks
|
|
694
|
+
def combined_step_callback(step_output: Any) -> None:
|
|
695
|
+
self.step_callback(step_output)
|
|
696
|
+
if original_step_callback:
|
|
697
|
+
original_step_callback(step_output)
|
|
698
|
+
|
|
699
|
+
def combined_task_callback(task_output: Any) -> None:
|
|
700
|
+
self.task_callback(task_output)
|
|
701
|
+
if original_task_callback:
|
|
702
|
+
original_task_callback(task_output)
|
|
703
|
+
|
|
704
|
+
# Set combined callbacks
|
|
705
|
+
crew.step_callback = combined_step_callback
|
|
706
|
+
crew.task_callback = combined_task_callback
|
|
707
|
+
|
|
708
|
+
# Optionally wrap tools
|
|
709
|
+
if self._config.wrap_tools:
|
|
710
|
+
self._wrap_crew_tools(crew)
|
|
711
|
+
|
|
712
|
+
logger.debug(
|
|
713
|
+
"crew_protected",
|
|
714
|
+
extra={
|
|
715
|
+
"mode": self._config.mode.value,
|
|
716
|
+
"wrap_tools": self._config.wrap_tools,
|
|
717
|
+
},
|
|
718
|
+
)
|
|
719
|
+
|
|
720
|
+
return crew
|
|
721
|
+
|
|
722
|
+
def wrap_tool(self, tool: T) -> T:
|
|
723
|
+
"""Wrap a CrewAI tool with input/output scanning.
|
|
724
|
+
|
|
725
|
+
Use this to protect individual tools. For automatic wrapping
|
|
726
|
+
of all crew tools, use protect_crew() with wrap_tools=True.
|
|
727
|
+
|
|
728
|
+
Args:
|
|
729
|
+
tool: CrewAI BaseTool instance
|
|
730
|
+
|
|
731
|
+
Returns:
|
|
732
|
+
Wrapped tool with scanning enabled
|
|
733
|
+
|
|
734
|
+
Example:
|
|
735
|
+
>>> from crewai_tools import SerperDevTool
|
|
736
|
+
>>> search_tool = guard.wrap_tool(SerperDevTool())
|
|
737
|
+
>>> agent = Agent(tools=[search_tool])
|
|
738
|
+
"""
|
|
739
|
+
# Get the original _run method
|
|
740
|
+
original_run = getattr(tool, "_run", None)
|
|
741
|
+
if original_run is None:
|
|
742
|
+
logger.warning(
|
|
743
|
+
"tool_wrap_failed",
|
|
744
|
+
extra={"reason": "no _run method", "tool": type(tool).__name__},
|
|
745
|
+
)
|
|
746
|
+
return tool
|
|
747
|
+
|
|
748
|
+
@functools.wraps(original_run)
|
|
749
|
+
def wrapped_run(*args: Any, **kwargs: Any) -> Any:
|
|
750
|
+
# Scan inputs
|
|
751
|
+
if self._config.scan_tool_inputs:
|
|
752
|
+
input_text = self._extract_tool_input(args, kwargs)
|
|
753
|
+
if input_text:
|
|
754
|
+
self._scan_tool_io(
|
|
755
|
+
text=input_text,
|
|
756
|
+
tool_name=getattr(tool, "name", type(tool).__name__),
|
|
757
|
+
io_type="input",
|
|
758
|
+
)
|
|
759
|
+
|
|
760
|
+
# Execute original
|
|
761
|
+
result = original_run(*args, **kwargs)
|
|
762
|
+
|
|
763
|
+
# Scan outputs
|
|
764
|
+
if self._config.scan_tool_outputs:
|
|
765
|
+
output_text = str(result) if result else None
|
|
766
|
+
if output_text:
|
|
767
|
+
self._scan_tool_io(
|
|
768
|
+
text=output_text,
|
|
769
|
+
tool_name=getattr(tool, "name", type(tool).__name__),
|
|
770
|
+
io_type="output",
|
|
771
|
+
)
|
|
772
|
+
|
|
773
|
+
return result
|
|
774
|
+
|
|
775
|
+
# Replace _run method
|
|
776
|
+
tool._run = wrapped_run
|
|
777
|
+
|
|
778
|
+
logger.debug(
|
|
779
|
+
"tool_wrapped",
|
|
780
|
+
extra={"tool": getattr(tool, "name", type(tool).__name__)},
|
|
781
|
+
)
|
|
782
|
+
|
|
783
|
+
return tool
|
|
784
|
+
|
|
785
|
+
def wrap_tools(self, tools: list[T]) -> list[T]:
|
|
786
|
+
"""Wrap multiple CrewAI tools with input/output scanning.
|
|
787
|
+
|
|
788
|
+
Convenience method to wrap multiple tools at once.
|
|
789
|
+
|
|
790
|
+
Args:
|
|
791
|
+
tools: List of CrewAI BaseTool instances
|
|
792
|
+
|
|
793
|
+
Returns:
|
|
794
|
+
List of wrapped tools with scanning enabled
|
|
795
|
+
|
|
796
|
+
Example:
|
|
797
|
+
>>> from crewai_tools import SerperDevTool, FileReadTool
|
|
798
|
+
>>> tools = [SerperDevTool(), FileReadTool()]
|
|
799
|
+
>>> wrapped_tools = guard.wrap_tools(tools)
|
|
800
|
+
>>> agent = Agent(tools=wrapped_tools)
|
|
801
|
+
"""
|
|
802
|
+
return [self.wrap_tool(tool) for tool in tools]
|
|
803
|
+
|
|
804
|
+
def _scan_tool_io(
|
|
805
|
+
self,
|
|
806
|
+
text: str,
|
|
807
|
+
tool_name: str,
|
|
808
|
+
io_type: str,
|
|
809
|
+
) -> None:
|
|
810
|
+
"""Scan tool input or output.
|
|
811
|
+
|
|
812
|
+
Args:
|
|
813
|
+
text: Text to scan
|
|
814
|
+
tool_name: Name of the tool
|
|
815
|
+
io_type: "input" or "output"
|
|
816
|
+
"""
|
|
817
|
+
try:
|
|
818
|
+
message_type = (
|
|
819
|
+
MessageType.FUNCTION_CALL
|
|
820
|
+
if io_type == "input"
|
|
821
|
+
else MessageType.FUNCTION_RESULT
|
|
822
|
+
)
|
|
823
|
+
|
|
824
|
+
context = ScanContext(
|
|
825
|
+
message_type=message_type,
|
|
826
|
+
metadata={
|
|
827
|
+
"source": f"tool_{io_type}",
|
|
828
|
+
"tool_name": tool_name,
|
|
829
|
+
},
|
|
830
|
+
)
|
|
831
|
+
|
|
832
|
+
result = self._scanner.scan_message(text, context=context)
|
|
833
|
+
|
|
834
|
+
# Record stats
|
|
835
|
+
self._stats.record_scan("tool", result, result.should_block)
|
|
836
|
+
|
|
837
|
+
if result.has_threats:
|
|
838
|
+
logger.warning(
|
|
839
|
+
f"crew_tool_{io_type}_threat",
|
|
840
|
+
extra={
|
|
841
|
+
"tool": tool_name,
|
|
842
|
+
"severity": result.severity,
|
|
843
|
+
"prompt_hash": result.prompt_hash,
|
|
844
|
+
},
|
|
845
|
+
)
|
|
846
|
+
|
|
847
|
+
# Block if configured
|
|
848
|
+
if result.should_block:
|
|
849
|
+
self._raise_security_exception(result)
|
|
850
|
+
|
|
851
|
+
except SecurityException:
|
|
852
|
+
raise
|
|
853
|
+
except Exception as e:
|
|
854
|
+
logger.error(
|
|
855
|
+
f"crew_tool_{io_type}_scan_error",
|
|
856
|
+
extra={
|
|
857
|
+
"tool": tool_name,
|
|
858
|
+
"error": str(e),
|
|
859
|
+
"error_type": type(e).__name__,
|
|
860
|
+
},
|
|
861
|
+
)
|
|
862
|
+
|
|
863
|
+
def _wrap_crew_tools(self, crew: Any) -> None:
|
|
864
|
+
"""Wrap all tools in a crew's agents.
|
|
865
|
+
|
|
866
|
+
Args:
|
|
867
|
+
crew: CrewAI Crew instance
|
|
868
|
+
"""
|
|
869
|
+
agents = getattr(crew, "agents", [])
|
|
870
|
+
for agent in agents:
|
|
871
|
+
tools = getattr(agent, "tools", [])
|
|
872
|
+
wrapped_tools = [self.wrap_tool(tool) for tool in tools]
|
|
873
|
+
agent.tools = wrapped_tools
|
|
874
|
+
|
|
875
|
+
def _extract_step_text(self, step_output: Any) -> str | None:
|
|
876
|
+
"""Extract text from step output.
|
|
877
|
+
|
|
878
|
+
Uses unified extractors where applicable, with CrewAI-specific fallbacks.
|
|
879
|
+
|
|
880
|
+
Args:
|
|
881
|
+
step_output: Step output from CrewAI
|
|
882
|
+
|
|
883
|
+
Returns:
|
|
884
|
+
Text content or None
|
|
885
|
+
"""
|
|
886
|
+
if isinstance(step_output, str):
|
|
887
|
+
return step_output
|
|
888
|
+
|
|
889
|
+
# Handle AgentAction
|
|
890
|
+
if hasattr(step_output, "tool_input"):
|
|
891
|
+
return str(step_output.tool_input)
|
|
892
|
+
|
|
893
|
+
# Handle ToolResult
|
|
894
|
+
if hasattr(step_output, "result"):
|
|
895
|
+
return str(step_output.result)
|
|
896
|
+
|
|
897
|
+
# Handle dict using unified extractor
|
|
898
|
+
if isinstance(step_output, dict):
|
|
899
|
+
return extract_text_from_dict(step_output, ("output", "text", "result"))
|
|
900
|
+
|
|
901
|
+
# Handle thought attribute
|
|
902
|
+
if hasattr(step_output, "thought"):
|
|
903
|
+
return str(step_output.thought)
|
|
904
|
+
|
|
905
|
+
# Handle log attribute
|
|
906
|
+
if hasattr(step_output, "log"):
|
|
907
|
+
return str(step_output.log)
|
|
908
|
+
|
|
909
|
+
# Fallback to string conversion
|
|
910
|
+
try:
|
|
911
|
+
text = str(step_output)
|
|
912
|
+
if text and len(text) < 10000: # Sanity check
|
|
913
|
+
return text
|
|
914
|
+
except Exception:
|
|
915
|
+
pass
|
|
916
|
+
|
|
917
|
+
return None
|
|
918
|
+
|
|
919
|
+
def _extract_task_text(self, task_output: Any) -> str | None:
|
|
920
|
+
"""Extract text from task output.
|
|
921
|
+
|
|
922
|
+
Uses unified extractors where applicable, with CrewAI-specific fallbacks.
|
|
923
|
+
|
|
924
|
+
Args:
|
|
925
|
+
task_output: TaskOutput from CrewAI
|
|
926
|
+
|
|
927
|
+
Returns:
|
|
928
|
+
Text content or None
|
|
929
|
+
"""
|
|
930
|
+
if isinstance(task_output, str):
|
|
931
|
+
return task_output
|
|
932
|
+
|
|
933
|
+
# Handle TaskOutput object
|
|
934
|
+
if hasattr(task_output, "raw"):
|
|
935
|
+
return str(task_output.raw)
|
|
936
|
+
|
|
937
|
+
if hasattr(task_output, "output"):
|
|
938
|
+
return str(task_output.output)
|
|
939
|
+
|
|
940
|
+
if hasattr(task_output, "result"):
|
|
941
|
+
return str(task_output.result)
|
|
942
|
+
|
|
943
|
+
# Handle dict using unified extractor
|
|
944
|
+
if isinstance(task_output, dict):
|
|
945
|
+
return extract_text_from_dict(
|
|
946
|
+
task_output, ("raw", "output", "result", "text")
|
|
947
|
+
)
|
|
948
|
+
|
|
949
|
+
return None
|
|
950
|
+
|
|
951
|
+
def _extract_task_description(self, task_output: Any) -> str | None:
|
|
952
|
+
"""Extract task description from task output.
|
|
953
|
+
|
|
954
|
+
Args:
|
|
955
|
+
task_output: TaskOutput from CrewAI
|
|
956
|
+
|
|
957
|
+
Returns:
|
|
958
|
+
Task description or None
|
|
959
|
+
"""
|
|
960
|
+
if hasattr(task_output, "description"):
|
|
961
|
+
return str(task_output.description)
|
|
962
|
+
|
|
963
|
+
if hasattr(task_output, "task") and hasattr(task_output.task, "description"):
|
|
964
|
+
return str(task_output.task.description)
|
|
965
|
+
|
|
966
|
+
if isinstance(task_output, dict) and "description" in task_output:
|
|
967
|
+
return str(task_output["description"])
|
|
968
|
+
|
|
969
|
+
return None
|
|
970
|
+
|
|
971
|
+
def _extract_agent_name(self, step_output: Any) -> str | None:
|
|
972
|
+
"""Extract agent name from step output.
|
|
973
|
+
|
|
974
|
+
Args:
|
|
975
|
+
step_output: Step output from CrewAI
|
|
976
|
+
|
|
977
|
+
Returns:
|
|
978
|
+
Agent name or None
|
|
979
|
+
"""
|
|
980
|
+
# Try direct agent attribute
|
|
981
|
+
if hasattr(step_output, "agent"):
|
|
982
|
+
agent = step_output.agent
|
|
983
|
+
if hasattr(agent, "role"):
|
|
984
|
+
return str(agent.role)
|
|
985
|
+
if hasattr(agent, "name"):
|
|
986
|
+
return str(agent.name)
|
|
987
|
+
return str(agent)
|
|
988
|
+
|
|
989
|
+
# Try agent_name attribute
|
|
990
|
+
if hasattr(step_output, "agent_name"):
|
|
991
|
+
return str(step_output.agent_name)
|
|
992
|
+
|
|
993
|
+
# Try dict
|
|
994
|
+
if isinstance(step_output, dict):
|
|
995
|
+
if "agent" in step_output:
|
|
996
|
+
return str(step_output["agent"])
|
|
997
|
+
if "agent_name" in step_output:
|
|
998
|
+
return str(step_output["agent_name"])
|
|
999
|
+
|
|
1000
|
+
return None
|
|
1001
|
+
|
|
1002
|
+
def _extract_agent_from_task(self, task_output: Any) -> str | None:
|
|
1003
|
+
"""Extract agent name from task output.
|
|
1004
|
+
|
|
1005
|
+
Args:
|
|
1006
|
+
task_output: TaskOutput from CrewAI
|
|
1007
|
+
|
|
1008
|
+
Returns:
|
|
1009
|
+
Agent name or None
|
|
1010
|
+
"""
|
|
1011
|
+
if hasattr(task_output, "agent"):
|
|
1012
|
+
agent = task_output.agent
|
|
1013
|
+
if hasattr(agent, "role"):
|
|
1014
|
+
return str(agent.role)
|
|
1015
|
+
if hasattr(agent, "name"):
|
|
1016
|
+
return str(agent.name)
|
|
1017
|
+
|
|
1018
|
+
if hasattr(task_output, "task") and hasattr(task_output.task, "agent"):
|
|
1019
|
+
agent = task_output.task.agent
|
|
1020
|
+
if hasattr(agent, "role"):
|
|
1021
|
+
return str(agent.role)
|
|
1022
|
+
|
|
1023
|
+
return None
|
|
1024
|
+
|
|
1025
|
+
def _extract_crew_output_text(self, output: Any) -> str | None:
|
|
1026
|
+
"""Extract text from crew output.
|
|
1027
|
+
|
|
1028
|
+
Uses unified extractors where applicable, with CrewAI-specific fallbacks.
|
|
1029
|
+
|
|
1030
|
+
Args:
|
|
1031
|
+
output: CrewOutput from CrewAI
|
|
1032
|
+
|
|
1033
|
+
Returns:
|
|
1034
|
+
Text content or None
|
|
1035
|
+
"""
|
|
1036
|
+
if isinstance(output, str):
|
|
1037
|
+
return output
|
|
1038
|
+
|
|
1039
|
+
# Handle CrewOutput
|
|
1040
|
+
if hasattr(output, "raw"):
|
|
1041
|
+
return str(output.raw)
|
|
1042
|
+
|
|
1043
|
+
if hasattr(output, "result"):
|
|
1044
|
+
return str(output.result)
|
|
1045
|
+
|
|
1046
|
+
# Handle dict using unified extractor
|
|
1047
|
+
if isinstance(output, dict):
|
|
1048
|
+
return extract_text_from_dict(
|
|
1049
|
+
output, ("raw", "result", "output", "text")
|
|
1050
|
+
)
|
|
1051
|
+
|
|
1052
|
+
return None
|
|
1053
|
+
|
|
1054
|
+
def _extract_tool_input(
|
|
1055
|
+
self,
|
|
1056
|
+
args: tuple[Any, ...],
|
|
1057
|
+
kwargs: dict[str, Any],
|
|
1058
|
+
) -> str | None:
|
|
1059
|
+
"""Extract tool input text from arguments.
|
|
1060
|
+
|
|
1061
|
+
Args:
|
|
1062
|
+
args: Positional arguments
|
|
1063
|
+
kwargs: Keyword arguments
|
|
1064
|
+
|
|
1065
|
+
Returns:
|
|
1066
|
+
Input text or None
|
|
1067
|
+
"""
|
|
1068
|
+
# Check kwargs for common input keys
|
|
1069
|
+
for key in ["query", "input", "text", "question", "prompt"]:
|
|
1070
|
+
if key in kwargs:
|
|
1071
|
+
return str(kwargs[key])
|
|
1072
|
+
|
|
1073
|
+
# Check first string arg
|
|
1074
|
+
for arg in args:
|
|
1075
|
+
if isinstance(arg, str):
|
|
1076
|
+
return arg
|
|
1077
|
+
|
|
1078
|
+
# Fallback to all kwargs as string
|
|
1079
|
+
if kwargs:
|
|
1080
|
+
return str(kwargs)
|
|
1081
|
+
|
|
1082
|
+
return None
|
|
1083
|
+
|
|
1084
|
+
def get_summary(self) -> dict[str, Any]:
|
|
1085
|
+
"""Get summary of guard activity.
|
|
1086
|
+
|
|
1087
|
+
Returns:
|
|
1088
|
+
Dictionary with configuration and statistics
|
|
1089
|
+
"""
|
|
1090
|
+
return {
|
|
1091
|
+
"config": {
|
|
1092
|
+
"mode": self._config.mode.value,
|
|
1093
|
+
"wrap_tools": self._config.wrap_tools,
|
|
1094
|
+
"scan_step_outputs": self._config.scan_step_outputs,
|
|
1095
|
+
"scan_task_outputs": self._config.scan_task_outputs,
|
|
1096
|
+
},
|
|
1097
|
+
"stats": self._stats.to_dict(),
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
# =========================================================================
|
|
1101
|
+
# Async Callback Methods
|
|
1102
|
+
# =========================================================================
|
|
1103
|
+
# Async versions of callbacks for use in async contexts.
|
|
1104
|
+
|
|
1105
|
+
async def step_callback_async(self, step_output: Any) -> None:
|
|
1106
|
+
"""Async callback for CrewAI step events.
|
|
1107
|
+
|
|
1108
|
+
Async version of step_callback for use in async contexts.
|
|
1109
|
+
Uses asyncio.get_event_loop().run_in_executor() for non-blocking scans.
|
|
1110
|
+
|
|
1111
|
+
Args:
|
|
1112
|
+
step_output: Step output from CrewAI
|
|
1113
|
+
"""
|
|
1114
|
+
import asyncio
|
|
1115
|
+
|
|
1116
|
+
if not self._config.scan_step_outputs:
|
|
1117
|
+
return
|
|
1118
|
+
|
|
1119
|
+
try:
|
|
1120
|
+
text = self._extract_step_text(step_output)
|
|
1121
|
+
if not text:
|
|
1122
|
+
return
|
|
1123
|
+
|
|
1124
|
+
agent_name = self._extract_agent_name(step_output)
|
|
1125
|
+
|
|
1126
|
+
context = ScanContext(
|
|
1127
|
+
message_type=MessageType.AGENT_TO_AGENT,
|
|
1128
|
+
sender_name=agent_name,
|
|
1129
|
+
metadata={
|
|
1130
|
+
"source": "step_callback_async",
|
|
1131
|
+
"step_type": type(step_output).__name__,
|
|
1132
|
+
},
|
|
1133
|
+
)
|
|
1134
|
+
|
|
1135
|
+
result = await asyncio.get_event_loop().run_in_executor(
|
|
1136
|
+
None, lambda: self._scanner.scan_message(text, context=context)
|
|
1137
|
+
)
|
|
1138
|
+
|
|
1139
|
+
self._stats.record_scan("step", result, result.should_block)
|
|
1140
|
+
|
|
1141
|
+
if result.should_block:
|
|
1142
|
+
logger.warning(
|
|
1143
|
+
"crew_step_blocked_async",
|
|
1144
|
+
extra={
|
|
1145
|
+
"agent": agent_name,
|
|
1146
|
+
"severity": result.severity,
|
|
1147
|
+
"prompt_hash": result.prompt_hash,
|
|
1148
|
+
},
|
|
1149
|
+
)
|
|
1150
|
+
|
|
1151
|
+
except Exception as e:
|
|
1152
|
+
logger.error(
|
|
1153
|
+
"crew_step_scan_error_async",
|
|
1154
|
+
extra={"error": str(e), "error_type": type(e).__name__},
|
|
1155
|
+
)
|
|
1156
|
+
|
|
1157
|
+
async def task_callback_async(self, task_output: Any) -> None:
|
|
1158
|
+
"""Async callback for CrewAI task completion events.
|
|
1159
|
+
|
|
1160
|
+
Async version of task_callback for use in async contexts.
|
|
1161
|
+
|
|
1162
|
+
Args:
|
|
1163
|
+
task_output: TaskOutput from CrewAI
|
|
1164
|
+
"""
|
|
1165
|
+
import asyncio
|
|
1166
|
+
|
|
1167
|
+
if not self._config.scan_task_outputs:
|
|
1168
|
+
return
|
|
1169
|
+
|
|
1170
|
+
try:
|
|
1171
|
+
text = self._extract_task_text(task_output)
|
|
1172
|
+
if not text:
|
|
1173
|
+
return
|
|
1174
|
+
|
|
1175
|
+
task_description = self._extract_task_description(task_output)
|
|
1176
|
+
agent_name = self._extract_agent_from_task(task_output)
|
|
1177
|
+
|
|
1178
|
+
context = ScanContext(
|
|
1179
|
+
message_type=MessageType.AGENT_RESPONSE,
|
|
1180
|
+
sender_name=agent_name,
|
|
1181
|
+
metadata={
|
|
1182
|
+
"source": "task_callback_async",
|
|
1183
|
+
"task_description": (
|
|
1184
|
+
task_description[:200] if task_description else None
|
|
1185
|
+
),
|
|
1186
|
+
},
|
|
1187
|
+
)
|
|
1188
|
+
|
|
1189
|
+
result = await asyncio.get_event_loop().run_in_executor(
|
|
1190
|
+
None, lambda: self._scanner.scan_message(text, context=context)
|
|
1191
|
+
)
|
|
1192
|
+
|
|
1193
|
+
self._stats.record_scan("task", result, result.should_block)
|
|
1194
|
+
|
|
1195
|
+
if result.should_block:
|
|
1196
|
+
logger.warning(
|
|
1197
|
+
"crew_task_blocked_async",
|
|
1198
|
+
extra={
|
|
1199
|
+
"agent": agent_name,
|
|
1200
|
+
"severity": result.severity,
|
|
1201
|
+
"task": task_description[:100] if task_description else None,
|
|
1202
|
+
"prompt_hash": result.prompt_hash,
|
|
1203
|
+
},
|
|
1204
|
+
)
|
|
1205
|
+
|
|
1206
|
+
except Exception as e:
|
|
1207
|
+
logger.error(
|
|
1208
|
+
"crew_task_scan_error_async",
|
|
1209
|
+
extra={"error": str(e), "error_type": type(e).__name__},
|
|
1210
|
+
)
|
|
1211
|
+
|
|
1212
|
+
async def before_kickoff_async(
|
|
1213
|
+
self, inputs: dict[str, Any]
|
|
1214
|
+
) -> dict[str, Any]:
|
|
1215
|
+
"""Async callback for before crew kickoff.
|
|
1216
|
+
|
|
1217
|
+
Async version of before_kickoff for use in async contexts.
|
|
1218
|
+
|
|
1219
|
+
Args:
|
|
1220
|
+
inputs: Input dictionary to the crew
|
|
1221
|
+
|
|
1222
|
+
Returns:
|
|
1223
|
+
Original inputs
|
|
1224
|
+
|
|
1225
|
+
Raises:
|
|
1226
|
+
SecurityException: If blocking mode and threat detected
|
|
1227
|
+
"""
|
|
1228
|
+
import asyncio
|
|
1229
|
+
|
|
1230
|
+
if not self._config.scan_crew_inputs:
|
|
1231
|
+
return inputs
|
|
1232
|
+
|
|
1233
|
+
self.reset_stats()
|
|
1234
|
+
|
|
1235
|
+
try:
|
|
1236
|
+
for key, value in inputs.items():
|
|
1237
|
+
if isinstance(value, str) and value.strip():
|
|
1238
|
+
context = ScanContext(
|
|
1239
|
+
message_type=MessageType.HUMAN_INPUT,
|
|
1240
|
+
metadata={
|
|
1241
|
+
"source": "before_kickoff_async",
|
|
1242
|
+
"input_key": key,
|
|
1243
|
+
},
|
|
1244
|
+
)
|
|
1245
|
+
|
|
1246
|
+
result = await asyncio.get_event_loop().run_in_executor(
|
|
1247
|
+
None, lambda v=value: self._scanner.scan_message(v, context=context)
|
|
1248
|
+
)
|
|
1249
|
+
|
|
1250
|
+
if result.should_block:
|
|
1251
|
+
logger.warning(
|
|
1252
|
+
"crew_input_blocked_async",
|
|
1253
|
+
extra={
|
|
1254
|
+
"input_key": key,
|
|
1255
|
+
"severity": result.severity,
|
|
1256
|
+
"prompt_hash": result.prompt_hash,
|
|
1257
|
+
},
|
|
1258
|
+
)
|
|
1259
|
+
self._raise_security_exception(result)
|
|
1260
|
+
|
|
1261
|
+
except (SecurityException, ThreatDetectedError):
|
|
1262
|
+
raise
|
|
1263
|
+
except Exception as e:
|
|
1264
|
+
logger.error(
|
|
1265
|
+
"crew_input_scan_error_async",
|
|
1266
|
+
extra={"error": str(e), "error_type": type(e).__name__},
|
|
1267
|
+
)
|
|
1268
|
+
|
|
1269
|
+
return inputs
|
|
1270
|
+
|
|
1271
|
+
async def after_kickoff_async(self, output: Any) -> Any:
|
|
1272
|
+
"""Async callback for after crew kickoff completes.
|
|
1273
|
+
|
|
1274
|
+
Async version of after_kickoff for use in async contexts.
|
|
1275
|
+
|
|
1276
|
+
Args:
|
|
1277
|
+
output: Final output from the crew
|
|
1278
|
+
|
|
1279
|
+
Returns:
|
|
1280
|
+
Original output
|
|
1281
|
+
"""
|
|
1282
|
+
import asyncio
|
|
1283
|
+
|
|
1284
|
+
if not self._config.scan_crew_outputs:
|
|
1285
|
+
return output
|
|
1286
|
+
|
|
1287
|
+
try:
|
|
1288
|
+
text = self._extract_crew_output_text(output)
|
|
1289
|
+
if text:
|
|
1290
|
+
context = ScanContext(
|
|
1291
|
+
message_type=MessageType.AGENT_RESPONSE,
|
|
1292
|
+
metadata={"source": "after_kickoff_async"},
|
|
1293
|
+
)
|
|
1294
|
+
|
|
1295
|
+
result = await asyncio.get_event_loop().run_in_executor(
|
|
1296
|
+
None, lambda: self._scanner.scan_message(text, context=context)
|
|
1297
|
+
)
|
|
1298
|
+
|
|
1299
|
+
if result.has_threats:
|
|
1300
|
+
logger.warning(
|
|
1301
|
+
"crew_output_threat_async",
|
|
1302
|
+
extra={
|
|
1303
|
+
"severity": result.severity,
|
|
1304
|
+
"prompt_hash": result.prompt_hash,
|
|
1305
|
+
},
|
|
1306
|
+
)
|
|
1307
|
+
|
|
1308
|
+
except Exception as e:
|
|
1309
|
+
logger.error(
|
|
1310
|
+
"crew_output_scan_error_async",
|
|
1311
|
+
extra={"error": str(e), "error_type": type(e).__name__},
|
|
1312
|
+
)
|
|
1313
|
+
|
|
1314
|
+
return output
|
|
1315
|
+
|
|
1316
|
+
def __repr__(self) -> str:
|
|
1317
|
+
"""String representation."""
|
|
1318
|
+
return (
|
|
1319
|
+
f"RaxeCrewGuard("
|
|
1320
|
+
f"mode={self._config.mode.value}, "
|
|
1321
|
+
f"scans={self._stats.total_scans}, "
|
|
1322
|
+
f"threats={self._stats.threats_detected})"
|
|
1323
|
+
)
|
|
1324
|
+
|
|
1325
|
+
|
|
1326
|
+
# Convenience function for quick setup
|
|
1327
|
+
def create_crew_guard(
|
|
1328
|
+
raxe: Raxe | None = None,
|
|
1329
|
+
mode: ScanMode = ScanMode.LOG_ONLY,
|
|
1330
|
+
**kwargs: Any,
|
|
1331
|
+
) -> RaxeCrewGuard:
|
|
1332
|
+
"""Create a RaxeCrewGuard with common defaults.
|
|
1333
|
+
|
|
1334
|
+
Convenience function for quick guard creation.
|
|
1335
|
+
|
|
1336
|
+
Args:
|
|
1337
|
+
raxe: Optional RAXE client (creates default if None)
|
|
1338
|
+
mode: Scan mode (default: LOG_ONLY)
|
|
1339
|
+
**kwargs: Additional CrewGuardConfig parameters
|
|
1340
|
+
|
|
1341
|
+
Returns:
|
|
1342
|
+
Configured RaxeCrewGuard instance
|
|
1343
|
+
|
|
1344
|
+
Example:
|
|
1345
|
+
>>> from raxe.sdk.integrations import create_crew_guard
|
|
1346
|
+
>>> from raxe.sdk.integrations.agent_scanner import ScanMode
|
|
1347
|
+
>>>
|
|
1348
|
+
>>> # Quick monitoring setup
|
|
1349
|
+
>>> guard = create_crew_guard()
|
|
1350
|
+
>>>
|
|
1351
|
+
>>> # Strict mode
|
|
1352
|
+
>>> guard = create_crew_guard(mode=ScanMode.BLOCK_ON_HIGH)
|
|
1353
|
+
"""
|
|
1354
|
+
from raxe.sdk.client import Raxe
|
|
1355
|
+
|
|
1356
|
+
if raxe is None:
|
|
1357
|
+
raxe = Raxe()
|
|
1358
|
+
|
|
1359
|
+
config = CrewGuardConfig(mode=mode, **kwargs)
|
|
1360
|
+
return RaxeCrewGuard(raxe, config)
|
|
1361
|
+
|
|
1362
|
+
|
|
1363
|
+
__all__ = [
|
|
1364
|
+
"CrewGuardConfig",
|
|
1365
|
+
"CrewScanStats",
|
|
1366
|
+
"RaxeCrewGuard",
|
|
1367
|
+
"create_crew_guard",
|
|
1368
|
+
]
|