guard-core 2.2.2__tar.gz → 3.1.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 (233) hide show
  1. {guard_core-2.2.2/guard_core.egg-info → guard_core-3.1.0}/PKG-INFO +18 -1
  2. {guard_core-2.2.2 → guard_core-3.1.0}/README.md +17 -0
  3. {guard_core-2.2.2/guard_core/sync → guard_core-3.1.0/guard_core}/core/events/event_types.py +2 -0
  4. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/initialization/handler_initializer.py +30 -12
  5. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/decorators/access_control.py +16 -5
  6. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/decorators/base.py +2 -2
  7. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/handlers/cloud_handler.py +98 -1
  8. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/handlers/cloud_ip_stores.py +1 -1
  9. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/handlers/dynamic_rule_handler.py +25 -8
  10. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/handlers/ratelimit_handler.py +43 -9
  11. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/models.py +76 -20
  12. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/protocols/cloud_ip_store_protocol.py +6 -0
  13. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/initialization/handler_initializer.py +32 -12
  14. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/decorators/access_control.py +16 -5
  15. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/decorators/base.py +2 -2
  16. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/handlers/cloud_handler.py +96 -1
  17. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/handlers/cloud_ip_stores.py +1 -1
  18. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/handlers/dynamic_rule_handler.py +25 -8
  19. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/handlers/ratelimit_handler.py +41 -9
  20. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/protocols/cloud_ip_store_protocol.py +6 -0
  21. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/utils.py +16 -3
  22. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/utils.py +16 -3
  23. {guard_core-2.2.2 → guard_core-3.1.0/guard_core.egg-info}/PKG-INFO +18 -1
  24. {guard_core-2.2.2 → guard_core-3.1.0}/pyproject.toml +1 -1
  25. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_cloud_ip_refresh_on_demand.py +1 -0
  26. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_cloud_ip_stores.py +23 -0
  27. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_dynamic_rule_atomicity.py +1 -1
  28. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_event_types.py +2 -0
  29. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_handler_initializer_lazy_init.py +18 -2
  30. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_pipeline_fail_secure.py +5 -5
  31. {guard_core-2.2.2 → guard_core-3.1.0}/LICENSE +0 -0
  32. {guard_core-2.2.2 → guard_core-3.1.0}/MANIFEST.in +0 -0
  33. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/__init__.py +0 -0
  34. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/__init__.py +0 -0
  35. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/behavioral/__init__.py +0 -0
  36. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/behavioral/context.py +0 -0
  37. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/behavioral/processor.py +0 -0
  38. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/bypass/__init__.py +0 -0
  39. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/bypass/context.py +0 -0
  40. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/bypass/handler.py +0 -0
  41. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/checks/__init__.py +0 -0
  42. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/checks/base.py +0 -0
  43. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/checks/helpers.py +0 -0
  44. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/checks/implementations/__init__.py +0 -0
  45. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/checks/implementations/authentication.py +0 -0
  46. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/checks/implementations/cloud_ip_refresh.py +0 -0
  47. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/checks/implementations/cloud_provider.py +0 -0
  48. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/checks/implementations/custom_request.py +0 -0
  49. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/checks/implementations/custom_validators.py +0 -0
  50. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/checks/implementations/emergency_mode.py +0 -0
  51. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/checks/implementations/https_enforcement.py +0 -0
  52. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/checks/implementations/ip_security.py +0 -0
  53. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/checks/implementations/rate_limit.py +0 -0
  54. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/checks/implementations/referrer.py +0 -0
  55. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/checks/implementations/request_logging.py +0 -0
  56. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/checks/implementations/request_size_content.py +0 -0
  57. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/checks/implementations/required_headers.py +0 -0
  58. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/checks/implementations/route_config.py +0 -0
  59. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/checks/implementations/suspicious_activity.py +0 -0
  60. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/checks/implementations/time_window.py +0 -0
  61. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/checks/implementations/user_agent.py +0 -0
  62. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/checks/pipeline.py +0 -0
  63. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/events/__init__.py +0 -0
  64. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/events/composite_handler.py +0 -0
  65. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/events/enricher.py +0 -0
  66. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/events/logfire_handler.py +0 -0
  67. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/events/metrics.py +0 -0
  68. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/events/middleware_events.py +0 -0
  69. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/events/otel_handler.py +0 -0
  70. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/initialization/__init__.py +0 -0
  71. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/responses/__init__.py +0 -0
  72. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/responses/context.py +0 -0
  73. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/responses/factory.py +0 -0
  74. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/routing/__init__.py +0 -0
  75. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/routing/context.py +0 -0
  76. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/routing/resolver.py +0 -0
  77. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/validation/__init__.py +0 -0
  78. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/validation/context.py +0 -0
  79. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/core/validation/validator.py +0 -0
  80. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/decorators/__init__.py +0 -0
  81. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/decorators/advanced.py +0 -0
  82. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/decorators/authentication.py +0 -0
  83. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/decorators/behavioral.py +0 -0
  84. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/decorators/content_filtering.py +0 -0
  85. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/decorators/rate_limiting.py +0 -0
  86. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/detection_engine/__init__.py +0 -0
  87. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/detection_engine/compiler.py +0 -0
  88. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/detection_engine/monitor.py +0 -0
  89. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/detection_engine/preprocessor.py +0 -0
  90. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/detection_engine/semantic.py +0 -0
  91. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/detection_result.py +0 -0
  92. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/exceptions.py +0 -0
  93. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/handlers/__init__.py +0 -0
  94. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/handlers/behavior_handler.py +0 -0
  95. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/handlers/cors_handler.py +0 -0
  96. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/handlers/ipban_handler.py +0 -0
  97. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/handlers/ipinfo_handler.py +0 -0
  98. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/handlers/redis_handler.py +0 -0
  99. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/handlers/security_headers_handler.py +0 -0
  100. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/handlers/suspatterns_handler.py +0 -0
  101. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/protocols/__init__.py +0 -0
  102. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/protocols/agent_protocol.py +0 -0
  103. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/protocols/geo_ip_protocol.py +0 -0
  104. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/protocols/middleware_protocol.py +0 -0
  105. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/protocols/redis_protocol.py +0 -0
  106. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/protocols/request_protocol.py +0 -0
  107. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/protocols/response_protocol.py +0 -0
  108. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/py.typed +0 -0
  109. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/scripts/__init__.py +0 -0
  110. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/scripts/rate_lua.py +0 -0
  111. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/__init__.py +0 -0
  112. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/__init__.py +0 -0
  113. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/behavioral/__init__.py +0 -0
  114. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/behavioral/context.py +0 -0
  115. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/behavioral/processor.py +0 -0
  116. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/bypass/__init__.py +0 -0
  117. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/bypass/context.py +0 -0
  118. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/bypass/handler.py +0 -0
  119. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/checks/__init__.py +0 -0
  120. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/checks/base.py +0 -0
  121. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/checks/helpers.py +0 -0
  122. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/checks/implementations/__init__.py +0 -0
  123. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/checks/implementations/authentication.py +0 -0
  124. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/checks/implementations/cloud_ip_refresh.py +0 -0
  125. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/checks/implementations/cloud_provider.py +0 -0
  126. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/checks/implementations/custom_request.py +0 -0
  127. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/checks/implementations/custom_validators.py +0 -0
  128. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/checks/implementations/emergency_mode.py +0 -0
  129. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/checks/implementations/https_enforcement.py +0 -0
  130. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/checks/implementations/ip_security.py +0 -0
  131. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/checks/implementations/rate_limit.py +0 -0
  132. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/checks/implementations/referrer.py +0 -0
  133. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/checks/implementations/request_logging.py +0 -0
  134. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/checks/implementations/request_size_content.py +0 -0
  135. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/checks/implementations/required_headers.py +0 -0
  136. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/checks/implementations/route_config.py +0 -0
  137. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/checks/implementations/suspicious_activity.py +0 -0
  138. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/checks/implementations/time_window.py +0 -0
  139. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/checks/implementations/user_agent.py +0 -0
  140. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/checks/pipeline.py +0 -0
  141. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/events/__init__.py +0 -0
  142. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/events/composite_handler.py +0 -0
  143. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/events/enricher.py +0 -0
  144. {guard_core-2.2.2/guard_core → guard_core-3.1.0/guard_core/sync}/core/events/event_types.py +0 -0
  145. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/events/logfire_handler.py +0 -0
  146. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/events/metrics.py +0 -0
  147. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/events/middleware_events.py +0 -0
  148. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/events/otel_handler.py +0 -0
  149. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/initialization/__init__.py +0 -0
  150. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/responses/__init__.py +0 -0
  151. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/responses/context.py +0 -0
  152. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/responses/factory.py +0 -0
  153. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/routing/__init__.py +0 -0
  154. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/routing/context.py +0 -0
  155. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/routing/resolver.py +0 -0
  156. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/validation/__init__.py +0 -0
  157. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/validation/context.py +0 -0
  158. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/core/validation/validator.py +0 -0
  159. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/decorators/__init__.py +0 -0
  160. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/decorators/advanced.py +0 -0
  161. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/decorators/authentication.py +0 -0
  162. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/decorators/behavioral.py +0 -0
  163. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/decorators/content_filtering.py +0 -0
  164. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/decorators/rate_limiting.py +0 -0
  165. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/detection_engine/__init__.py +0 -0
  166. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/detection_engine/compiler.py +0 -0
  167. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/detection_engine/monitor.py +0 -0
  168. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/detection_engine/preprocessor.py +0 -0
  169. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/detection_engine/semantic.py +0 -0
  170. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/detection_result.py +0 -0
  171. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/handlers/__init__.py +0 -0
  172. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/handlers/behavior_handler.py +0 -0
  173. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/handlers/cors_handler.py +0 -0
  174. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/handlers/ipban_handler.py +0 -0
  175. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/handlers/ipinfo_handler.py +0 -0
  176. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/handlers/redis_handler.py +0 -0
  177. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/handlers/security_headers_handler.py +0 -0
  178. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/handlers/suspatterns_handler.py +0 -0
  179. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/protocols/__init__.py +0 -0
  180. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/protocols/agent_protocol.py +0 -0
  181. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/protocols/geo_ip_protocol.py +0 -0
  182. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/protocols/middleware_protocol.py +0 -0
  183. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/protocols/redis_protocol.py +0 -0
  184. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/protocols/request_protocol.py +0 -0
  185. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/protocols/response_protocol.py +0 -0
  186. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/py.typed +0 -0
  187. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/scripts/__init__.py +0 -0
  188. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core/sync/scripts/rate_lua.py +0 -0
  189. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core.egg-info/SOURCES.txt +0 -0
  190. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core.egg-info/dependency_links.txt +0 -0
  191. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core.egg-info/requires.txt +0 -0
  192. {guard_core-2.2.2 → guard_core-3.1.0}/guard_core.egg-info/top_level.txt +0 -0
  193. {guard_core-2.2.2 → guard_core-3.1.0}/setup.cfg +0 -0
  194. {guard_core-2.2.2 → guard_core-3.1.0}/setup.py +0 -0
  195. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_behavior_rule_ban_duration.py +0 -0
  196. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_check_log_muting.py +0 -0
  197. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_compiler_cache.py +0 -0
  198. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_composite_enricher.py +0 -0
  199. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_composite_handler.py +0 -0
  200. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_composite_handler_extra.py +0 -0
  201. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_cors_handler.py +0 -0
  202. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_detection_categories.py +0 -0
  203. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_detection_exclusion_integration.py +0 -0
  204. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_detection_result.py +0 -0
  205. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_detection_result_propagation.py +0 -0
  206. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_enricher.py +0 -0
  207. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_enricher_behavior_correlation.py +0 -0
  208. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_enricher_end_to_end.py +0 -0
  209. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_enricher_identity.py +0 -0
  210. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_enricher_logfire_integration.py +0 -0
  211. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_enricher_otel_integration.py +0 -0
  212. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_enricher_rule_correlation.py +0 -0
  213. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_enricher_threat_score.py +0 -0
  214. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_event_bus_filtering.py +0 -0
  215. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_handler_edge_cases.py +0 -0
  216. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_handler_initializer.py +0 -0
  217. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_handler_initializer_enricher.py +0 -0
  218. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_handler_initializer_factories.py +0 -0
  219. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_handlers_integration.py +0 -0
  220. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_ipban_cidr.py +0 -0
  221. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_ipban_eviction.py +0 -0
  222. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_ipban_lifecycle.py +0 -0
  223. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_ipban_ttl.py +0 -0
  224. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_ipinfo_lifecycle.py +0 -0
  225. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_logfire_handler.py +0 -0
  226. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_logfire_handler_metric.py +0 -0
  227. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_otel_handler.py +0 -0
  228. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_otel_handler_resource_attrs.py +0 -0
  229. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_preprocessor_attack_regions.py +0 -0
  230. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_preprocessor_encodings.py +0 -0
  231. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_suspicious_counts_per_type.py +0 -0
  232. {guard_core-2.2.2 → guard_core-3.1.0}/tests/test_telemetry_integration.py +0 -0
  233. {guard_core-2.2.2 → guard_core-3.1.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.2.2
3
+ Version: 3.1.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
@@ -308,6 +308,23 @@ See the [SecurityConfig Reference](https://rennf93.github.io/guard-core/latest/c
308
308
 
309
309
  ___
310
310
 
311
+ Migration: fail_secure default flipped
312
+ --------------------------------------
313
+
314
+ `SecurityConfig.fail_secure` now defaults to `True`. When a security check raises an unexpected exception, the request is blocked with HTTP 500 instead of falling through.
315
+
316
+ **Why**: the old fail-open default silently masked check bugs. The new default surfaces them so they can be fixed instead of leaking past the security layer.
317
+
318
+ **Migration**: to restore the previous fail-open behavior, opt in explicitly:
319
+
320
+ ```python
321
+ config = SecurityConfig(fail_secure=False)
322
+ ```
323
+
324
+ Recommended path: keep the new default and fix any check exceptions that surface. The old default could mask genuine bugs.
325
+
326
+ ___
327
+
311
328
  Detection Engine
312
329
  ----------------
313
330
 
@@ -250,6 +250,23 @@ See the [SecurityConfig Reference](https://rennf93.github.io/guard-core/latest/c
250
250
 
251
251
  ___
252
252
 
253
+ Migration: fail_secure default flipped
254
+ --------------------------------------
255
+
256
+ `SecurityConfig.fail_secure` now defaults to `True`. When a security check raises an unexpected exception, the request is blocked with HTTP 500 instead of falling through.
257
+
258
+ **Why**: the old fail-open default silently masked check bugs. The new default surfaces them so they can be fixed instead of leaking past the security layer.
259
+
260
+ **Migration**: to restore the previous fail-open behavior, opt in explicitly:
261
+
262
+ ```python
263
+ config = SecurityConfig(fail_secure=False)
264
+ ```
265
+
266
+ Recommended path: keep the new default and fix any check exceptions that surface. The old default could mask genuine bugs.
267
+
268
+ ___
269
+
253
270
  Detection Engine
254
271
  ----------------
255
272
 
@@ -28,6 +28,7 @@ EVENT_PATH_EXCLUDED = "path_excluded"
28
28
  EVENT_PATTERN_ADDED = "pattern_added"
29
29
  EVENT_PATTERN_REMOVED = "pattern_removed"
30
30
  EVENT_RATE_LIMITED = "rate_limited"
31
+ EVENT_RATE_LIMIT_SCRIPT_RELOADED = "rate_limit_script_reloaded"
31
32
  EVENT_REDIS_CONNECTION = "redis_connection"
32
33
  EVENT_REDIS_ERROR = "redis_error"
33
34
  EVENT_SECURITY_BYPASS = "security_bypass"
@@ -61,6 +62,7 @@ EVENT_TYPE_VALUES: frozenset[str] = frozenset(
61
62
  EVENT_PATTERN_ADDED,
62
63
  EVENT_PATTERN_REMOVED,
63
64
  EVENT_RATE_LIMITED,
65
+ EVENT_RATE_LIMIT_SCRIPT_RELOADED,
64
66
  EVENT_REDIS_CONNECTION,
65
67
  EVENT_REDIS_ERROR,
66
68
  EVENT_SECURITY_BYPASS,
@@ -1,6 +1,8 @@
1
1
  import asyncio
2
2
  import logging
3
- from typing import TYPE_CHECKING, Any
3
+ from typing import TYPE_CHECKING, Any, cast
4
+
5
+ from guard_core.protocols.cloud_ip_store_protocol import CloudIpStoreProtocol
4
6
 
5
7
  if TYPE_CHECKING:
6
8
  from guard_core.core.events.metrics import MetricsCollector
@@ -116,21 +118,37 @@ class HandlerInitializer:
116
118
  )
117
119
 
118
120
  async def _run_lazy_init(self) -> None:
119
- try:
120
- from guard_core.handlers.cloud_handler import cloud_handler
121
+ from guard_core.handlers.cloud_handler import cloud_handler
121
122
 
122
- if self.config.block_cloud_providers:
123
+ if self.config.block_cloud_providers:
124
+ try:
123
125
  await cloud_handler.initialize_redis(
124
126
  self.redis_handler,
125
- self.config.block_cloud_providers,
127
+ cast(set[str], self.config.block_cloud_providers),
126
128
  ttl=self.config.cloud_ip_refresh_interval,
127
129
  )
128
- if self.geo_ip_handler is not None:
130
+ except Exception as e:
131
+ self.logger.warning(
132
+ "Lazy cloud-IP initialization failed: %s", e, exc_info=True
133
+ )
134
+
135
+ if self.geo_ip_handler is not None:
136
+ try:
129
137
  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
- )
138
+ except Exception as e:
139
+ self.logger.warning(
140
+ "Lazy geo-IP initialization failed: %s", e, exc_info=True
141
+ )
142
+
143
+ def _resolve_cloud_ip_store(self) -> CloudIpStoreProtocol:
144
+ store = self.config.cloud_ip_store
145
+ needs_invocation = isinstance(store, type) or (
146
+ callable(store) and not isinstance(store, CloudIpStoreProtocol)
147
+ )
148
+ if needs_invocation:
149
+ factory = cast(Any, store)
150
+ return cast(CloudIpStoreProtocol, factory(self.redis_handler))
151
+ return cast(CloudIpStoreProtocol, store)
134
152
 
135
153
  async def initialize_redis_handlers(self) -> None:
136
154
  if not (self.config.enable_redis and self.redis_handler):
@@ -143,7 +161,7 @@ class HandlerInitializer:
143
161
  from guard_core.handlers.suspatterns_handler import sus_patterns_handler
144
162
 
145
163
  if self.config.cloud_ip_store is not None:
146
- cloud_handler.set_store(self.config.cloud_ip_store)
164
+ cloud_handler.set_store(self._resolve_cloud_ip_store())
147
165
 
148
166
  if self.config.lazy_init:
149
167
  self._lazy_init_task = asyncio.create_task(self._run_lazy_init())
@@ -151,7 +169,7 @@ class HandlerInitializer:
151
169
  if self.config.block_cloud_providers:
152
170
  await cloud_handler.initialize_redis(
153
171
  self.redis_handler,
154
- self.config.block_cloud_providers,
172
+ cast(set[str], self.config.block_cloud_providers),
155
173
  ttl=self.config.cloud_ip_refresh_interval,
156
174
  )
157
175
  if self.geo_ip_handler is not None:
@@ -1,7 +1,9 @@
1
+ import logging
1
2
  from collections.abc import Callable
2
- from typing import Any
3
+ from typing import Any, cast
3
4
 
4
5
  from guard_core.decorators.base import BaseSecurityMixin, DecoratedFunction
6
+ from guard_core.models import VALID_CLOUD_PROVIDERS, CloudProvider
5
7
 
6
8
 
7
9
  class AccessControlMixin(BaseSecurityMixin):
@@ -25,7 +27,7 @@ class AccessControlMixin(BaseSecurityMixin):
25
27
  ) -> Callable[[Callable[..., Any]], DecoratedFunction]:
26
28
  def decorator(func: Callable[..., Any]) -> DecoratedFunction:
27
29
  route_config = self._ensure_route_config(func)
28
- route_config.blocked_countries = countries
30
+ route_config.blocked_countries = [c.upper() for c in countries]
29
31
  return self._apply_route_config(func)
30
32
 
31
33
  return decorator
@@ -35,7 +37,7 @@ class AccessControlMixin(BaseSecurityMixin):
35
37
  ) -> Callable[[Callable[..., Any]], DecoratedFunction]:
