Files
gh-christianlouis-docuelevate/tests/test_security_headers.py
T
2026-02-13 09:10:52 +00:00

190 lines
7.4 KiB
Python

#!/usr/bin/env python3
"""
Tests for security headers middleware.
These tests validate that security headers are properly added to HTTP responses
based on configuration settings.
"""
import pytest
@pytest.mark.unit
def test_security_headers_enabled_by_default(client):
"""Test that security headers are enabled by default."""
response = client.get("/")
# Should get a valid response (200, 302 redirect, or 404)
assert response.status_code in [200, 302, 404], f"Unexpected status code: {response.status_code}"
@pytest.mark.unit
def test_hsts_header_present(client):
"""Test that HSTS header is present when enabled."""
from app.config import settings
# Skip test if HSTS is disabled
if not settings.security_headers_enabled or not settings.security_header_hsts_enabled:
pytest.skip("HSTS header is disabled in configuration")
response = client.get("/")
assert "Strict-Transport-Security" in response.headers
assert "max-age" in response.headers["Strict-Transport-Security"]
@pytest.mark.unit
def test_csp_header_present(client):
"""Test that CSP header is present when enabled."""
from app.config import settings
# Skip test if CSP is disabled
if not settings.security_headers_enabled or not settings.security_header_csp_enabled:
pytest.skip("CSP header is disabled in configuration")
response = client.get("/")
assert "Content-Security-Policy" in response.headers
assert "default-src" in response.headers["Content-Security-Policy"]
@pytest.mark.unit
def test_x_frame_options_header_present(client):
"""Test that X-Frame-Options header is present when enabled."""
from app.config import settings
# Skip test if X-Frame-Options is disabled
if not settings.security_headers_enabled or not settings.security_header_x_frame_options_enabled:
pytest.skip("X-Frame-Options header is disabled in configuration")
response = client.get("/")
assert "X-Frame-Options" in response.headers
assert response.headers["X-Frame-Options"] in ["DENY", "SAMEORIGIN"]
@pytest.mark.unit
def test_x_content_type_options_header_present(client):
"""Test that X-Content-Type-Options header is present when enabled."""
from app.config import settings
# Skip test if X-Content-Type-Options is disabled
if not settings.security_headers_enabled or not settings.security_header_x_content_type_options_enabled:
pytest.skip("X-Content-Type-Options header is disabled in configuration")
response = client.get("/")
assert "X-Content-Type-Options" in response.headers
assert response.headers["X-Content-Type-Options"] == "nosniff"
@pytest.mark.unit
def test_security_headers_on_api_endpoints(client):
"""Test that security headers are applied to API endpoints."""
from app.config import settings
if not settings.security_headers_enabled:
pytest.skip("Security headers are disabled in configuration")
response = client.get("/api/diagnostic/health")
# Check that at least some security headers are present
security_headers = [
"Strict-Transport-Security",
"Content-Security-Policy",
"X-Frame-Options",
"X-Content-Type-Options",
]
present_headers = [h for h in security_headers if h in response.headers]
assert len(present_headers) > 0, "No security headers found on API endpoint"
@pytest.mark.security
def test_hsts_header_value_format(client):
"""Test that HSTS header has correct format."""
from app.config import settings
if not settings.security_headers_enabled or not settings.security_header_hsts_enabled:
pytest.skip("HSTS header is disabled in configuration")
response = client.get("/")
if "Strict-Transport-Security" in response.headers:
hsts_value = response.headers["Strict-Transport-Security"]
assert "max-age=" in hsts_value, "HSTS header missing max-age directive"
# Extract max-age value
parts = hsts_value.split(";")
max_age_part = [p.strip() for p in parts if p.strip().startswith("max-age=")]
assert len(max_age_part) > 0, "HSTS header missing max-age value"
@pytest.mark.security
def test_csp_header_value_format(client):
"""Test that CSP header has correct format."""
from app.config import settings
if not settings.security_headers_enabled or not settings.security_header_csp_enabled:
pytest.skip("CSP header is disabled in configuration")
response = client.get("/")
if "Content-Security-Policy" in response.headers:
csp_value = response.headers["Content-Security-Policy"]
# CSP should have at least a default-src directive
assert "default-src" in csp_value or "script-src" in csp_value, "CSP header missing required directives"
@pytest.mark.security
def test_x_frame_options_valid_value(client):
"""Test that X-Frame-Options header has valid value."""
from app.config import settings
if not settings.security_headers_enabled or not settings.security_header_x_frame_options_enabled:
pytest.skip("X-Frame-Options header is disabled in configuration")
response = client.get("/")
if "X-Frame-Options" in response.headers:
x_frame_value = response.headers["X-Frame-Options"]
valid_values = ["DENY", "SAMEORIGIN"]
# Note: ALLOW-FROM is deprecated in modern browsers; use CSP frame-ancestors instead
assert x_frame_value in valid_values or x_frame_value.startswith("ALLOW-FROM"), (
f"Invalid X-Frame-Options value: {x_frame_value}"
)
@pytest.mark.integration
def test_security_headers_configuration_loading():
"""Test that security header configuration is loaded correctly."""
from app.config import settings
# Verify that security header configuration attributes exist
assert hasattr(settings, "security_headers_enabled")
assert hasattr(settings, "security_header_hsts_enabled")
assert hasattr(settings, "security_header_hsts_value")
assert hasattr(settings, "security_header_csp_enabled")
assert hasattr(settings, "security_header_csp_value")
assert hasattr(settings, "security_header_x_frame_options_enabled")
assert hasattr(settings, "security_header_x_frame_options_value")
assert hasattr(settings, "security_header_x_content_type_options_enabled")
# Verify that boolean settings are actual booleans
assert isinstance(settings.security_headers_enabled, bool)
assert isinstance(settings.security_header_hsts_enabled, bool)
assert isinstance(settings.security_header_csp_enabled, bool)
assert isinstance(settings.security_header_x_frame_options_enabled, bool)
assert isinstance(settings.security_header_x_content_type_options_enabled, bool)
# Verify that string settings are actual strings
assert isinstance(settings.security_header_hsts_value, str)
assert isinstance(settings.security_header_csp_value, str)
assert isinstance(settings.security_header_x_frame_options_value, str)
@pytest.mark.integration
def test_middleware_respects_configuration():
"""Test that middleware respects individual header enable/disable settings."""
from app.config import settings
from app.middleware.security_headers import SecurityHeadersMiddleware
# Create middleware instance
middleware = SecurityHeadersMiddleware(app=None, config=settings)
# Verify that middleware stores configuration
assert middleware.config == settings
assert middleware.enabled == settings.security_headers_enabled