Files
Gondulf/tests/security/test_xss_prevention.py
Phil Skentelbery d3c3e8dc6b 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>
2025-11-20 18:28:50 -07:00

84 lines
3.0 KiB
Python

"""Security tests for XSS prevention."""
import pytest
from jinja2 import Environment
@pytest.mark.security
class TestXSSPrevention:
"""Test XSS prevention in HTML templates."""
def test_client_name_xss_escaped(self):
"""Test that client name is HTML-escaped in templates."""
# Test that Jinja2 autoescaping works
malicious_name = '<script>alert("XSS")</script>'
env = Environment(autoescape=True)
template_source = "{{ client_name }}"
template = env.from_string(template_source)
rendered = template.render(client_name=malicious_name)
# Should be escaped
assert "<script>" not in rendered
assert "&lt;script&gt;" in rendered
def test_me_parameter_xss_escaped(self):
"""Test that 'me' parameter is HTML-escaped in UI."""
malicious_me = '<img src=x onerror="alert(1)">'
env = Environment(autoescape=True)
template_source = "<p>{{ me }}</p>"
template = env.from_string(template_source)
rendered = template.render(me=malicious_me)
# Should be escaped
assert "<img" not in rendered
assert "&lt;img" in rendered
def test_client_url_xss_escaped(self):
"""Test that client URL is HTML-escaped in templates."""
malicious_url = "javascript:alert(1)"
env = Environment(autoescape=True)
template_source = '<a href="{{ client_url }}">{{ client_url }}</a>'
template = env.from_string(template_source)
rendered = template.render(client_url=malicious_url)
# Jinja2 escapes href attributes
# Note: javascript: URLs still need validation at input layer (handled by Pydantic HttpUrl)
assert "javascript:" in rendered # Jinja2 doesn't prevent javascript: in href
# So we rely on Pydantic HttpUrl validation
def test_jinja2_autoescape_enabled(self):
"""Test that Jinja2 autoescaping is enabled by default."""
from fastapi.templating import Jinja2Templates
# FastAPI's Jinja2Templates has autoescape=True by default
# Create templates instance to verify
templates = Jinja2Templates(directory="src/gondulf/templates")
assert templates.env.autoescape is True
def test_html_entities_escaped(self):
"""Test that HTML entities are properly escaped."""
env = Environment(autoescape=True)
dangerous_inputs = [
"<script>alert('xss')</script>",
"<img src=x onerror=alert(1)>",
'<a href="javascript:alert(1)">click</a>',
"'; DROP TABLE users; --",
"<svg/onload=alert('xss')>",
]
for dangerous_input in dangerous_inputs:
template = env.from_string("{{ value }}")
rendered = template.render(value=dangerous_input)
# Verify dangerous characters are escaped
assert "<" not in rendered or "&lt;" in rendered
assert ">" not in rendered or "&gt;" in rendered
assert '"' not in rendered or "&quot;" in rendered or "&#34;" in rendered