diff --git a/Dockerfile b/Dockerfile index 0e2ff58..5497616 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,7 +18,7 @@ USER 10001 COPY --chown=app:app pyproject.toml uv.lock* README.md /app/ -RUN uv sync --extra dev --frozen +RUN uv sync --extra dev ENV PATH="/app/.venv/bin:$PATH" diff --git a/compose.yml b/compose.yml index 42b8bd9..85c8664 100644 --- a/compose.yml +++ b/compose.yml @@ -5,7 +5,7 @@ services: args: PYTHON_VERSION: ${PYTHON_VERSION:-3.10} image: flaskapi-guard-example-py${PYTHON_VERSION:-3.10} - command: gunicorn examples.main:app --bind 0.0.0.0:8000 --reload + command: gunicorn examples.simple_app.main:app --bind 0.0.0.0:8000 --reload ports: - "8000:8000" environment: @@ -36,7 +36,6 @@ services: - ./flaskapi_guard:/app/flaskapi_guard:z - ./tests:/app/tests:z - ./pyproject.toml:/app/pyproject.toml:z - - ./uv.lock:/app/uv.lock:z - ./vulture_whitelist.py:/app/vulture_whitelist.py:z depends_on: redis: diff --git a/examples/advanced_app/app/security.py b/examples/advanced_app/app/security.py index 4c98af1..87fc62d 100644 --- a/examples/advanced_app/app/security.py +++ b/examples/advanced_app/app/security.py @@ -41,6 +41,7 @@ def custom_response_modifier(resp: Response) -> Response: auto_ban_threshold=5, auto_ban_duration=300, enable_penetration_detection=True, + behavior_scan_response_body=True, enable_redis=True, redis_url=os.environ.get("REDIS_URL", "redis://localhost:6379"), redis_prefix=os.environ.get("REDIS_PREFIX", "flaskapi_guard:"), diff --git a/tests/conftest.py b/tests/conftest.py index 8789553..fa311b3 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -17,6 +17,18 @@ IPINFO_TOKEN = str(os.getenv("IPINFO_TOKEN")) REDIS_URL = str(os.getenv("REDIS_URL")) + + +@pytest.fixture(scope="session", autouse=True) +def _engine_default_redis_url_follows_redis_url() -> None: + # A SecurityConfig built without redis_url falls back to localhost:6379, which + # is not where Redis lives inside the compose network. + SecurityConfig.model_fields["redis_url"].default = ( + os.getenv("REDIS_URL") or "redis://localhost:6379" + ) + SecurityConfig.model_rebuild(force=True) + + REDIS_PREFIX = str(os.getenv("REDIS_PREFIX")) diff --git a/tests/test_decorators/test_advanced.py b/tests/test_decorators/test_advanced.py index a516c10..b2bc47e 100644 --- a/tests/test_decorators/test_advanced.py +++ b/tests/test_decorators/test_advanced.py @@ -227,7 +227,7 @@ def test_honeypot_form_detection(security_config: SecurityConfig) -> None: "application/x-www-form-urlencoded" if key == "content-type" else default ) mock_request.body.return_value = b"bot_trap=filled" - mock_request.read_body_prefix.return_value = b"bot_trap=filled" + mock_request.read_body_prefix = lambda _max_bytes: b"bot_trap=filled" result = validator(mock_request) assert result.status_code == 403 @@ -278,7 +278,7 @@ def test_honeypot_json_detection(security_config: SecurityConfig) -> None: "application/json" if key == "content-type" else default ) mock_request.body.return_value = b'{"spam_check": "filled"}' - mock_request.read_body_prefix.return_value = b'{"spam_check": "filled"}' + mock_request.read_body_prefix = lambda _max_bytes: b'{"spam_check": "filled"}' result = validator(mock_request) assert result.status_code == 403 diff --git a/tests/test_decorators/test_extension_integration.py b/tests/test_decorators/test_extension_integration.py index d94760b..0878636 100644 --- a/tests/test_decorators/test_extension_integration.py +++ b/tests/test_decorators/test_extension_integration.py @@ -1,3 +1,4 @@ +import os from typing import Any, cast from unittest.mock import MagicMock, Mock, patch @@ -11,6 +12,8 @@ from flaskapi_guard.adapters import FlaskGuardResponse from flaskapi_guard.extension import FlaskAPIGuard +REDIS_URL = os.getenv("REDIS_URL") or "redis://localhost:6379" + def test_set_decorator_handler() -> None: """Test set_decorator_handler method.""" @@ -575,7 +578,7 @@ def test_endpoint() -> str: def test_route_specific_rate_limit_with_redis() -> None: app = Flask(__name__) - config = SecurityConfig(enable_redis=True, redis_url="redis://localhost:6379") + config = SecurityConfig(enable_redis=True, redis_url=REDIS_URL) config.enable_penetration_detection = False decorator = SecurityDecorator(config)