36
38
  def decorator(func: Callable[..., Any]) -> DecoratedFunction:
37
39
  route_config = self._ensure_route_config(func)
38
- route_config.whitelist_countries = countries
40
+ route_config.whitelist_countries = [c.upper() for c in countries]
39
41
  return self._apply_route_config(func)
40
42
 
41
43
  return decorator
@@ -46,9 +48,18 @@ class AccessControlMixin(BaseSecurityMixin):
46
48
  def decorator(func: Callable[..., Any]) -> DecoratedFunction:
47
49
  route_config = self._ensure_route_config(func)
48
50
  if providers is None:
49
- route_config.block_cloud_providers = {"AWS", "GCP", "Azure"}
51
+ route_config.block_cloud_providers = cast(
52
+ set[CloudProvider], {"AWS", "GCP", "Azure"}
53
+ )
50
54
  else:
51
- route_config.block_cloud_providers = set(providers)
55
+ valid = {p for p in providers if p in VALID_CLOUD_PROVIDERS}
56
+ route_config.block_cloud_providers = cast(set[CloudProvider], valid)
57
+ invalid = set(providers) - valid
58
+ if invalid:
59
+ logging.getLogger("guard_core.decorators").warning(
60
+ "@block_clouds: ignored unknown cloud providers %s",
61
+ sorted(invalid),
62
+ )
52
63
  return self._apply_route_config(func)
