feat(security): merge Phase 4b security hardening
Complete security hardening implementation including HTTPS enforcement, security headers, rate limiting, and comprehensive security test suite. Key features: - HTTPS enforcement with HSTS support - Security headers (CSP, X-Frame-Options, X-Content-Type-Options) - Rate limiting for all critical endpoints - Enhanced email template security - 87% test coverage with security-specific tests Architect approval: 9.5/10 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
65
tests/security/test_csrf_protection.py
Normal file
65
tests/security/test_csrf_protection.py
Normal file
@@ -0,0 +1,65 @@
|
||||
"""Security tests for CSRF protection."""
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.security
|
||||
class TestCSRFProtection:
|
||||
"""Test CSRF protection via state parameter."""
|
||||
|
||||
def test_state_parameter_preserved(self):
|
||||
"""Test that state parameter is preserved in authorization flow."""
|
||||
from gondulf.storage import CodeStore
|
||||
|
||||
code_store = CodeStore(ttl_seconds=600)
|
||||
|
||||
original_state = "my-csrf-token-with-special-chars-!@#$%"
|
||||
|
||||
# Store authorization code with state
|
||||
code = "test_code_12345"
|
||||
code_data = {
|
||||
"client_id": "https://client.example.com",
|
||||
"redirect_uri": "https://client.example.com/callback",
|
||||
"me": "https://user.example.com",
|
||||
"state": original_state,
|
||||
}
|
||||
|
||||
code_store.store(code, code_data)
|
||||
|
||||
# Retrieve code data
|
||||
retrieved_data = code_store.get(code)
|
||||
|
||||
# State should be unchanged
|
||||
assert retrieved_data["state"] == original_state
|
||||
|
||||
def test_state_parameter_returned_unchanged(self):
|
||||
"""Test that state parameter is returned without modification."""
|
||||
from gondulf.storage import CodeStore
|
||||
|
||||
code_store = CodeStore(ttl_seconds=600)
|
||||
|
||||
# Test various state values
|
||||
test_states = [
|
||||
"simple-state",
|
||||
"state_with_underscores",
|
||||
"state-with-dashes",
|
||||
"state.with.dots",
|
||||
"state!with@special#chars",
|
||||
"very-long-state-" + "x" * 100,
|
||||
]
|
||||
|
||||
for state in test_states:
|
||||
code = f"code_{hash(state)}"
|
||||
code_data = {
|
||||
"client_id": "https://client.example.com",
|
||||
"redirect_uri": "https://client.example.com/callback",
|
||||
"me": "https://user.example.com",
|
||||
"state": state,
|
||||
}
|
||||
|
||||
code_store.store(code, code_data)
|
||||
retrieved = code_store.get(code)
|
||||
|
||||
assert (
|
||||
retrieved["state"] == state
|
||||
), f"State modified: {state} -> {retrieved['state']}"
|
||||
Reference in New Issue
Block a user