guard-core 2.0.0__tar.gz → 2.2.0__tar.gz
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.
- {guard_core-2.0.0/guard_core.egg-info → guard_core-2.2.0}/PKG-INFO +1 -1
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/implementations/cloud_ip_refresh.py +0 -13
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/pipeline.py +1 -1
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/initialization/handler_initializer.py +35 -12
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/decorators/access_control.py +11 -11
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/decorators/advanced.py +7 -7
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/decorators/authentication.py +9 -9
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/decorators/base.py +12 -5
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/decorators/behavioral.py +9 -9
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/decorators/content_filtering.py +13 -13
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/decorators/rate_limiting.py +5 -5
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/detection_engine/compiler.py +1 -1
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/detection_engine/preprocessor.py +76 -19
- {guard_core-2.0.0/guard_core/sync → guard_core-2.2.0/guard_core}/handlers/__init__.py +4 -0
- guard_core-2.2.0/guard_core/handlers/cors_handler.py +169 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/handlers/dynamic_rule_handler.py +44 -11
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/handlers/ipban_handler.py +99 -12
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/models.py +8 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/implementations/cloud_ip_refresh.py +0 -13
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/pipeline.py +1 -1
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/initialization/handler_initializer.py +37 -11
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/decorators/access_control.py +11 -11
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/decorators/advanced.py +7 -7
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/decorators/authentication.py +9 -9
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/decorators/base.py +12 -5
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/decorators/behavioral.py +9 -9
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/decorators/content_filtering.py +13 -13
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/decorators/rate_limiting.py +5 -5
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/detection_engine/compiler.py +1 -1
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/detection_engine/preprocessor.py +76 -19
- {guard_core-2.0.0/guard_core → guard_core-2.2.0/guard_core/sync}/handlers/__init__.py +4 -0
- guard_core-2.2.0/guard_core/sync/handlers/cors_handler.py +169 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/handlers/dynamic_rule_handler.py +44 -11
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/handlers/ipban_handler.py +99 -12
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/handlers/ratelimit_handler.py +11 -7
- {guard_core-2.0.0 → guard_core-2.2.0/guard_core.egg-info}/PKG-INFO +1 -1
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core.egg-info/SOURCES.txt +10 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/pyproject.toml +5 -1
- {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_check_log_muting.py +4 -2
- {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_cloud_ip_refresh_on_demand.py +65 -14
- guard_core-2.2.0/tests/test_compiler_cache.py +333 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_composite_enricher.py +7 -6
- {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_composite_handler_extra.py +24 -12
- guard_core-2.2.0/tests/test_cors_handler.py +236 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_detection_exclusion_integration.py +40 -6
- {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_detection_result_propagation.py +16 -7
- guard_core-2.2.0/tests/test_dynamic_rule_atomicity.py +115 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_enricher_otel_integration.py +11 -6
- {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_enricher_rule_correlation.py +1 -1
- {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_handler_edge_cases.py +1 -1
- guard_core-2.2.0/tests/test_handler_initializer_lazy_init.py +185 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_handlers_integration.py +9 -8
- guard_core-2.2.0/tests/test_ipban_cidr.py +176 -0
- guard_core-2.2.0/tests/test_ipban_ttl.py +76 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_logfire_handler.py +8 -2
- {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_logfire_handler_metric.py +4 -3
- {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_otel_handler.py +8 -2
- {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_otel_handler_resource_attrs.py +16 -11
- guard_core-2.2.0/tests/test_pipeline_fail_secure.py +52 -0
- guard_core-2.2.0/tests/test_preprocessor_attack_regions.py +42 -0
- guard_core-2.2.0/tests/test_preprocessor_encodings.py +206 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_telemetry_integration.py +30 -21
- guard_core-2.0.0/tests/test_handler_initializer_lazy_init.py +0 -83
- {guard_core-2.0.0 → guard_core-2.2.0}/LICENSE +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/MANIFEST.in +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/README.md +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/__init__.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/__init__.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/behavioral/__init__.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/behavioral/context.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/behavioral/processor.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/bypass/__init__.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/bypass/context.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/bypass/handler.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/__init__.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/base.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/helpers.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/implementations/__init__.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/implementations/authentication.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/implementations/cloud_provider.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/implementations/custom_request.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/implementations/custom_validators.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/implementations/emergency_mode.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/implementations/https_enforcement.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/implementations/ip_security.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/implementations/rate_limit.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/implementations/referrer.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/implementations/request_logging.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/implementations/request_size_content.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/implementations/required_headers.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/implementations/route_config.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/implementations/suspicious_activity.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/implementations/time_window.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/implementations/user_agent.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/events/__init__.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/events/composite_handler.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/events/enricher.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/events/event_types.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/events/logfire_handler.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/events/metrics.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/events/middleware_events.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/events/otel_handler.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/initialization/__init__.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/responses/__init__.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/responses/context.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/responses/factory.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/routing/__init__.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/routing/context.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/routing/resolver.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/validation/__init__.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/validation/context.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/validation/validator.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/decorators/__init__.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/detection_engine/__init__.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/detection_engine/monitor.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/detection_engine/semantic.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/detection_result.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/exceptions.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/handlers/behavior_handler.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/handlers/cloud_handler.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/handlers/cloud_ip_stores.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/handlers/ipinfo_handler.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/handlers/ratelimit_handler.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/handlers/redis_handler.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/handlers/security_headers_handler.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/handlers/suspatterns_handler.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/protocols/__init__.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/protocols/agent_protocol.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/protocols/cloud_ip_store_protocol.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/protocols/geo_ip_protocol.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/protocols/middleware_protocol.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/protocols/redis_protocol.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/protocols/request_protocol.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/protocols/response_protocol.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/py.typed +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/scripts/__init__.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/scripts/rate_lua.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/__init__.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/__init__.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/behavioral/__init__.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/behavioral/context.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/behavioral/processor.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/bypass/__init__.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/bypass/context.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/bypass/handler.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/__init__.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/base.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/helpers.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/implementations/__init__.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/implementations/authentication.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/implementations/cloud_provider.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/implementations/custom_request.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/implementations/custom_validators.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/implementations/emergency_mode.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/implementations/https_enforcement.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/implementations/ip_security.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/implementations/rate_limit.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/implementations/referrer.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/implementations/request_logging.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/implementations/request_size_content.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/implementations/required_headers.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/implementations/route_config.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/implementations/suspicious_activity.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/implementations/time_window.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/implementations/user_agent.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/events/__init__.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/events/composite_handler.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/events/enricher.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/events/event_types.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/events/logfire_handler.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/events/metrics.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/events/middleware_events.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/events/otel_handler.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/initialization/__init__.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/responses/__init__.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/responses/context.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/responses/factory.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/routing/__init__.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/routing/context.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/routing/resolver.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/validation/__init__.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/validation/context.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/validation/validator.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/decorators/__init__.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/detection_engine/__init__.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/detection_engine/monitor.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/detection_engine/semantic.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/detection_result.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/handlers/behavior_handler.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/handlers/cloud_handler.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/handlers/cloud_ip_stores.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/handlers/ipinfo_handler.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/handlers/redis_handler.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/handlers/security_headers_handler.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/handlers/suspatterns_handler.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/protocols/__init__.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/protocols/agent_protocol.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/protocols/cloud_ip_store_protocol.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/protocols/geo_ip_protocol.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/protocols/middleware_protocol.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/protocols/redis_protocol.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/protocols/request_protocol.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/protocols/response_protocol.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/py.typed +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/scripts/__init__.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/scripts/rate_lua.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/utils.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/utils.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core.egg-info/dependency_links.txt +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core.egg-info/requires.txt +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/guard_core.egg-info/top_level.txt +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/setup.cfg +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/setup.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_behavior_rule_ban_duration.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_cloud_ip_stores.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_composite_handler.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_detection_categories.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_detection_result.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_enricher.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_enricher_behavior_correlation.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_enricher_end_to_end.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_enricher_identity.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_enricher_logfire_integration.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_enricher_threat_score.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_event_bus_filtering.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_event_types.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_handler_initializer.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_handler_initializer_enricher.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_handler_initializer_factories.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_ipinfo_lifecycle.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_suspicious_counts_per_type.py +0 -0
- {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_threat_ban_config.py +0 -0
{guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/implementations/cloud_ip_refresh.py
RENAMED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import time
|
|
2
2
|
|
|
3
3
|
from guard_core.core.checks.base import SecurityCheck
|
|
4
|
-
from guard_core.handlers.cloud_handler import cloud_handler
|
|
5
4
|
from guard_core.protocols.request_protocol import GuardRequest
|
|
6
5
|
from guard_core.protocols.response_protocol import GuardResponse
|
|
7
6
|
|
|
@@ -15,18 +14,6 @@ class CloudIpRefreshCheck(SecurityCheck):
|
|
|
15
14
|
if not self.config.block_cloud_providers:
|
|
16
15
|
return None
|
|
17
16
|
|
|
18
|
-
providers = self.config.block_cloud_providers
|
|
19
|
-
has_any_ranges = any(
|
|
20
|
-
cloud_handler.ip_ranges.get(provider) for provider in providers
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
if self.config.lazy_init and not has_any_ranges:
|
|
24
|
-
await cloud_handler.refresh_async(
|
|
25
|
-
providers, ttl=self.config.cloud_ip_refresh_interval
|
|
26
|
-
)
|
|
27
|
-
self.middleware.last_cloud_ip_refresh = int(time.time())
|
|
28
|
-
return None
|
|
29
|
-
|
|
30
17
|
if (
|
|
31
18
|
time.time() - self.middleware.last_cloud_ip_refresh
|
|
32
19
|
> self.config.cloud_ip_refresh_interval
|
|
@@ -46,7 +46,7 @@ class SecurityCheckPipeline:
|
|
|
46
46
|
exc_info=True,
|
|
47
47
|
)
|
|
48
48
|
|
|
49
|
-
if
|
|
49
|
+
if check.config.fail_secure:
|
|
50
50
|
if check.check_name not in self.muted_check_logs:
|
|
51
51
|
self.logger.warning(
|
|
52
52
|
f"Blocking request due to check error "
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
1
3
|
from typing import TYPE_CHECKING, Any
|
|
2
4
|
|
|
3
5
|
if TYPE_CHECKING:
|
|
@@ -26,6 +28,8 @@ class HandlerInitializer:
|
|
|
26
28
|
self.event_filter: Any = None
|
|
27
29
|
self.enricher: Any = None
|
|
28
30
|
self.behavior_tracker: Any = None
|
|
31
|
+
self._lazy_init_task: asyncio.Task[None] | None = None
|
|
32
|
+
self.logger = logging.getLogger("guard_core.core.initialization")
|
|
29
33
|
|
|
30
34
|
def build_enricher(self) -> Any | None:
|
|
31
35
|
if not self.config.enable_enrichment:
|
|
@@ -111,6 +115,23 @@ class HandlerInitializer:
|
|
|
111
115
|
event_filter=self.event_filter,
|
|
112
116
|
)
|
|
113
117
|
|
|
118
|
+
async def _run_lazy_init(self) -> None:
|
|
119
|
+
try:
|
|
120
|
+
from guard_core.handlers.cloud_handler import cloud_handler
|
|
121
|
+
|
|
122
|
+
if self.config.block_cloud_providers:
|
|
123
|
+
await cloud_handler.initialize_redis(
|
|
124
|
+
self.redis_handler,
|
|
125
|
+
self.config.block_cloud_providers,
|
|
126
|
+
ttl=self.config.cloud_ip_refresh_interval,
|
|
127
|
+
)
|
|
128
|
+
if self.geo_ip_handler is not None:
|
|
129
|
+
await self.geo_ip_handler.initialize_redis(self.redis_handler)
|
|
130
|
+
except Exception as e:
|
|
131
|
+
self.logger.warning(
|
|
132
|
+
"Lazy background initialization failed: %s", e, exc_info=True
|
|
133
|
+
)
|
|
134
|
+
|
|
114
135
|
async def initialize_redis_handlers(self) -> None:
|
|
115
136
|
if not (self.config.enable_redis and self.redis_handler):
|
|
116
137
|
return
|
|
@@ -121,25 +142,27 @@ class HandlerInitializer:
|
|
|
121
142
|
from guard_core.handlers.ipban_handler import ip_ban_manager
|
|
122
143
|
from guard_core.handlers.suspatterns_handler import sus_patterns_handler
|
|
123
144
|
|
|
124
|
-
if
|
|
125
|
-
|
|
126
|
-
self.redis_handler,
|
|
127
|
-
self.config.block_cloud_providers,
|
|
128
|
-
ttl=self.config.cloud_ip_refresh_interval,
|
|
129
|
-
)
|
|
145
|
+
if self.config.cloud_ip_store is not None:
|
|
146
|
+
cloud_handler.set_store(self.config.cloud_ip_store)
|
|
130
147
|
|
|
131
|
-
|
|
148
|
+
if self.config.lazy_init:
|
|
149
|
+
self._lazy_init_task = asyncio.create_task(self._run_lazy_init())
|
|
150
|
+
else:
|
|
151
|
+
if self.config.block_cloud_providers:
|
|
152
|
+
await cloud_handler.initialize_redis(
|
|
153
|
+
self.redis_handler,
|
|
154
|
+
self.config.block_cloud_providers,
|
|
155
|
+
ttl=self.config.cloud_ip_refresh_interval,
|
|
156
|
+
)
|
|
157
|
+
if self.geo_ip_handler is not None:
|
|
158
|
+
await self.geo_ip_handler.initialize_redis(self.redis_handler)
|
|
132
159
|
|
|
133
|
-
|
|
134
|
-
await self.geo_ip_handler.initialize_redis(self.redis_handler)
|
|
160
|
+
await ip_ban_manager.initialize_redis(self.redis_handler)
|
|
135
161
|
|
|
136
162
|
if self.rate_limit_handler is not None:
|
|
137
163
|
await self.rate_limit_handler.initialize_redis(self.redis_handler)
|
|
138
164
|
await sus_patterns_handler.initialize_redis(self.redis_handler)
|
|
139
165
|
|
|
140
|
-
if self.config.cloud_ip_store is not None:
|
|
141
|
-
cloud_handler.set_store(self.config.cloud_ip_store)
|
|
142
|
-
|
|
143
166
|
async def initialize_agent_for_handlers(self) -> None:
|
|
144
167
|
telemetry = self.composite_handler or self.agent_handler
|
|
145
168
|
if telemetry is None:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from collections.abc import Callable
|
|
2
2
|
from typing import Any
|
|
3
3
|
|
|
4
|
-
from guard_core.decorators.base import BaseSecurityMixin
|
|
4
|
+
from guard_core.decorators.base import BaseSecurityMixin, DecoratedFunction
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class AccessControlMixin(BaseSecurityMixin):
|
|
@@ -9,8 +9,8 @@ class AccessControlMixin(BaseSecurityMixin):
|
|
|
9
9
|
self,
|
|
10
10
|
whitelist: list[str] | None = None,
|
|
11
11
|
blacklist: list[str] | None = None,
|
|
12
|
-
) -> Callable[[Callable[..., Any]],
|
|
13
|
-
def decorator(func: Callable[..., Any]) ->
|
|
12
|
+
) -> Callable[[Callable[..., Any]], DecoratedFunction]:
|
|
13
|
+
def decorator(func: Callable[..., Any]) -> DecoratedFunction:
|
|
14
14
|
route_config = self._ensure_route_config(func)
|
|
15
15
|
if whitelist:
|
|
16
16
|
route_config.ip_whitelist = whitelist
|
|
@@ -22,8 +22,8 @@ class AccessControlMixin(BaseSecurityMixin):
|
|
|
22
22
|
|
|
23
23
|
def block_countries(
|
|
24
24
|
self, countries: list[str]
|
|
25
|
-
) -> Callable[[Callable[..., Any]],
|
|
26
|
-
def decorator(func: Callable[..., Any]) ->
|
|
25
|
+
) -> Callable[[Callable[..., Any]], DecoratedFunction]:
|
|
26
|
+
def decorator(func: Callable[..., Any]) -> DecoratedFunction:
|
|
27
27
|
route_config = self._ensure_route_config(func)
|
|
28
28
|
route_config.blocked_countries = countries
|
|
29
29
|
return self._apply_route_config(func)
|
|
@@ -32,8 +32,8 @@ class AccessControlMixin(BaseSecurityMixin):
|
|
|
32
32
|
|
|
33
33
|
def allow_countries(
|
|
34
34
|
self, countries: list[str]
|
|
35
|
-
) -> Callable[[Callable[..., Any]],
|
|
36
|
-
def decorator(func: Callable[..., Any]) ->
|
|
35
|
+
) -> Callable[[Callable[..., Any]], DecoratedFunction]:
|
|
36
|
+
def decorator(func: Callable[..., Any]) -> DecoratedFunction:
|
|
37
37
|
route_config = self._ensure_route_config(func)
|
|
38
38
|
route_config.whitelist_countries = countries
|
|
39
39
|
return self._apply_route_config(func)
|
|
@@ -42,8 +42,8 @@ class AccessControlMixin(BaseSecurityMixin):
|
|
|
42
42
|
|
|
43
43
|
def block_clouds(
|
|
44
44
|
self, providers: list[str] | None = None
|
|
45
|
-
) -> Callable[[Callable[..., Any]],
|
|
46
|
-
def decorator(func: Callable[..., Any]) ->
|
|
45
|
+
) -> Callable[[Callable[..., Any]], DecoratedFunction]:
|
|
46
|
+
def decorator(func: Callable[..., Any]) -> DecoratedFunction:
|
|
47
47
|
route_config = self._ensure_route_config(func)
|
|
48
48
|
if providers is None:
|
|
49
49
|
route_config.block_cloud_providers = {"AWS", "GCP", "Azure"}
|
|
@@ -55,8 +55,8 @@ class AccessControlMixin(BaseSecurityMixin):
|
|
|
55
55
|
|
|
56
56
|
def bypass(
|
|
57
57
|
self, checks: list[str]
|
|
58
|
-
) -> Callable[[Callable[..., Any]],
|
|
59
|
-
def decorator(func: Callable[..., Any]) ->
|
|
58
|
+
) -> Callable[[Callable[..., Any]], DecoratedFunction]:
|
|
59
|
+
def decorator(func: Callable[..., Any]) -> DecoratedFunction:
|
|
60
60
|
route_config = self._ensure_route_config(func)
|
|
61
61
|
route_config.bypassed_checks.update(checks)
|
|
62
62
|
return self._apply_route_config(func)
|
|
@@ -3,7 +3,7 @@ from collections.abc import Callable, MutableMapping
|
|
|
3
3
|
from typing import Any
|
|
4
4
|
from urllib.parse import parse_qs
|
|
5
5
|
|
|
6
|
-
from guard_core.decorators.base import BaseSecurityMixin
|
|
6
|
+
from guard_core.decorators.base import BaseSecurityMixin, DecoratedFunction
|
|
7
7
|
from guard_core.protocols.request_protocol import GuardRequest
|
|
8
8
|
from guard_core.protocols.response_protocol import GuardResponse
|
|
9
9
|
|
|
@@ -30,8 +30,8 @@ class _SimpleResponse:
|
|
|
30
30
|
class AdvancedMixin(BaseSecurityMixin):
|
|
31
31
|
def time_window(
|
|
32
32
|
self, start_time: str, end_time: str, timezone: str = "UTC"
|
|
33
|
-
) -> Callable[[Callable[..., Any]],
|
|
34
|
-
def decorator(func: Callable[..., Any]) ->
|
|
33
|
+
) -> Callable[[Callable[..., Any]], DecoratedFunction]:
|
|
34
|
+
def decorator(func: Callable[..., Any]) -> DecoratedFunction:
|
|
35
35
|
route_config = self._ensure_route_config(func)
|
|
36
36
|
route_config.time_restrictions = {
|
|
37
37
|
"start": start_time,
|
|
@@ -44,8 +44,8 @@ class AdvancedMixin(BaseSecurityMixin):
|
|
|
44
44
|
|
|
45
45
|
def suspicious_detection(
|
|
46
46
|
self, enabled: bool = True
|
|
47
|
-
) -> Callable[[Callable[..., Any]],
|
|
48
|
-
def decorator(func: Callable[..., Any]) ->
|
|
47
|
+
) -> Callable[[Callable[..., Any]], DecoratedFunction]:
|
|
48
|
+
def decorator(func: Callable[..., Any]) -> DecoratedFunction:
|
|
49
49
|
route_config = self._ensure_route_config(func)
|
|
50
50
|
route_config.enable_suspicious_detection = enabled
|
|
51
51
|
return self._apply_route_config(func)
|
|
@@ -54,8 +54,8 @@ class AdvancedMixin(BaseSecurityMixin):
|
|
|
54
54
|
|
|
55
55
|
def honeypot_detection(
|
|
56
56
|
self, trap_fields: list[str]
|
|
57
|
-
) -> Callable[[Callable[..., Any]],
|
|
58
|
-
def decorator(func: Callable[..., Any]) ->
|
|
57
|
+
) -> Callable[[Callable[..., Any]], DecoratedFunction]:
|
|
58
|
+
def decorator(func: Callable[..., Any]) -> DecoratedFunction:
|
|
59
59
|
async def honeypot_validator(
|
|
60
60
|
request: GuardRequest,
|
|
61
61
|
) -> GuardResponse | None:
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
from collections.abc import Callable
|
|
2
2
|
from typing import Any
|
|
3
3
|
|
|
4
|
-
from guard_core.decorators.base import BaseSecurityMixin
|
|
4
|
+
from guard_core.decorators.base import BaseSecurityMixin, DecoratedFunction
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class AuthenticationMixin(BaseSecurityMixin):
|
|
8
|
-
def require_https(self) -> Callable[[Callable[..., Any]],
|
|
9
|
-
def decorator(func: Callable[..., Any]) ->
|
|
8
|
+
def require_https(self) -> Callable[[Callable[..., Any]], DecoratedFunction]:
|
|
9
|
+
def decorator(func: Callable[..., Any]) -> DecoratedFunction:
|
|
10
10
|
route_config = self._ensure_route_config(func)
|
|
11
11
|
route_config.require_https = True
|
|
12
12
|
return self._apply_route_config(func)
|
|
@@ -15,8 +15,8 @@ class AuthenticationMixin(BaseSecurityMixin):
|
|
|
15
15
|
|
|
16
16
|
def require_auth(
|
|
17
17
|
self, type: str = "bearer"
|
|
18
|
-
) -> Callable[[Callable[..., Any]],
|
|
19
|
-
def decorator(func: Callable[..., Any]) ->
|
|
18
|
+
) -> Callable[[Callable[..., Any]], DecoratedFunction]:
|
|
19
|
+
def decorator(func: Callable[..., Any]) -> DecoratedFunction:
|
|
20
20
|
route_config = self._ensure_route_config(func)
|
|
21
21
|
route_config.auth_required = type
|
|
22
22
|
return self._apply_route_config(func)
|
|
@@ -25,8 +25,8 @@ class AuthenticationMixin(BaseSecurityMixin):
|
|
|
25
25
|
|
|
26
26
|
def api_key_auth(
|
|
27
27
|
self, header_name: str = "X-API-Key"
|
|
28
|
-
) -> Callable[[Callable[..., Any]],
|
|
29
|
-
def decorator(func: Callable[..., Any]) ->
|
|
28
|
+
) -> Callable[[Callable[..., Any]], DecoratedFunction]:
|
|
29
|
+
def decorator(func: Callable[..., Any]) -> DecoratedFunction:
|
|
30
30
|
route_config = self._ensure_route_config(func)
|
|
31
31
|
route_config.api_key_required = True
|
|
32
32
|
route_config.required_headers[header_name] = "required"
|
|
@@ -36,8 +36,8 @@ class AuthenticationMixin(BaseSecurityMixin):
|
|
|
36
36
|
|
|
37
37
|
def require_headers(
|
|
38
38
|
self, headers: dict[str, str]
|
|
39
|
-
) -> Callable[[Callable[..., Any]],
|
|
40
|
-
def decorator(func: Callable[..., Any]) ->
|
|
39
|
+
) -> Callable[[Callable[..., Any]], DecoratedFunction]:
|
|
40
|
+
def decorator(func: Callable[..., Any]) -> DecoratedFunction:
|
|
41
41
|
route_config = self._ensure_route_config(func)
|
|
42
42
|
route_config.required_headers.update(headers)
|
|
43
43
|
return self._apply_route_config(func)
|
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
from collections.abc import Callable
|
|
2
2
|
from datetime import datetime, timezone
|
|
3
|
-
from typing import Any
|
|
3
|
+
from typing import Any, Protocol, cast, runtime_checkable
|
|
4
4
|
|
|
5
5
|
from guard_core.handlers.behavior_handler import BehaviorRule, BehaviorTracker
|
|
6
6
|
from guard_core.models import SecurityConfig
|
|
7
7
|
from guard_core.protocols.request_protocol import GuardRequest
|
|
8
8
|
|
|
9
9
|
|
|
10
|
+
@runtime_checkable
|
|
11
|
+
class DecoratedFunction(Protocol):
|
|
12
|
+
_guard_route_id: str
|
|
13
|
+
|
|
14
|
+
def __call__(self, *args: Any, **kwargs: Any) -> Any: ...
|
|
15
|
+
|
|
16
|
+
|
|
10
17
|
class RouteConfig:
|
|
11
18
|
def __init__(self) -> None:
|
|
12
19
|
self.rate_limit: int | None = None
|
|
@@ -41,7 +48,7 @@ class BaseSecurityMixin:
|
|
|
41
48
|
def _ensure_route_config(self, func: Callable[..., Any]) -> RouteConfig:
|
|
42
49
|
raise NotImplementedError("This mixin must be used with BaseSecurityDecorator")
|
|
43
50
|
|
|
44
|
-
def _apply_route_config(self, func: Callable[..., Any]) ->
|
|
51
|
+
def _apply_route_config(self, func: Callable[..., Any]) -> "DecoratedFunction":
|
|
45
52
|
raise NotImplementedError("This mixin must be used with BaseSecurityDecorator")
|
|
46
53
|
|
|
47
54
|
|
|
@@ -68,10 +75,10 @@ class BaseSecurityDecorator:
|
|
|
68
75
|
self._route_configs[route_id] = config
|
|
69
76
|
return self._route_configs[route_id]
|
|
70
77
|
|
|
71
|
-
def _apply_route_config(self, func: Callable[..., Any]) ->
|
|
78
|
+
def _apply_route_config(self, func: Callable[..., Any]) -> DecoratedFunction:
|
|
72
79
|
route_id = self._get_route_id(func)
|
|
73
|
-
func._guard_route_id = route_id
|
|
74
|
-
return func
|
|
80
|
+
cast(Any, func)._guard_route_id = route_id
|
|
81
|
+
return cast(DecoratedFunction, func)
|
|
75
82
|
|
|
76
83
|
async def initialize_behavior_tracking(self, redis_handler: Any = None) -> None:
|
|
77
84
|
if redis_handler:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from collections.abc import Callable
|
|
2
2
|
from typing import Any, Literal
|
|
3
3
|
|
|
4
|
-
from guard_core.decorators.base import BaseSecurityMixin
|
|
4
|
+
from guard_core.decorators.base import BaseSecurityMixin, DecoratedFunction
|
|
5
5
|
from guard_core.handlers.behavior_handler import BehaviorRule
|
|
6
6
|
|
|
7
7
|
|
|
@@ -11,8 +11,8 @@ class BehavioralMixin(BaseSecurityMixin):
|
|
|
11
11
|
max_calls: int,
|
|
12
12
|
window: int = 3600,
|
|
13
13
|
action: Literal["ban", "log", "throttle", "alert"] = "ban",
|
|
14
|
-
) -> Callable[[Callable[..., Any]],
|
|
15
|
-
def decorator(func: Callable[..., Any]) ->
|
|
14
|
+
) -> Callable[[Callable[..., Any]], DecoratedFunction]:
|
|
15
|
+
def decorator(func: Callable[..., Any]) -> DecoratedFunction:
|
|
16
16
|
route_config = self._ensure_route_config(func)
|
|
17
17
|
|
|
18
18
|
rule = BehaviorRule(
|
|
@@ -29,8 +29,8 @@ class BehavioralMixin(BaseSecurityMixin):
|
|
|
29
29
|
max_occurrences: int,
|
|
30
30
|
window: int = 86400,
|
|
31
31
|
action: Literal["ban", "log", "throttle", "alert"] = "ban",
|
|
32
|
-
) -> Callable[[Callable[..., Any]],
|
|
33
|
-
def decorator(func: Callable[..., Any]) ->
|
|
32
|
+
) -> Callable[[Callable[..., Any]], DecoratedFunction]:
|
|
33
|
+
def decorator(func: Callable[..., Any]) -> DecoratedFunction:
|
|
34
34
|
route_config = self._ensure_route_config(func)
|
|
35
35
|
|
|
36
36
|
rule = BehaviorRule(
|
|
@@ -47,8 +47,8 @@ class BehavioralMixin(BaseSecurityMixin):
|
|
|
47
47
|
|
|
48
48
|
def behavior_analysis(
|
|
49
49
|
self, rules: list[BehaviorRule]
|
|
50
|
-
) -> Callable[[Callable[..., Any]],
|
|
51
|
-
def decorator(func: Callable[..., Any]) ->
|
|
50
|
+
) -> Callable[[Callable[..., Any]], DecoratedFunction]:
|
|
51
|
+
def decorator(func: Callable[..., Any]) -> DecoratedFunction:
|
|
52
52
|
route_config = self._ensure_route_config(func)
|
|
53
53
|
route_config.behavior_rules.extend(rules)
|
|
54
54
|
return self._apply_route_config(func)
|
|
@@ -60,8 +60,8 @@ class BehavioralMixin(BaseSecurityMixin):
|
|
|
60
60
|
max_frequency: float,
|
|
61
61
|
window: int = 300,
|
|
62
62
|
action: Literal["ban", "log", "throttle", "alert"] = "ban",
|
|
63
|
-
) -> Callable[[Callable[..., Any]],
|
|
64
|
-
def decorator(func: Callable[..., Any]) ->
|
|
63
|
+
) -> Callable[[Callable[..., Any]], DecoratedFunction]:
|
|
64
|
+
def decorator(func: Callable[..., Any]) -> DecoratedFunction:
|
|
65
65
|
route_config = self._ensure_route_config(func)
|
|
66
66
|
max_calls = int(max_frequency * window)
|
|
67
67
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from collections.abc import Awaitable, Callable
|
|
2
2
|
from typing import Any
|
|
3
3
|
|
|
4
|
-
from guard_core.decorators.base import BaseSecurityMixin
|
|
4
|
+
from guard_core.decorators.base import BaseSecurityMixin, DecoratedFunction
|
|
5
5
|
from guard_core.protocols.request_protocol import GuardRequest
|
|
6
6
|
from guard_core.protocols.response_protocol import GuardResponse
|
|
7
7
|
|
|
@@ -9,8 +9,8 @@ from guard_core.protocols.response_protocol import GuardResponse
|
|
|
9
9
|
class ContentFilteringMixin(BaseSecurityMixin):
|
|
10
10
|
def block_user_agents(
|
|
11
11
|
self, patterns: list[str]
|
|
12
|
-
) -> Callable[[Callable[..., Any]],
|
|
13
|
-
def decorator(func: Callable[..., Any]) ->
|
|
12
|
+
) -> Callable[[Callable[..., Any]], DecoratedFunction]:
|
|
13
|
+
def decorator(func: Callable[..., Any]) -> DecoratedFunction:
|
|
14
14
|
route_config = self._ensure_route_config(func)
|
|
15
15
|
route_config.blocked_user_agents.extend(patterns)
|
|
16
16
|
return self._apply_route_config(func)
|
|
@@ -19,8 +19,8 @@ class ContentFilteringMixin(BaseSecurityMixin):
|
|
|
19
19
|
|
|
20
20
|
def content_type_filter(
|
|
21
21
|
self, allowed_types: list[str]
|
|
22
|
-
) -> Callable[[Callable[..., Any]],
|
|
23
|
-
def decorator(func: Callable[..., Any]) ->
|
|
22
|
+
) -> Callable[[Callable[..., Any]], DecoratedFunction]:
|
|
23
|
+
def decorator(func: Callable[..., Any]) -> DecoratedFunction:
|
|
24
24
|
route_config = self._ensure_route_config(func)
|
|
25
25
|
route_config.allowed_content_types = allowed_types
|
|
26
26
|
return self._apply_route_config(func)
|
|
@@ -29,8 +29,8 @@ class ContentFilteringMixin(BaseSecurityMixin):
|
|
|
29
29
|
|
|
30
30
|
def max_request_size(
|
|
31
31
|
self, size_bytes: int
|
|
32
|
-
) -> Callable[[Callable[..., Any]],
|
|
33
|
-
def decorator(func: Callable[..., Any]) ->
|
|
32
|
+
) -> Callable[[Callable[..., Any]], DecoratedFunction]:
|
|
33
|
+
def decorator(func: Callable[..., Any]) -> DecoratedFunction:
|
|
34
34
|
route_config = self._ensure_route_config(func)
|
|
35
35
|
route_config.max_request_size = size_bytes
|
|
36
36
|
return self._apply_route_config(func)
|
|
@@ -39,8 +39,8 @@ class ContentFilteringMixin(BaseSecurityMixin):
|
|
|
39
39
|
|
|
40
40
|
def require_referrer(
|
|
41
41
|
self, allowed_domains: list[str]
|
|
42
|
-
) -> Callable[[Callable[..., Any]],
|
|
43
|
-
def decorator(func: Callable[..., Any]) ->
|
|
42
|
+
) -> Callable[[Callable[..., Any]], DecoratedFunction]:
|
|
43
|
+
def decorator(func: Callable[..., Any]) -> DecoratedFunction:
|
|
44
44
|
route_config = self._ensure_route_config(func)
|
|
45
45
|
route_config.require_referrer = allowed_domains
|
|
46
46
|
return self._apply_route_config(func)
|
|
@@ -50,8 +50,8 @@ class ContentFilteringMixin(BaseSecurityMixin):
|
|
|
50
50
|
def custom_validation(
|
|
51
51
|
self,
|
|
52
52
|
validator: Callable[[GuardRequest], Awaitable[GuardResponse | None]],
|
|
53
|
-
) -> Callable[[Callable[..., Any]],
|
|
54
|
-
def decorator(func: Callable[..., Any]) ->
|
|
53
|
+
) -> Callable[[Callable[..., Any]], DecoratedFunction]:
|
|
54
|
+
def decorator(func: Callable[..., Any]) -> DecoratedFunction:
|
|
55
55
|
route_config = self._ensure_route_config(func)
|
|
56
56
|
route_config.custom_validators.append(validator)
|
|
57
57
|
return self._apply_route_config(func)
|
|
@@ -64,8 +64,8 @@ class ContentFilteringMixin(BaseSecurityMixin):
|
|
|
64
64
|
params: set[str] | None = None,
|
|
65
65
|
body_fields: set[str] | None = None,
|
|
66
66
|
categories: set[str] | None = None,
|
|
67
|
-
) -> Callable[[Callable[..., Any]],
|
|
68
|
-
def decorator(func: Callable[..., Any]) ->
|
|
67
|
+
) -> Callable[[Callable[..., Any]], DecoratedFunction]:
|
|
68
|
+
def decorator(func: Callable[..., Any]) -> DecoratedFunction:
|
|
69
69
|
route_config = self._ensure_route_config(func)
|
|
70
70
|
if headers is not None:
|
|
71
71
|
route_config.excluded_detection_headers = set(headers)
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
from collections.abc import Callable
|
|
2
2
|
from typing import Any
|
|
3
3
|
|
|
4
|
-
from guard_core.decorators.base import BaseSecurityMixin
|
|
4
|
+
from guard_core.decorators.base import BaseSecurityMixin, DecoratedFunction
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class RateLimitingMixin(BaseSecurityMixin):
|
|
8
8
|
def rate_limit(
|
|
9
9
|
self, requests: int, window: int = 60
|
|
10
|
-
) -> Callable[[Callable[..., Any]],
|
|
11
|
-
def decorator(func: Callable[..., Any]) ->
|
|
10
|
+
) -> Callable[[Callable[..., Any]], DecoratedFunction]:
|
|
11
|
+
def decorator(func: Callable[..., Any]) -> DecoratedFunction:
|
|
12
12
|
route_config = self._ensure_route_config(func)
|
|
13
13
|
route_config.rate_limit = requests
|
|
14
14
|
route_config.rate_limit_window = window
|
|
@@ -18,8 +18,8 @@ class RateLimitingMixin(BaseSecurityMixin):
|
|
|
18
18
|
|
|
19
19
|
def geo_rate_limit(
|
|
20
20
|
self, limits: dict[str, tuple[int, int]]
|
|
21
|
-
) -> Callable[[Callable[..., Any]],
|
|
22
|
-
def decorator(func: Callable[..., Any]) ->
|
|
21
|
+
) -> Callable[[Callable[..., Any]], DecoratedFunction]:
|
|
22
|
+
def decorator(func: Callable[..., Any]) -> DecoratedFunction:
|
|
23
23
|
route_config = self._ensure_route_config(func)
|
|
24
24
|
route_config.geo_rate_limits = limits
|
|
25
25
|
return self._apply_route_config(func)
|
|
@@ -21,7 +21,7 @@ class PatternCompiler:
|
|
|
21
21
|
async def compile_pattern(
|
|
22
22
|
self, pattern: str, flags: int = re.IGNORECASE | re.MULTILINE
|
|
23
23
|
) -> re.Pattern:
|
|
24
|
-
cache_key = f"{
|
|
24
|
+
cache_key = f"{pattern}:{flags}"
|
|
25
25
|
|
|
26
26
|
if cache_key in self._compiled_cache:
|
|
27
27
|
async with self._lock:
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import binascii
|
|
1
2
|
import re
|
|
2
3
|
import unicodedata
|
|
3
4
|
from typing import Any
|
|
@@ -26,7 +27,7 @@ class ContentPreprocessor:
|
|
|
26
27
|
r"eval\s*\(",
|
|
27
28
|
r"exec\s*\(",
|
|
28
29
|
r"system\s*\(",
|
|
29
|
-
r"
|
|
30
|
+
r"<\?php",
|
|
30
31
|
r"<%",
|
|
31
32
|
r"{{",
|
|
32
33
|
r"{%",
|
|
@@ -44,6 +45,20 @@ class ContentPreprocessor:
|
|
|
44
45
|
re.compile(pattern, re.IGNORECASE) for pattern in self.attack_indicators
|
|
45
46
|
]
|
|
46
47
|
|
|
48
|
+
_BASE64_RE = re.compile(
|
|
49
|
+
r"(?<![A-Za-z0-9+/])[A-Za-z0-9+/]{20,}={0,2}(?![A-Za-z0-9+/=])"
|
|
50
|
+
)
|
|
51
|
+
_HEX_ESCAPE_RE = re.compile(r"\\x([0-9a-fA-F]{2})")
|
|
52
|
+
_UNICODE_ESCAPE_RE = re.compile(r"\\u([0-9a-fA-F]{4})")
|
|
53
|
+
_SQL_BLOCK_COMMENT_INNER_UPPER_RE = re.compile(
|
|
54
|
+
r"(?<=[A-Z])/\*.*?\*/(?=[A-Z])", re.DOTALL
|
|
55
|
+
)
|
|
56
|
+
_SQL_BLOCK_COMMENT_INNER_LOWER_RE = re.compile(
|
|
57
|
+
r"(?<=[a-z])/\*.*?\*/(?=[a-z])", re.DOTALL
|
|
58
|
+
)
|
|
59
|
+
_SQL_BLOCK_COMMENT_RE = re.compile(r"/\*.*?\*/", re.DOTALL)
|
|
60
|
+
_SQL_LINE_COMMENT_RE = re.compile(r"(--|#)[^\n]*")
|
|
61
|
+
|
|
47
62
|
async def _send_preprocessor_event(
|
|
48
63
|
self,
|
|
49
64
|
event_type: str,
|
|
@@ -174,32 +189,25 @@ class ContentPreprocessor:
|
|
|
174
189
|
|
|
175
190
|
return result
|
|
176
191
|
|
|
177
|
-
def _add_non_attack_content(
|
|
178
|
-
self,
|
|
179
|
-
content: str,
|
|
180
|
-
attack_regions: list[tuple[int, int]],
|
|
181
|
-
result_parts: list[str],
|
|
182
|
-
remaining: int,
|
|
183
|
-
) -> None:
|
|
184
|
-
last_end = 0
|
|
185
|
-
for start, end in attack_regions:
|
|
186
|
-
if last_end < start and remaining > 0:
|
|
187
|
-
chunk_len = min(start - last_end, remaining)
|
|
188
|
-
result_parts.insert(0, content[last_end : last_end + chunk_len])
|
|
189
|
-
remaining -= chunk_len
|
|
190
|
-
last_end = end
|
|
191
|
-
|
|
192
192
|
def _build_result_with_attack_regions_and_context(
|
|
193
193
|
self, content: str, attack_regions: list[tuple[int, int]]
|
|
194
194
|
) -> str:
|
|
195
195
|
attack_length = sum(end - start for start, end in attack_regions)
|
|
196
|
-
|
|
197
|
-
|
|
196
|
+
gap_budget = self.max_content_length - attack_length
|
|
197
|
+
result_parts: list[str] = []
|
|
198
|
+
last_end = 0
|
|
198
199
|
|
|
199
200
|
for start, end in attack_regions:
|
|
201
|
+
if last_end < start and gap_budget > 0:
|
|
202
|
+
chunk_len = min(start - last_end, gap_budget)
|
|
203
|
+
result_parts.append(content[last_end : last_end + chunk_len])
|
|
204
|
+
gap_budget -= chunk_len
|
|
200
205
|
result_parts.append(content[start:end])
|
|
206
|
+
last_end = end
|
|
201
207
|
|
|
202
|
-
|
|
208
|
+
if last_end < len(content) and gap_budget > 0:
|
|
209
|
+
tail_len = min(len(content) - last_end, gap_budget)
|
|
210
|
+
result_parts.append(content[last_end : last_end + tail_len])
|
|
203
211
|
|
|
204
212
|
return "".join(result_parts)
|
|
205
213
|
|
|
@@ -231,6 +239,50 @@ class ContentPreprocessor:
|
|
|
231
239
|
translator = str.maketrans("", "", control_chars)
|
|
232
240
|
return content.translate(translator)
|
|
233
241
|
|
|
242
|
+
def _decode_base64_candidates(self, content: str) -> str:
|
|
243
|
+
import base64
|
|
244
|
+
|
|
245
|
+
def _replace(match: re.Match[str]) -> str:
|
|
246
|
+
token = match.group(0)
|
|
247
|
+
padding = (4 - len(token) % 4) % 4
|
|
248
|
+
padded = token + "=" * padding
|
|
249
|
+
try:
|
|
250
|
+
decoded = base64.b64decode(padded, validate=True).decode(
|
|
251
|
+
"utf-8", errors="ignore"
|
|
252
|
+
)
|
|
253
|
+
except (ValueError, binascii.Error):
|
|
254
|
+
return token
|
|
255
|
+
if decoded and any(c.isprintable() for c in decoded):
|
|
256
|
+
return decoded
|
|
257
|
+
return token
|
|
258
|
+
|
|
259
|
+
return self._BASE64_RE.sub(_replace, content)
|
|
260
|
+
|
|
261
|
+
def _decode_hex_escapes(self, content: str) -> str:
|
|
262
|
+
def _replace(match: re.Match[str]) -> str:
|
|
263
|
+
try:
|
|
264
|
+
return chr(int(match.group(1), 16))
|
|
265
|
+
except ValueError:
|
|
266
|
+
return match.group(0)
|
|
267
|
+
|
|
268
|
+
return self._HEX_ESCAPE_RE.sub(_replace, content)
|
|
269
|
+
|
|
270
|
+
def _decode_unicode_escapes(self, content: str) -> str:
|
|
271
|
+
def _replace(match: re.Match[str]) -> str:
|
|
272
|
+
try:
|
|
273
|
+
return chr(int(match.group(1), 16))
|
|
274
|
+
except ValueError:
|
|
275
|
+
return match.group(0)
|
|
276
|
+
|
|
277
|
+
return self._UNICODE_ESCAPE_RE.sub(_replace, content)
|
|
278
|
+
|
|
279
|
+
def _strip_sql_comments(self, content: str) -> str:
|
|
280
|
+
content = self._SQL_BLOCK_COMMENT_INNER_UPPER_RE.sub("", content)
|
|
281
|
+
content = self._SQL_BLOCK_COMMENT_INNER_LOWER_RE.sub("", content)
|
|
282
|
+
content = self._SQL_BLOCK_COMMENT_RE.sub(" ", content)
|
|
283
|
+
content = self._SQL_LINE_COMMENT_RE.sub(" ", content)
|
|
284
|
+
return content
|
|
285
|
+
|
|
234
286
|
async def decode_common_encodings(self, content: str) -> str:
|
|
235
287
|
max_decode_iterations = 3
|
|
236
288
|
iterations = 0
|
|
@@ -268,11 +320,16 @@ class ContentPreprocessor:
|
|
|
268
320
|
error_type="html_decode",
|
|
269
321
|
)
|
|
270
322
|
|
|
323
|
+
content = self._decode_hex_escapes(content)
|
|
324
|
+
content = self._decode_unicode_escapes(content)
|
|
325
|
+
content = self._decode_base64_candidates(content)
|
|
326
|
+
|
|
271
327
|
if content == original:
|
|
272
328
|
break
|
|
273
329
|
|
|
274
330
|
iterations += 1
|
|
275
331
|
|
|
332
|
+
content = self._strip_sql_comments(content)
|
|
276
333
|
return content
|
|
277
334
|
|
|
278
335
|
async def preprocess(self, content: str) -> str:
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from .behavior_handler import BehaviorTracker
|
|
2
2
|
from .cloud_handler import CloudManager
|
|
3
|
+
from .cors_handler import CorsHandler, CorsPreflightResponse, is_preflight
|
|
3
4
|
from .dynamic_rule_handler import DynamicRuleManager
|
|
4
5
|
from .ipban_handler import IPBanManager
|
|
5
6
|
from .ipinfo_handler import IPInfoManager
|
|
@@ -11,6 +12,8 @@ from .suspatterns_handler import SusPatternsManager
|
|
|
11
12
|
__all__ = [
|
|
12
13
|
"BehaviorTracker",
|
|
13
14
|
"CloudManager",
|
|
15
|
+
"CorsHandler",
|
|
16
|
+
"CorsPreflightResponse",
|
|
14
17
|
"DynamicRuleManager",
|
|
15
18
|
"IPBanManager",
|
|
16
19
|
"IPInfoManager",
|
|
@@ -18,4 +21,5 @@ __all__ = [
|
|
|
18
21
|
"RedisManager",
|
|
19
22
|
"SecurityHeadersManager",
|
|
20
23
|
"SusPatternsManager",
|
|
24
|
+
"is_preflight",
|
|
21
25
|
]
|