53
64
 
54
65
  return decorator
@@ -3,7 +3,7 @@ from datetime import datetime, timezone
3
3
  from typing import Any, Protocol, cast, runtime_checkable
4
4
 
5
5
  from guard_core.handlers.behavior_handler import BehaviorRule, BehaviorTracker
6
- from guard_core.models import SecurityConfig
6
+ from guard_core.models import CloudProvider, SecurityConfig
7
7
  from guard_core.protocols.request_protocol import GuardRequest
8
8
 
9
9
 
@@ -29,7 +29,7 @@ class RouteConfig:
29
29
  self.blocked_user_agents: list[str] = []
30
30
  self.required_headers: dict[str, str] = {}
31
31
  self.behavior_rules: list[BehaviorRule] = []
32
- self.block_cloud_providers: set[str] = set()
32
+ self.block_cloud_providers: set[CloudProvider] = set()
33
33
  self.max_request_size: int | None = None
34
34
  self.allowed_content_types: list[str] | None = None
35
35
  self.time_restrictions: dict[str, str] | None = None
@@ -94,7 +94,92 @@ async def fetch_azure_ip_ranges() -> set[ipaddress.IPv4Network | ipaddress.IPv6N
94
94
  return set()
95
95
 
96
96
 
97
- _ALL_PROVIDERS = set({"AWS", "GCP", "Azure"})
97
+ async def fetch_digitalocean_ip_ranges() -> set[
98
+ ipaddress.IPv4Network | ipaddress.IPv6Network
99
+ ]:
100
+ try:
101
+ async with aiohttp.ClientSession() as session:
102
+ response = await session.get(
103
+ "https://www.digitalocean.com/geo/google.csv",
104
+ timeout=aiohttp.ClientTimeout(total=10),
105
+ )
106
+ response.raise_for_status()
107
+ body = await response.text()
108
+
109
+ networks: set[ipaddress.IPv4Network | ipaddress.IPv6Network] = set()
110
+ for raw_line in body.splitlines():
111
+ line = raw_line.strip()
112
+ if not line or line.startswith("#"):
113
+ continue
114
+ prefix = line.split(",", 1)[0].strip()
115
+ if not prefix:
116
+ continue
117
+ try:
118
+ networks.add(ipaddress.ip_network(prefix))
119
+ except ValueError:
120
+ continue
121
+ return networks
122
+ except Exception as e:
123
+ logging.error(f"Failed to fetch DigitalOcean IP ranges: {str(e)}")
124
+ return set()
125
+
126
+
127
+ async def fetch_linode_ip_ranges() -> set[
128
+ ipaddress.IPv4Network | ipaddress.IPv6Network
129
+ ]:
130
+ try:
131
+ async with aiohttp.ClientSession() as session:
132
+ response = await session.get(
133
+ "https://geoip.linode.com/",
134
+ timeout=aiohttp.ClientTimeout(total=10),
135
+ )
136
+ response.raise_for_status()
137
+ body = await response.text()
138
+
139
+ networks: set[ipaddress.IPv4Network | ipaddress.IPv6Network] = set()
140
+ for raw_line in body.splitlines():
141
+ line = raw_line.strip()
142
+ if not line or line.startswith("#"):
143
+ continue
144
+ prefix = line.split(",", 1)[0].strip()
145
+ if not prefix:
146
+ continue
147
+ try:
148
+ networks.add(ipaddress.ip_network(prefix))
149
+ except ValueError:
150
+ continue
151
+ return networks
152
+ except Exception as e:
153
+ logging.error(f"Failed to fetch Linode IP ranges: {str(e)}")
154
+ return set()
155
+
156
+
157
+ async def fetch_vultr_ip_ranges() -> set[ipaddress.IPv4Network | ipaddress.IPv6Network]:
158
+ try:
159
+ async with aiohttp.ClientSession() as session:
160
+ response = await session.get(
161
+ "https://geofeed.constant.com/?json",
162
+ timeout=aiohttp.ClientTimeout(total=10),
163
+ )
164
+ response.raise_for_status()
165
+ data = await response.json(content_type=None)
166
+
167
+ networks: set[ipaddress.IPv4Network | ipaddress.IPv6Network] = set()
168
+ for entry in data.get("subnets", []):
169
+ prefix = entry.get("ip_prefix")
170
+ if not prefix:
171
+ continue
172
+ try:
173
+ networks.add(ipaddress.ip_network(prefix))
174
+ except ValueError:
175
+ continue
176
+ return networks
177
+ except Exception as e:
178
+ logging.error(f"Failed to fetch Vultr IP ranges: {str(e)}")
179
+ return set()
180
+
181
+
182
+ _ALL_PROVIDERS = set({"AWS", "GCP", "Azure", "DigitalOcean", "Linode", "Vultr"})
98
183
 
99
184
 
100
185
  class CloudManager:
@@ -113,6 +198,9 @@ class CloudManager:
113
198
  "AWS": set(),
114
199
  "GCP": set(),
115
200
  "Azure": set(),
201
+ "DigitalOcean": set(),
202
+ "Linode": set(),
203
+ "Vultr": set(),
116
204
  }
