126 lines
5.3 KiB
Python
126 lines
5.3 KiB
Python
|
|
"""Unit tests for CORS middleware.
|
||
|
|
|
||
|
|
WHAT: Tests for the CORS middleware configuration, including wildcard
|
||
|
|
origin handling, credentials support, and preflight responses.
|
||
|
|
WHY: CORS misconfiguration can break browser clients or leak credentials.
|
||
|
|
EXPECTED: Correct Access-Control-* headers for all origin configurations.
|
||
|
|
"""
|
||
|
|
import pytest
|
||
|
|
from flask import Flask
|
||
|
|
|
||
|
|
from gatehouse_app.middleware.cors import (
|
||
|
|
_is_origin_allowed,
|
||
|
|
_cors_origin_header,
|
||
|
|
setup_cors,
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
# ---------------------------------------------------------------------------
|
||
|
|
# _is_origin_allowed
|
||
|
|
# ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
class TestIsOriginAllowed:
|
||
|
|
def test_empty_origin_rejected(self):
|
||
|
|
"""TEST: CORS-01 -- Empty origin is never allowed."""
|
||
|
|
assert _is_origin_allowed("", ["https://example.com"]) is False
|
||
|
|
assert _is_origin_allowed(None, "*") is False
|
||
|
|
|
||
|
|
def test_wildcard_string(self):
|
||
|
|
"""TEST: CORS-02 -- Wildcard string allows any origin."""
|
||
|
|
assert _is_origin_allowed("https://evil.com", "*") is True
|
||
|
|
|
||
|
|
def test_wildcard_in_list(self):
|
||
|
|
"""TEST: CORS-03 -- Wildcard in list allows any origin."""
|
||
|
|
assert _is_origin_allowed("https://evil.com", ["*", "https://example.com"]) is True
|
||
|
|
|
||
|
|
def test_explicit_origin_match(self):
|
||
|
|
"""TEST: CORS-04 -- Explicit list matches exact origin."""
|
||
|
|
origins = ["https://example.com", "http://localhost:3000"]
|
||
|
|
assert _is_origin_allowed("https://example.com", origins) is True
|
||
|
|
assert _is_origin_allowed("https://evil.com", origins) is False
|
||
|
|
|
||
|
|
def test_empty_origins_list(self):
|
||
|
|
"""TEST: CORS-05 -- Empty list rejects everything."""
|
||
|
|
assert _is_origin_allowed("https://example.com", []) is False
|
||
|
|
|
||
|
|
|
||
|
|
# ---------------------------------------------------------------------------
|
||
|
|
# _cors_origin_header
|
||
|
|
# ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
class TestCorsOriginHeader:
|
||
|
|
def test_wildcard_with_origin_echoes(self):
|
||
|
|
"""TEST: CORS-HDR-01 -- Wildcard echoes request origin (for credentials)."""
|
||
|
|
assert _cors_origin_header("*", "https://example.com") == "https://example.com"
|
||
|
|
|
||
|
|
def test_wildcard_without_origin(self):
|
||
|
|
"""TEST: CORS-HDR-02 -- Wildcard with no origin returns *."""
|
||
|
|
assert _cors_origin_header("*", None) == "*"
|
||
|
|
|
||
|
|
def test_wildcard_in_list_with_origin(self):
|
||
|
|
"""TEST: CORS-HDR-03 -- Wildcard in list echoes request origin."""
|
||
|
|
result = _cors_origin_header(["*", "https://example.com"], "https://any.com")
|
||
|
|
assert result == "https://any.com"
|
||
|
|
|
||
|
|
def test_specific_origin_match(self):
|
||
|
|
"""TEST: CORS-HDR-04 -- Matching origin is echoed."""
|
||
|
|
origins = ["https://example.com"]
|
||
|
|
assert _cors_origin_header(origins, "https://example.com") == "https://example.com"
|
||
|
|
|
||
|
|
def test_specific_origin_no_match(self):
|
||
|
|
"""TEST: CORS-HDR-05 -- Non-matching origin returns None."""
|
||
|
|
origins = ["https://example.com"]
|
||
|
|
assert _cors_origin_header(origins, "https://evil.com") is None
|
||
|
|
|
||
|
|
def test_no_origin_no_match(self):
|
||
|
|
"""TEST: CORS-HDR-06 -- No origin with specific list returns None."""
|
||
|
|
origins = ["https://example.com"]
|
||
|
|
assert _cors_origin_header(origins, None) is None
|
||
|
|
|
||
|
|
|
||
|
|
# ---------------------------------------------------------------------------
|
||
|
|
# Integration: preflight response
|
||
|
|
# ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
class TestPreflightIntegration:
|
||
|
|
@pytest.fixture
|
||
|
|
def app_wildcard(self):
|
||
|
|
app = Flask(__name__)
|
||
|
|
app.config["CORS_ORIGINS"] = "*"
|
||
|
|
app.config["CORS_SUPPORTS_CREDENTIALS"] = True
|
||
|
|
setup_cors(app)
|
||
|
|
app.config["TESTING"] = True
|
||
|
|
return app
|
||
|
|
|
||
|
|
@pytest.fixture
|
||
|
|
def app_specific(self):
|
||
|
|
app = Flask(__name__)
|
||
|
|
app.config["CORS_ORIGINS"] = ["https://example.com"]
|
||
|
|
app.config["CORS_SUPPORTS_CREDENTIALS"] = True
|
||
|
|
setup_cors(app)
|
||
|
|
app.config["TESTING"] = True
|
||
|
|
return app
|
||
|
|
|
||
|
|
def test_wildcard_preflight_echoes_origin(self, app_wildcard):
|
||
|
|
"""TEST: CORS-PF-01 -- Wildcard preflight echoes request origin."""
|
||
|
|
with app_wildcard.test_client() as client:
|
||
|
|
resp = client.options("/", headers={"Origin": "https://example.com"})
|
||
|
|
assert resp.status_code == 204
|
||
|
|
assert resp.headers.get("Access-Control-Allow-Origin") == "https://example.com"
|
||
|
|
assert resp.headers.get("Access-Control-Allow-Credentials") == "true"
|
||
|
|
|
||
|
|
def test_specific_origin_preflight(self, app_specific):
|
||
|
|
"""TEST: CORS-PF-02 -- Specific origin preflight allows matching origin."""
|
||
|
|
with app_specific.test_client() as client:
|
||
|
|
resp = client.options("/", headers={"Origin": "https://example.com"})
|
||
|
|
assert resp.status_code == 204
|
||
|
|
assert resp.headers.get("Access-Control-Allow-Origin") == "https://example.com"
|
||
|
|
assert resp.headers.get("Access-Control-Allow-Credentials") == "true"
|
||
|
|
|
||
|
|
def test_specific_origin_rejects_unknown(self, app_specific):
|
||
|
|
"""TEST: CORS-PF-03 -- Non-matching origin gets no CORS headers."""
|
||
|
|
with app_specific.test_client() as client:
|
||
|
|
resp = client.options("/", headers={"Origin": "https://evil.com"})
|
||
|
|
# No preflight handler runs, Flask returns default
|
||
|
|
assert resp.headers.get("Access-Control-Allow-Origin") is None
|