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.
Files changed (232) hide show
  1. {guard_core-2.0.0/guard_core.egg-info → guard_core-2.2.0}/PKG-INFO +1 -1
  2. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/implementations/cloud_ip_refresh.py +0 -13
  3. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/pipeline.py +1 -1
  4. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/initialization/handler_initializer.py +35 -12
  5. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/decorators/access_control.py +11 -11
  6. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/decorators/advanced.py +7 -7
  7. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/decorators/authentication.py +9 -9
  8. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/decorators/base.py +12 -5
  9. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/decorators/behavioral.py +9 -9
  10. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/decorators/content_filtering.py +13 -13
  11. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/decorators/rate_limiting.py +5 -5
  12. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/detection_engine/compiler.py +1 -1
  13. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/detection_engine/preprocessor.py +76 -19
  14. {guard_core-2.0.0/guard_core/sync → guard_core-2.2.0/guard_core}/handlers/__init__.py +4 -0
  15. guard_core-2.2.0/guard_core/handlers/cors_handler.py +169 -0
  16. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/handlers/dynamic_rule_handler.py +44 -11
  17. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/handlers/ipban_handler.py +99 -12
  18. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/models.py +8 -0
  19. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/implementations/cloud_ip_refresh.py +0 -13
  20. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/pipeline.py +1 -1
  21. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/initialization/handler_initializer.py +37 -11
  22. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/decorators/access_control.py +11 -11
  23. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/decorators/advanced.py +7 -7
  24. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/decorators/authentication.py +9 -9
  25. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/decorators/base.py +12 -5
  26. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/decorators/behavioral.py +9 -9
  27. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/decorators/content_filtering.py +13 -13
  28. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/decorators/rate_limiting.py +5 -5
  29. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/detection_engine/compiler.py +1 -1
  30. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/detection_engine/preprocessor.py +76 -19
  31. {guard_core-2.0.0/guard_core → guard_core-2.2.0/guard_core/sync}/handlers/__init__.py +4 -0
  32. guard_core-2.2.0/guard_core/sync/handlers/cors_handler.py +169 -0
  33. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/handlers/dynamic_rule_handler.py +44 -11
  34. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/handlers/ipban_handler.py +99 -12
  35. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/handlers/ratelimit_handler.py +11 -7
  36. {guard_core-2.0.0 → guard_core-2.2.0/guard_core.egg-info}/PKG-INFO +1 -1
  37. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core.egg-info/SOURCES.txt +10 -0
  38. {guard_core-2.0.0 → guard_core-2.2.0}/pyproject.toml +5 -1
  39. {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_check_log_muting.py +4 -2
  40. {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_cloud_ip_refresh_on_demand.py +65 -14
  41. guard_core-2.2.0/tests/test_compiler_cache.py +333 -0
  42. {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_composite_enricher.py +7 -6
  43. {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_composite_handler_extra.py +24 -12
  44. guard_core-2.2.0/tests/test_cors_handler.py +236 -0
  45. {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_detection_exclusion_integration.py +40 -6
  46. {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_detection_result_propagation.py +16 -7
  47. guard_core-2.2.0/tests/test_dynamic_rule_atomicity.py +115 -0
  48. {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_enricher_otel_integration.py +11 -6
  49. {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_enricher_rule_correlation.py +1 -1
  50. {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_handler_edge_cases.py +1 -1
  51. guard_core-2.2.0/tests/test_handler_initializer_lazy_init.py +185 -0
  52. {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_handlers_integration.py +9 -8
  53. guard_core-2.2.0/tests/test_ipban_cidr.py +176 -0
  54. guard_core-2.2.0/tests/test_ipban_ttl.py +76 -0
  55. {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_logfire_handler.py +8 -2
  56. {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_logfire_handler_metric.py +4 -3
  57. {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_otel_handler.py +8 -2
  58. {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_otel_handler_resource_attrs.py +16 -11
  59. guard_core-2.2.0/tests/test_pipeline_fail_secure.py +52 -0
  60. guard_core-2.2.0/tests/test_preprocessor_attack_regions.py +42 -0
  61. guard_core-2.2.0/tests/test_preprocessor_encodings.py +206 -0
  62. {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_telemetry_integration.py +30 -21
  63. guard_core-2.0.0/tests/test_handler_initializer_lazy_init.py +0 -83
  64. {guard_core-2.0.0 → guard_core-2.2.0}/LICENSE +0 -0
  65. {guard_core-2.0.0 → guard_core-2.2.0}/MANIFEST.in +0 -0
  66. {guard_core-2.0.0 → guard_core-2.2.0}/README.md +0 -0
  67. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/__init__.py +0 -0
  68. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/__init__.py +0 -0
  69. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/behavioral/__init__.py +0 -0
  70. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/behavioral/context.py +0 -0
  71. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/behavioral/processor.py +0 -0
  72. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/bypass/__init__.py +0 -0
  73. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/bypass/context.py +0 -0
  74. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/bypass/handler.py +0 -0
  75. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/__init__.py +0 -0
  76. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/base.py +0 -0
  77. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/helpers.py +0 -0
  78. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/implementations/__init__.py +0 -0
  79. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/implementations/authentication.py +0 -0
  80. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/implementations/cloud_provider.py +0 -0
  81. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/implementations/custom_request.py +0 -0
  82. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/implementations/custom_validators.py +0 -0
  83. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/implementations/emergency_mode.py +0 -0
  84. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/implementations/https_enforcement.py +0 -0
  85. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/implementations/ip_security.py +0 -0
  86. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/implementations/rate_limit.py +0 -0
  87. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/implementations/referrer.py +0 -0
  88. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/implementations/request_logging.py +0 -0
  89. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/implementations/request_size_content.py +0 -0
  90. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/implementations/required_headers.py +0 -0
  91. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/implementations/route_config.py +0 -0
  92. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/implementations/suspicious_activity.py +0 -0
  93. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/implementations/time_window.py +0 -0
  94. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/checks/implementations/user_agent.py +0 -0
  95. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/events/__init__.py +0 -0
  96. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/events/composite_handler.py +0 -0
  97. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/events/enricher.py +0 -0
  98. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/events/event_types.py +0 -0
  99. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/events/logfire_handler.py +0 -0
  100. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/events/metrics.py +0 -0
  101. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/events/middleware_events.py +0 -0
  102. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/events/otel_handler.py +0 -0
  103. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/initialization/__init__.py +0 -0
  104. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/responses/__init__.py +0 -0
  105. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/responses/context.py +0 -0
  106. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/responses/factory.py +0 -0
  107. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/routing/__init__.py +0 -0
  108. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/routing/context.py +0 -0
  109. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/routing/resolver.py +0 -0
  110. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/validation/__init__.py +0 -0
  111. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/validation/context.py +0 -0
  112. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/core/validation/validator.py +0 -0
  113. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/decorators/__init__.py +0 -0
  114. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/detection_engine/__init__.py +0 -0
  115. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/detection_engine/monitor.py +0 -0
  116. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/detection_engine/semantic.py +0 -0
  117. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/detection_result.py +0 -0
  118. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/exceptions.py +0 -0
  119. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/handlers/behavior_handler.py +0 -0
  120. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/handlers/cloud_handler.py +0 -0
  121. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/handlers/cloud_ip_stores.py +0 -0
  122. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/handlers/ipinfo_handler.py +0 -0
  123. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/handlers/ratelimit_handler.py +0 -0
  124. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/handlers/redis_handler.py +0 -0
  125. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/handlers/security_headers_handler.py +0 -0
  126. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/handlers/suspatterns_handler.py +0 -0
  127. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/protocols/__init__.py +0 -0
  128. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/protocols/agent_protocol.py +0 -0
  129. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/protocols/cloud_ip_store_protocol.py +0 -0
  130. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/protocols/geo_ip_protocol.py +0 -0
  131. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/protocols/middleware_protocol.py +0 -0
  132. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/protocols/redis_protocol.py +0 -0
  133. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/protocols/request_protocol.py +0 -0
  134. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/protocols/response_protocol.py +0 -0
  135. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/py.typed +0 -0
  136. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/scripts/__init__.py +0 -0
  137. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/scripts/rate_lua.py +0 -0
  138. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/__init__.py +0 -0
  139. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/__init__.py +0 -0
  140. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/behavioral/__init__.py +0 -0
  141. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/behavioral/context.py +0 -0
  142. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/behavioral/processor.py +0 -0
  143. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/bypass/__init__.py +0 -0
  144. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/bypass/context.py +0 -0
  145. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/bypass/handler.py +0 -0
  146. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/__init__.py +0 -0
  147. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/base.py +0 -0
  148. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/helpers.py +0 -0
  149. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/implementations/__init__.py +0 -0
  150. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/implementations/authentication.py +0 -0
  151. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/implementations/cloud_provider.py +0 -0
  152. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/implementations/custom_request.py +0 -0
  153. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/implementations/custom_validators.py +0 -0
  154. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/implementations/emergency_mode.py +0 -0
  155. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/implementations/https_enforcement.py +0 -0
  156. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/implementations/ip_security.py +0 -0
  157. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/implementations/rate_limit.py +0 -0
  158. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/implementations/referrer.py +0 -0
  159. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/implementations/request_logging.py +0 -0
  160. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/implementations/request_size_content.py +0 -0
  161. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/implementations/required_headers.py +0 -0
  162. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/implementations/route_config.py +0 -0
  163. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/implementations/suspicious_activity.py +0 -0
  164. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/implementations/time_window.py +0 -0
  165. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/checks/implementations/user_agent.py +0 -0
  166. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/events/__init__.py +0 -0
  167. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/events/composite_handler.py +0 -0
  168. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/events/enricher.py +0 -0
  169. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/events/event_types.py +0 -0
  170. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/events/logfire_handler.py +0 -0
  171. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/events/metrics.py +0 -0
  172. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/events/middleware_events.py +0 -0
  173. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/events/otel_handler.py +0 -0
  174. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/initialization/__init__.py +0 -0
  175. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/responses/__init__.py +0 -0
  176. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/responses/context.py +0 -0
  177. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/responses/factory.py +0 -0
  178. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/routing/__init__.py +0 -0
  179. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/routing/context.py +0 -0
  180. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/routing/resolver.py +0 -0
  181. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/validation/__init__.py +0 -0
  182. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/validation/context.py +0 -0
  183. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/core/validation/validator.py +0 -0
  184. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/decorators/__init__.py +0 -0
  185. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/detection_engine/__init__.py +0 -0
  186. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/detection_engine/monitor.py +0 -0
  187. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/detection_engine/semantic.py +0 -0
  188. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/detection_result.py +0 -0
  189. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/handlers/behavior_handler.py +0 -0
  190. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/handlers/cloud_handler.py +0 -0
  191. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/handlers/cloud_ip_stores.py +0 -0
  192. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/handlers/ipinfo_handler.py +0 -0
  193. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/handlers/redis_handler.py +0 -0
  194. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/handlers/security_headers_handler.py +0 -0
  195. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/handlers/suspatterns_handler.py +0 -0
  196. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/protocols/__init__.py +0 -0
  197. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/protocols/agent_protocol.py +0 -0
  198. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/protocols/cloud_ip_store_protocol.py +0 -0
  199. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/protocols/geo_ip_protocol.py +0 -0
  200. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/protocols/middleware_protocol.py +0 -0
  201. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/protocols/redis_protocol.py +0 -0
  202. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/protocols/request_protocol.py +0 -0
  203. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/protocols/response_protocol.py +0 -0
  204. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/py.typed +0 -0
  205. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/scripts/__init__.py +0 -0
  206. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/scripts/rate_lua.py +0 -0
  207. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/sync/utils.py +0 -0
  208. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core/utils.py +0 -0
  209. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core.egg-info/dependency_links.txt +0 -0
  210. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core.egg-info/requires.txt +0 -0
  211. {guard_core-2.0.0 → guard_core-2.2.0}/guard_core.egg-info/top_level.txt +0 -0
  212. {guard_core-2.0.0 → guard_core-2.2.0}/setup.cfg +0 -0
  213. {guard_core-2.0.0 → guard_core-2.2.0}/setup.py +0 -0
  214. {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_behavior_rule_ban_duration.py +0 -0
  215. {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_cloud_ip_stores.py +0 -0
  216. {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_composite_handler.py +0 -0
  217. {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_detection_categories.py +0 -0
  218. {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_detection_result.py +0 -0
  219. {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_enricher.py +0 -0
  220. {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_enricher_behavior_correlation.py +0 -0
  221. {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_enricher_end_to_end.py +0 -0
  222. {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_enricher_identity.py +0 -0
  223. {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_enricher_logfire_integration.py +0 -0
  224. {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_enricher_threat_score.py +0 -0
  225. {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_event_bus_filtering.py +0 -0
  226. {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_event_types.py +0 -0
  227. {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_handler_initializer.py +0 -0
  228. {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_handler_initializer_enricher.py +0 -0
  229. {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_handler_initializer_factories.py +0 -0
  230. {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_ipinfo_lifecycle.py +0 -0
  231. {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_suspicious_counts_per_type.py +0 -0
  232. {guard_core-2.0.0 → guard_core-2.2.0}/tests/test_threat_ban_config.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: guard-core
3
- Version: 2.0.0
3
+ Version: 2.2.0
4
4
  Summary: Framework-agnostic security engine for the Guard ecosystem.
5
5
  Author-email: Renzo Franceschini <rennf93@users.noreply.github.com>
6
6
  License: MIT
@@ -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 hasattr(check.config, "fail_secure") and check.config.fail_secure:
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 not self.config.lazy_init and self.config.block_cloud_providers:
125
- await cloud_handler.initialize_redis(
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
- await ip_ban_manager.initialize_redis(self.redis_handler)
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
- if not self.config.lazy_init and self.geo_ip_handler is not None:
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]], Callable[..., Any]]:
13
- def decorator(func: Callable[..., Any]) -> 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]], Callable[..., Any]]:
26
- def decorator(func: Callable[..., Any]) -> 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]], Callable[..., Any]]:
36
- def decorator(func: Callable[..., Any]) -> 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]], Callable[..., Any]]:
46
- def decorator(func: Callable[..., Any]) -> 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]], Callable[..., Any]]:
59
- def decorator(func: Callable[..., Any]) -> 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]], Callable[..., Any]]:
34
- def decorator(func: Callable[..., Any]) -> 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]], Callable[..., Any]]:
48
- def decorator(func: Callable[..., Any]) -> 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]], Callable[..., Any]]:
58
- def decorator(func: Callable[..., Any]) -> 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]], Callable[..., Any]]:
9
- def decorator(func: Callable[..., Any]) -> 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]], Callable[..., Any]]:
19
- def decorator(func: Callable[..., Any]) -> 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]], Callable[..., Any]]:
29
- def decorator(func: Callable[..., Any]) -> 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]], Callable[..., Any]]:
40
- def decorator(func: Callable[..., Any]) -> 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]) -> 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]) -> 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 # type: ignore[attr-defined]
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]], Callable[..., Any]]:
15
- def decorator(func: Callable[..., Any]) -> 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]], Callable[..., Any]]:
33
- def decorator(func: Callable[..., Any]) -> 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]], Callable[..., Any]]:
51
- def decorator(func: Callable[..., Any]) -> 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]], Callable[..., Any]]:
64
- def decorator(func: Callable[..., Any]) -> 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]], Callable[..., Any]]:
13
- def decorator(func: Callable[..., Any]) -> 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]], Callable[..., Any]]:
23
- def decorator(func: Callable[..., Any]) -> 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]], Callable[..., Any]]:
33
- def decorator(func: Callable[..., Any]) -> 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]], Callable[..., Any]]:
43
- def decorator(func: Callable[..., Any]) -> 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]], Callable[..., Any]]:
54
- def decorator(func: Callable[..., Any]) -> 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]], Callable[..., Any]]:
68
- def decorator(func: Callable[..., Any]) -> 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]], Callable[..., Any]]:
11
- def decorator(func: Callable[..., Any]) -> 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]], Callable[..., Any]]:
22
- def decorator(func: Callable[..., Any]) -> 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"{hash(pattern)}:{flags}"
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"<?php",
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
- result_parts = []
197
- remaining = self.max_content_length - attack_length
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
- self._add_non_attack_content(content, attack_regions, result_parts, remaining)
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
  ]