117
205
  cls._instance.last_updated = {provider: None for provider in _ALL_PROVIDERS}
118
206
  cls._instance.redis_handler = None
@@ -146,6 +234,9 @@ class CloudManager:
146
234
  "AWS": fetch_aws_ip_ranges,
147
235
  "GCP": fetch_gcp_ip_ranges,
148
236
  "Azure": fetch_azure_ip_ranges,
237
+ "DigitalOcean": fetch_digitalocean_ip_ranges,
238
+ "Linode": fetch_linode_ip_ranges,
239
+ "Vultr": fetch_vultr_ip_ranges,
149
240
  }[provider]()
150
241
  if ranges:
151
242
  old_ranges = self.ip_ranges.get(provider, set())
@@ -195,6 +286,9 @@ class CloudManager:
195
286
  "AWS": fetch_aws_ip_ranges,
196
287
  "GCP": fetch_gcp_ip_ranges,
197
288
  "Azure": fetch_azure_ip_ranges,
289
+ "DigitalOcean": fetch_digitalocean_ip_ranges,
290
+ "Linode": fetch_linode_ip_ranges,
291
+ "Vultr": fetch_vultr_ip_ranges,
198
292
  }[provider]
199
293
 
200
294
  ranges = await fetch_func()
@@ -233,6 +327,9 @@ class CloudManager:
233
327
  "AWS": fetch_aws_ip_ranges,
