flaskapi-guard 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- flaskapi_guard/__init__.py +40 -0
- flaskapi_guard/core/__init__.py +13 -0
- flaskapi_guard/core/behavioral/__init__.py +7 -0
- flaskapi_guard/core/behavioral/context.py +17 -0
- flaskapi_guard/core/behavioral/processor.py +113 -0
- flaskapi_guard/core/bypass/__init__.py +7 -0
- flaskapi_guard/core/bypass/context.py +21 -0
- flaskapi_guard/core/bypass/handler.py +71 -0
- flaskapi_guard/core/checks/__init__.py +48 -0
- flaskapi_guard/core/checks/base.py +115 -0
- flaskapi_guard/core/checks/helpers.py +352 -0
- flaskapi_guard/core/checks/implementations/__init__.py +68 -0
- flaskapi_guard/core/checks/implementations/authentication.py +68 -0
- flaskapi_guard/core/checks/implementations/cloud_ip_refresh.py +23 -0
- flaskapi_guard/core/checks/implementations/cloud_provider.py +75 -0
- flaskapi_guard/core/checks/implementations/custom_request.py +44 -0
- flaskapi_guard/core/checks/implementations/custom_validators.py +51 -0
- flaskapi_guard/core/checks/implementations/emergency_mode.py +68 -0
- flaskapi_guard/core/checks/implementations/https_enforcement.py +89 -0
- flaskapi_guard/core/checks/implementations/ip_security.py +163 -0
- flaskapi_guard/core/checks/implementations/rate_limit.py +231 -0
- flaskapi_guard/core/checks/implementations/referrer.py +102 -0
- flaskapi_guard/core/checks/implementations/request_logging.py +18 -0
- flaskapi_guard/core/checks/implementations/request_size_content.py +115 -0
- flaskapi_guard/core/checks/implementations/required_headers.py +85 -0
- flaskapi_guard/core/checks/implementations/route_config.py +29 -0
- flaskapi_guard/core/checks/implementations/suspicious_activity.py +179 -0
- flaskapi_guard/core/checks/implementations/time_window.py +77 -0
- flaskapi_guard/core/checks/implementations/user_agent.py +68 -0
- flaskapi_guard/core/checks/pipeline.py +137 -0
- flaskapi_guard/core/events/__init__.py +9 -0
- flaskapi_guard/core/events/extension_events.py +170 -0
- flaskapi_guard/core/events/metrics.py +85 -0
- flaskapi_guard/core/initialization/__init__.py +10 -0
- flaskapi_guard/core/initialization/handler_initializer.py +120 -0
- flaskapi_guard/core/responses/__init__.py +7 -0
- flaskapi_guard/core/responses/context.py +26 -0
- flaskapi_guard/core/responses/factory.py +170 -0
- flaskapi_guard/core/routing/__init__.py +7 -0
- flaskapi_guard/core/routing/context.py +22 -0
- flaskapi_guard/core/routing/resolver.py +117 -0
- flaskapi_guard/core/validation/__init__.py +7 -0
- flaskapi_guard/core/validation/context.py +15 -0
- flaskapi_guard/core/validation/validator.py +93 -0
- flaskapi_guard/decorators/__init__.py +63 -0
- flaskapi_guard/decorators/access_control.py +88 -0
- flaskapi_guard/decorators/advanced.py +124 -0
- flaskapi_guard/decorators/authentication.py +66 -0
- flaskapi_guard/decorators/base.py +228 -0
- flaskapi_guard/decorators/behavioral.py +162 -0
- flaskapi_guard/decorators/content_filtering.py +102 -0
- flaskapi_guard/decorators/rate_limiting.py +48 -0
- flaskapi_guard/detection_engine/__init__.py +12 -0
- flaskapi_guard/detection_engine/compiler.py +247 -0
- flaskapi_guard/detection_engine/monitor.py +574 -0
- flaskapi_guard/detection_engine/preprocessor.py +464 -0
- flaskapi_guard/detection_engine/semantic.py +553 -0
- flaskapi_guard/extension.py +542 -0
- flaskapi_guard/handlers/__init__.py +22 -0
- flaskapi_guard/handlers/behavior_handler.py +403 -0
- flaskapi_guard/handlers/cloud_handler.py +275 -0
- flaskapi_guard/handlers/dynamic_rule_handler.py +367 -0
- flaskapi_guard/handlers/ipban_handler.py +155 -0
- flaskapi_guard/handlers/ipinfo_handler.py +244 -0
- flaskapi_guard/handlers/ratelimit_handler.py +257 -0
- flaskapi_guard/handlers/redis_handler.py +291 -0
- flaskapi_guard/handlers/security_headers_handler.py +581 -0
- flaskapi_guard/handlers/suspatterns_handler.py +917 -0
- flaskapi_guard/models.py +870 -0
- flaskapi_guard/protocols/__init__.py +10 -0
- flaskapi_guard/protocols/agent_protocol.py +77 -0
- flaskapi_guard/protocols/geo_ip_protocol.py +17 -0
- flaskapi_guard/protocols/redis_protocol.py +19 -0
- flaskapi_guard/scripts/__init__.py +1 -0
- flaskapi_guard/scripts/rate_lua.py +24 -0
- flaskapi_guard/utils.py +838 -0
- flaskapi_guard-0.1.0.dist-info/LICENSE +21 -0
- flaskapi_guard-0.1.0.dist-info/METADATA +851 -0
- flaskapi_guard-0.1.0.dist-info/RECORD +81 -0
- flaskapi_guard-0.1.0.dist-info/WHEEL +5 -0
- flaskapi_guard-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# flaskapi_guard/__init__.py
|
|
2
|
+
from flaskapi_guard.decorators import RouteConfig, SecurityDecorator
|
|
3
|
+
from flaskapi_guard.extension import FlaskAPIGuard
|
|
4
|
+
from flaskapi_guard.handlers.behavior_handler import BehaviorRule, BehaviorTracker
|
|
5
|
+
from flaskapi_guard.handlers.cloud_handler import CloudManager, cloud_handler
|
|
6
|
+
from flaskapi_guard.handlers.ipban_handler import IPBanManager, ip_ban_manager
|
|
7
|
+
from flaskapi_guard.handlers.ipinfo_handler import IPInfoManager
|
|
8
|
+
from flaskapi_guard.handlers.ratelimit_handler import RateLimitManager, rate_limit_handler
|
|
9
|
+
from flaskapi_guard.handlers.redis_handler import RedisManager, redis_handler
|
|
10
|
+
from flaskapi_guard.handlers.security_headers_handler import (
|
|
11
|
+
SecurityHeadersManager,
|
|
12
|
+
security_headers_manager,
|
|
13
|
+
)
|
|
14
|
+
from flaskapi_guard.handlers.suspatterns_handler import sus_patterns_handler
|
|
15
|
+
from flaskapi_guard.models import SecurityConfig
|
|
16
|
+
from flaskapi_guard.protocols.geo_ip_protocol import GeoIPHandler
|
|
17
|
+
from flaskapi_guard.protocols.redis_protocol import RedisHandlerProtocol
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"FlaskAPIGuard",
|
|
21
|
+
"SecurityConfig",
|
|
22
|
+
"SecurityDecorator",
|
|
23
|
+
"RouteConfig",
|
|
24
|
+
"BehaviorTracker",
|
|
25
|
+
"BehaviorRule",
|
|
26
|
+
"ip_ban_manager",
|
|
27
|
+
"IPBanManager",
|
|
28
|
+
"cloud_handler",
|
|
29
|
+
"CloudManager",
|
|
30
|
+
"IPInfoManager",
|
|
31
|
+
"rate_limit_handler",
|
|
32
|
+
"RateLimitManager",
|
|
33
|
+
"redis_handler",
|
|
34
|
+
"RedisManager",
|
|
35
|
+
"security_headers_manager",
|
|
36
|
+
"SecurityHeadersManager",
|
|
37
|
+
"sus_patterns_handler",
|
|
38
|
+
"GeoIPHandler",
|
|
39
|
+
"RedisHandlerProtocol",
|
|
40
|
+
]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Extension supporting modules.
|
|
2
|
+
|
|
3
|
+
This package contains event bus, metrics, and security check modules
|
|
4
|
+
that support the main FlaskAPIGuard class.
|
|
5
|
+
|
|
6
|
+
The FlaskAPIGuard class itself is defined in flaskapi_guard/extension.py
|
|
7
|
+
and should be imported from there:
|
|
8
|
+
from flaskapi_guard.extension import FlaskAPIGuard
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from flaskapi_guard.core.events import MetricsCollector, SecurityEventBus
|
|
12
|
+
|
|
13
|
+
__all__ = ["SecurityEventBus", "MetricsCollector"]
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# flaskapi_guard/core/behavioral/__init__.py
|
|
2
|
+
"""Behavioral rule processing module."""
|
|
3
|
+
|
|
4
|
+
from flaskapi_guard.core.behavioral.context import BehavioralContext
|
|
5
|
+
from flaskapi_guard.core.behavioral.processor import BehavioralProcessor
|
|
6
|
+
|
|
7
|
+
__all__ = ["BehavioralContext", "BehavioralProcessor"]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# flaskapi_guard/core/behavioral/context.py
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from logging import Logger
|
|
4
|
+
|
|
5
|
+
from flaskapi_guard.core.events import SecurityEventBus
|
|
6
|
+
from flaskapi_guard.decorators.base import BaseSecurityDecorator
|
|
7
|
+
from flaskapi_guard.models import SecurityConfig
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass
|
|
11
|
+
class BehavioralContext:
|
|
12
|
+
"""Context for behavioral rule processing."""
|
|
13
|
+
|
|
14
|
+
config: SecurityConfig
|
|
15
|
+
logger: Logger
|
|
16
|
+
event_bus: SecurityEventBus
|
|
17
|
+
guard_decorator: BaseSecurityDecorator | None
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# flaskapi_guard/core/behavioral/processor.py
|
|
2
|
+
from flask import Request, Response, current_app
|
|
3
|
+
|
|
4
|
+
from flaskapi_guard.core.behavioral.context import BehavioralContext
|
|
5
|
+
from flaskapi_guard.decorators.base import RouteConfig
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BehavioralProcessor:
|
|
9
|
+
"""Handles behavioral rule processing operations."""
|
|
10
|
+
|
|
11
|
+
def __init__(self, context: BehavioralContext) -> None:
|
|
12
|
+
"""
|
|
13
|
+
Initialize the BehavioralProcessor.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
context: Behavioral context with config, logger, and dependencies
|
|
17
|
+
"""
|
|
18
|
+
self.context = context
|
|
19
|
+
|
|
20
|
+
def process_usage_rules(
|
|
21
|
+
self, request: Request, client_ip: str, route_config: RouteConfig
|
|
22
|
+
) -> None:
|
|
23
|
+
"""Process behavioral usage rules from decorators before request processing."""
|
|
24
|
+
if not self.context.guard_decorator:
|
|
25
|
+
return
|
|
26
|
+
|
|
27
|
+
endpoint_id = self.get_endpoint_id(request)
|
|
28
|
+
for rule in route_config.behavior_rules:
|
|
29
|
+
if rule.rule_type in ["usage", "frequency"]:
|
|
30
|
+
behavior_tracker = self.context.guard_decorator.behavior_tracker
|
|
31
|
+
threshold_exceeded = behavior_tracker.track_endpoint_usage(
|
|
32
|
+
endpoint_id, client_ip, rule
|
|
33
|
+
)
|
|
34
|
+
if threshold_exceeded:
|
|
35
|
+
details = f"{rule.threshold} calls in {rule.window}s"
|
|
36
|
+
message = f"Behavioral {rule.rule_type}"
|
|
37
|
+
reason = "threshold exceeded"
|
|
38
|
+
|
|
39
|
+
# Send decorator violation event for behavioral rule violation
|
|
40
|
+
self.context.event_bus.send_middleware_event(
|
|
41
|
+
event_type="decorator_violation",
|
|
42
|
+
request=request,
|
|
43
|
+
action_taken="behavioral_action_triggered",
|
|
44
|
+
reason=f"{message} {reason}: {details}",
|
|
45
|
+
decorator_type="behavioral",
|
|
46
|
+
violation_type=rule.rule_type,
|
|
47
|
+
threshold=rule.threshold,
|
|
48
|
+
window=rule.window,
|
|
49
|
+
action=rule.action,
|
|
50
|
+
endpoint_id=endpoint_id,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
self.context.guard_decorator.behavior_tracker.apply_action(
|
|
54
|
+
rule,
|
|
55
|
+
client_ip,
|
|
56
|
+
endpoint_id,
|
|
57
|
+
f"Usage threshold exceeded: {details}",
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
def process_return_rules(
|
|
61
|
+
self,
|
|
62
|
+
request: Request,
|
|
63
|
+
response: Response,
|
|
64
|
+
client_ip: str,
|
|
65
|
+
route_config: RouteConfig,
|
|
66
|
+
) -> None:
|
|
67
|
+
"""Process behavioral return pattern rules from decorators after response."""
|
|
68
|
+
if not self.context.guard_decorator:
|
|
69
|
+
return
|
|
70
|
+
|
|
71
|
+
endpoint_id = self.get_endpoint_id(request)
|
|
72
|
+
for rule in route_config.behavior_rules:
|
|
73
|
+
if rule.rule_type == "return_pattern":
|
|
74
|
+
behavior_tracker = self.context.guard_decorator.behavior_tracker
|
|
75
|
+
pattern_detected = behavior_tracker.track_return_pattern(
|
|
76
|
+
endpoint_id, client_ip, response, rule
|
|
77
|
+
)
|
|
78
|
+
if pattern_detected:
|
|
79
|
+
details = f"{rule.threshold} for '{rule.pattern}' in {rule.window}s"
|
|
80
|
+
|
|
81
|
+
# Send decorator violation event for return pattern violation
|
|
82
|
+
self.context.event_bus.send_middleware_event(
|
|
83
|
+
event_type="decorator_violation",
|
|
84
|
+
request=request,
|
|
85
|
+
action_taken="behavioral_action_triggered",
|
|
86
|
+
reason=f"Return pattern threshold exceeded: {details}",
|
|
87
|
+
decorator_type="behavioral",
|
|
88
|
+
violation_type="return_pattern",
|
|
89
|
+
threshold=rule.threshold,
|
|
90
|
+
window=rule.window,
|
|
91
|
+
pattern=rule.pattern,
|
|
92
|
+
action=rule.action,
|
|
93
|
+
endpoint_id=endpoint_id,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
self.context.guard_decorator.behavior_tracker.apply_action(
|
|
97
|
+
rule,
|
|
98
|
+
client_ip,
|
|
99
|
+
endpoint_id,
|
|
100
|
+
f"Return pattern threshold exceeded: {details}",
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
def get_endpoint_id(self, request: Request) -> str:
|
|
104
|
+
"""Generate unique endpoint identifier."""
|
|
105
|
+
if request.endpoint is None:
|
|
106
|
+
return f"{request.method}:{request.path}"
|
|
107
|
+
view_func = current_app.view_functions.get(request.endpoint)
|
|
108
|
+
if view_func is not None:
|
|
109
|
+
# This is an internal identifier for tracking, not returned to users.
|
|
110
|
+
# Values come from Python introspection (module,
|
|
111
|
+
# qualname), not from user input.
|
|
112
|
+
return f"{view_func.__module__}.{view_func.__qualname__}"
|
|
113
|
+
return f"{request.method}:{request.path}"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# flaskapi_guard/core/bypass/context.py
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from logging import Logger
|
|
4
|
+
|
|
5
|
+
from flaskapi_guard.core.events import SecurityEventBus
|
|
6
|
+
from flaskapi_guard.core.responses import ErrorResponseFactory
|
|
7
|
+
from flaskapi_guard.core.routing import RouteConfigResolver
|
|
8
|
+
from flaskapi_guard.core.validation import RequestValidator
|
|
9
|
+
from flaskapi_guard.models import SecurityConfig
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class BypassContext:
|
|
14
|
+
"""Context for bypass handler operations."""
|
|
15
|
+
|
|
16
|
+
config: SecurityConfig
|
|
17
|
+
logger: Logger
|
|
18
|
+
event_bus: SecurityEventBus
|
|
19
|
+
route_resolver: RouteConfigResolver
|
|
20
|
+
response_factory: ErrorResponseFactory
|
|
21
|
+
validator: RequestValidator
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# flaskapi_guard/core/bypass/handler.py
|
|
2
|
+
from flask import Request, Response
|
|
3
|
+
|
|
4
|
+
from flaskapi_guard.core.bypass.context import BypassContext
|
|
5
|
+
from flaskapi_guard.decorators.base import RouteConfig
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BypassHandler:
|
|
9
|
+
"""Handles security check bypassing operations."""
|
|
10
|
+
|
|
11
|
+
def __init__(self, context: BypassContext) -> None:
|
|
12
|
+
"""
|
|
13
|
+
Initialize the BypassHandler.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
context: Bypass context with config, logger, and dependencies
|
|
17
|
+
"""
|
|
18
|
+
self.context = context
|
|
19
|
+
|
|
20
|
+
def handle_passthrough(
|
|
21
|
+
self,
|
|
22
|
+
request: Request,
|
|
23
|
+
) -> Response | None:
|
|
24
|
+
"""
|
|
25
|
+
Handle special cases that require immediate passthrough.
|
|
26
|
+
|
|
27
|
+
This includes requests with no client information and excluded paths.
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
None to let Flask proceed with the request
|
|
31
|
+
"""
|
|
32
|
+
# No client information
|
|
33
|
+
if not request.remote_addr:
|
|
34
|
+
return None
|
|
35
|
+
|
|
36
|
+
# Excluded paths
|
|
37
|
+
if self.context.validator.is_path_excluded(request):
|
|
38
|
+
return None
|
|
39
|
+
|
|
40
|
+
return None
|
|
41
|
+
|
|
42
|
+
def handle_security_bypass(
|
|
43
|
+
self,
|
|
44
|
+
request: Request,
|
|
45
|
+
route_config: RouteConfig | None,
|
|
46
|
+
) -> Response | None:
|
|
47
|
+
"""
|
|
48
|
+
Handle bypassed security checks.
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
None to let Flask proceed with the request
|
|
52
|
+
"""
|
|
53
|
+
if not route_config or not self.context.route_resolver.should_bypass_check(
|
|
54
|
+
"all", route_config
|
|
55
|
+
):
|
|
56
|
+
return None
|
|
57
|
+
|
|
58
|
+
# Send security bypass event for monitoring
|
|
59
|
+
self.context.event_bus.send_middleware_event(
|
|
60
|
+
event_type="security_bypass",
|
|
61
|
+
request=request,
|
|
62
|
+
action_taken="all_checks_bypassed",
|
|
63
|
+
reason="Route configured to bypass all security checks",
|
|
64
|
+
bypassed_checks=list(route_config.bypassed_checks),
|
|
65
|
+
endpoint=request.path,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
if not self.context.config.passive_mode:
|
|
69
|
+
return None
|
|
70
|
+
|
|
71
|
+
return None
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# flaskapi_guard/core/checks/__init__.py
|
|
2
|
+
"""Security checks module with modular architecture."""
|
|
3
|
+
|
|
4
|
+
from flaskapi_guard.core.checks.base import SecurityCheck
|
|
5
|
+
from flaskapi_guard.core.checks.implementations import (
|
|
6
|
+
AuthenticationCheck,
|
|
7
|
+
CloudIpRefreshCheck,
|
|
8
|
+
CloudProviderCheck,
|
|
9
|
+
CustomRequestCheck,
|
|
10
|
+
CustomValidatorsCheck,
|
|
11
|
+
EmergencyModeCheck,
|
|
12
|
+
HttpsEnforcementCheck,
|
|
13
|
+
IpSecurityCheck,
|
|
14
|
+
RateLimitCheck,
|
|
15
|
+
ReferrerCheck,
|
|
16
|
+
RequestLoggingCheck,
|
|
17
|
+
RequestSizeContentCheck,
|
|
18
|
+
RequiredHeadersCheck,
|
|
19
|
+
RouteConfigCheck,
|
|
20
|
+
SuspiciousActivityCheck,
|
|
21
|
+
TimeWindowCheck,
|
|
22
|
+
UserAgentCheck,
|
|
23
|
+
)
|
|
24
|
+
from flaskapi_guard.core.checks.pipeline import SecurityCheckPipeline
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
# Base
|
|
28
|
+
"SecurityCheck",
|
|
29
|
+
"SecurityCheckPipeline",
|
|
30
|
+
# Implementations
|
|
31
|
+
"RouteConfigCheck",
|
|
32
|
+
"EmergencyModeCheck",
|
|
33
|
+
"HttpsEnforcementCheck",
|
|
34
|
+
"RequestLoggingCheck",
|
|
35
|
+
"RequestSizeContentCheck",
|
|
36
|
+
"RequiredHeadersCheck",
|
|
37
|
+
"AuthenticationCheck",
|
|
38
|
+
"ReferrerCheck",
|
|
39
|
+
"CustomValidatorsCheck",
|
|
40
|
+
"TimeWindowCheck",
|
|
41
|
+
"CloudIpRefreshCheck",
|
|
42
|
+
"IpSecurityCheck",
|
|
43
|
+
"CloudProviderCheck",
|
|
44
|
+
"UserAgentCheck",
|
|
45
|
+
"RateLimitCheck",
|
|
46
|
+
"SuspiciousActivityCheck",
|
|
47
|
+
"CustomRequestCheck",
|
|
48
|
+
]
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# flaskapi_guard/core/checks/base.py
|
|
2
|
+
import logging
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from typing import TYPE_CHECKING, Any
|
|
5
|
+
|
|
6
|
+
from flask import Request, Response
|
|
7
|
+
|
|
8
|
+
from flaskapi_guard.models import SecurityConfig
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from flaskapi_guard.extension import FlaskAPIGuard
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class SecurityCheck(ABC):
|
|
15
|
+
"""
|
|
16
|
+
Base class for security checks in the middleware pipeline.
|
|
17
|
+
|
|
18
|
+
Each security check implements a single security concern and can
|
|
19
|
+
independently block or allow a request to proceed.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(self, middleware: "FlaskAPIGuard") -> None:
|
|
23
|
+
"""
|
|
24
|
+
Initialize the security check.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
middleware: Reference to the parent FlaskAPIGuard instance
|
|
28
|
+
for accessing config, handlers, and utilities.
|
|
29
|
+
"""
|
|
30
|
+
self.middleware = middleware
|
|
31
|
+
assert middleware.config is not None, (
|
|
32
|
+
"FlaskAPIGuard must be initialized with config"
|
|
33
|
+
)
|
|
34
|
+
self.config: SecurityConfig = middleware.config
|
|
35
|
+
assert middleware.logger is not None, (
|
|
36
|
+
"FlaskAPIGuard must be initialized with logger"
|
|
37
|
+
)
|
|
38
|
+
self.logger: logging.Logger = middleware.logger
|
|
39
|
+
|
|
40
|
+
@abstractmethod
|
|
41
|
+
def check(self, request: Request) -> Response | None:
|
|
42
|
+
"""
|
|
43
|
+
Perform the security check on the request.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
request: The incoming Flask request.
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
Response if the check fails and request should be blocked.
|
|
50
|
+
None if the check passes and request should continue.
|
|
51
|
+
"""
|
|
52
|
+
pass # pragma: no cover
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
@abstractmethod
|
|
56
|
+
def check_name(self) -> str:
|
|
57
|
+
"""
|
|
58
|
+
Name of this security check for logging and debugging.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
Human-readable name of the check (e.g., "https_enforcement").
|
|
62
|
+
"""
|
|
63
|
+
pass # pragma: no cover
|
|
64
|
+
|
|
65
|
+
def send_event(
|
|
66
|
+
self,
|
|
67
|
+
event_type: str,
|
|
68
|
+
request: Request,
|
|
69
|
+
action_taken: str,
|
|
70
|
+
reason: str,
|
|
71
|
+
**kwargs: Any,
|
|
72
|
+
) -> None:
|
|
73
|
+
"""
|
|
74
|
+
Send a security event to the agent handler if enabled.
|
|
75
|
+
|
|
76
|
+
This is a helper method to simplify event sending from checks.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
event_type: Type of security event.
|
|
80
|
+
request: The request that triggered the event.
|
|
81
|
+
action_taken: Action taken by the check.
|
|
82
|
+
reason: Reason for the action.
|
|
83
|
+
**kwargs: Additional metadata for the event.
|
|
84
|
+
"""
|
|
85
|
+
if self.middleware.event_bus is None:
|
|
86
|
+
return
|
|
87
|
+
self.middleware.event_bus.send_middleware_event(
|
|
88
|
+
event_type=event_type,
|
|
89
|
+
request=request,
|
|
90
|
+
action_taken=action_taken,
|
|
91
|
+
reason=reason,
|
|
92
|
+
**kwargs,
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
def create_error_response(self, status_code: int, default_message: str) -> Response:
|
|
96
|
+
"""
|
|
97
|
+
Create an error response with custom message and security headers.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
status_code: HTTP status code for the error.
|
|
101
|
+
default_message: Default error message if no custom message configured.
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
Response object with appropriate status and headers.
|
|
105
|
+
"""
|
|
106
|
+
return self.middleware.create_error_response(status_code, default_message)
|
|
107
|
+
|
|
108
|
+
def is_passive_mode(self) -> bool:
|
|
109
|
+
"""
|
|
110
|
+
Check if middleware is in passive mode (log only, don't block).
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
True if passive mode is enabled, False otherwise.
|
|
114
|
+
"""
|
|
115
|
+
return self.config.passive_mode
|