234
328
  "GCP": fetch_gcp_ip_ranges,
235
329
  "Azure": fetch_azure_ip_ranges,
330
+ "DigitalOcean": fetch_digitalocean_ip_ranges,
331
+ "Linode": fetch_linode_ip_ranges,
332
+ "Vultr": fetch_vultr_ip_ranges,
236
333
  }[provider]
237
334
  ranges = await fetch_func()
238
335
  if ranges:
@@ -27,7 +27,7 @@ class RedisCloudIpStore:
27
27
  def __init__(
28
28
  self,
29
29
  redis_handler: RedisHandlerProtocol,
30
- key_prefix: str = "guard:cloud_ip",
30
+ key_prefix: str = "cloud_ip",
31
31
  ) -> None:
32
32
  self._redis = redis_handler
33
33
  self._prefix = key_prefix
@@ -3,9 +3,14 @@ import logging
3
3
  import time
4
4
  from copy import deepcopy
5
5
  from datetime import datetime, timezone
6
- from typing import Any
6
+ from typing import Any, cast
7
7
 
8
- from guard_core.models import DynamicRules, SecurityConfig
8
+ from guard_core.models import (
9
+ VALID_CLOUD_PROVIDERS,
10
+ CloudProvider,
11
+ DynamicRules,
12
+ SecurityConfig,
13
+ )
9
14
 
10
15
 
11
16
  class DynamicRuleManager:
@@ -247,12 +252,18 @@ class DynamicRuleManager:
247
252
  self, blocked: list[str], allowed: list[str]
248
253
  ) -> None:
249
254
  if blocked:
250
- self.config.blocked_countries = blocked
251
- self.logger.info(f"Dynamic rule: Blocked countries {blocked}")
255
+ normalized_blocked = frozenset(c.upper() for c in blocked)
256
+ self.config.blocked_countries = normalized_blocked
257
+ self.logger.info(
258
+ f"Dynamic rule: Blocked countries {sorted(normalized_blocked)}"
259
+ )
252
260
 
253
261
  if allowed:
254
- self.config.whitelist_countries = allowed
255
- self.logger.info(f"Dynamic rule: Whitelisted countries {allowed}")
262
+ normalized_allowed = frozenset(c.upper() for c in allowed)
263
+ self.config.whitelist_countries = normalized_allowed
264
+ self.logger.info(
265
+ f"Dynamic rule: Whitelisted countries {sorted(normalized_allowed)}"
266
+ )
256
267
 
257
268
  async def _apply_rate_limit_rules(self, rules: DynamicRules) -> None:
258
269
  if rules.global_rate_limit:
@@ -272,8 +283,14 @@ class DynamicRuleManager:
272
283
  )
273
284
 
274
285
  async def _apply_cloud_provider_rules(self, providers: set[str]) -> None:
275
- self.config.block_cloud_providers = providers
276
- self.logger.info(f"Dynamic rule: Blocked cloud providers {providers}")
286
+ valid = {p for p in providers if p in VALID_CLOUD_PROVIDERS}
287
+ self.config.block_cloud_providers = cast(set[CloudProvider], valid)
288
+ invalid = providers - valid
289
+ if invalid:
290
+ self.logger.warning(
291
+ f"Dynamic rule: ignored unknown cloud providers {sorted(invalid)}"
292
+ )
293
+ self.logger.info(f"Dynamic rule: Blocked cloud providers {valid}")
277
294
 
278
295
  async def _apply_user_agent_rules(self, user_agents: list[str]) -> None:
279
296
  self.config.blocked_user_agents = user_agents
@@ -5,7 +5,7 @@ from collections.abc import Awaitable, Callable
5
5
  from datetime import datetime, timezone
6
6
  from typing import Any, Optional
7
7
 
8
- from redis.exceptions import RedisError
8
+ from redis.exceptions import NoScriptError, RedisError
9
9
 
10
10
  from guard_core.models import SecurityConfig
11
11
  from guard_core.protocols.request_protocol import GuardRequest
@@ -54,6 +54,23 @@ class RateLimitManager:
54
54
  async def initialize_agent(self, agent_handler: Any) -> None:
55
55
  self.agent_handler = agent_handler
56
56
 
57
+ async def _emit_script_reloaded_event(self) -> None:
58
+ if not self.agent_handler:
59
+ return
60
+ try:
61
+ from guard_agent import SecurityEvent
62
+
63
+ event = SecurityEvent(
64
+ timestamp=datetime.now(timezone.utc),
65
+ event_type="rate_limit_script_reloaded",
66
+ ip_address="system",
67
+ action_taken="script_reloaded",
68
+ reason="NOSCRIPT recovery: Lua script re-cached on Redis",
69
+ )
70
+ await self.agent_handler.send_event(event)
71
+ except Exception as e:
72
+ self.logger.error(f"Failed to send script-reload event: {e}")
73
+
57
74
  async def _get_redis_request_count(
58
75
  self,
59
76
  client_ip: str,
@@ -78,14 +95,31 @@ class RateLimitManager:
78
95
  try:
79
96
  if self.rate_limit_script_sha:
80
97
  async with self.redis_handler.get_connection() as conn:
81
- count = await conn.evalsha(
82
- self.rate_limit_script_sha,
83
- 1,
84
- key_name,
85
- current_time,
86
- window,
87
- limit,
88
- )
98
+ try:
99
+ count = await conn.evalsha(
100
+ self.rate_limit_script_sha,
101
+ 1,
102
+ key_name,
103
+ current_time,
104
+ window,
105
+ limit,
106
+ )
107
+ except NoScriptError:
108
+ self.rate_limit_script_sha = await conn.script_load(
109
+ RATE_LIMIT_SCRIPT
110
+ )
111
+ self.logger.info(
112
+ "Rate limit Lua script reloaded after NOSCRIPT"
113
+ )
114
+ await self._emit_script_reloaded_event()
115
+ count = await conn.evalsha(
116
+ self.rate_limit_script_sha,
117
+ 1,
118
+ key_name,
119
+ current_time,
120
+ window,
121
+ limit,
122
+ )
89
123
  return int(count)
90
124
  else:
91
125
  async with self.redis_handler.get_connection() as conn: