Address code review feedback: improve XSS prevention, structured errors, API key logging, CSP warnings

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-02-09 11:51:58 +00:00
parent 2e22941927
commit 8550799bf8
6 changed files with 77 additions and 78 deletions
+30 -16
View File
@@ -2,8 +2,19 @@ import re
import socket
from typing import Dict, Tuple, Union, Optional
# Error codes for structured error handling
class DomainValidationError:
"""Domain validation error codes"""
EMPTY = "empty"
TOO_LONG = "too_long"
INVALID_FORMAT = "invalid_format"
INVALID_CHARACTERS = "invalid_characters"
LABEL_TOO_LONG = "label_too_long"
INVALID_LABEL = "invalid_label"
DNS_RESOLUTION_FAILED = "dns_resolution_failed"
def validate_domain(domain_name: str, check_dns: bool = True) -> Tuple[bool, Optional[str]]:
def validate_domain(domain_name: str, check_dns: bool = True) -> Tuple[bool, Optional[str], Optional[str]]:
"""
Validates a domain name for format and optionally resolvability.
@@ -12,25 +23,26 @@ def validate_domain(domain_name: str, check_dns: bool = True) -> Tuple[bool, Opt
check_dns: Whether to perform DNS resolution check (default: True)
Returns:
Tuple containing (is_valid, error_message)
Tuple containing (is_valid, error_message, error_code)
- is_valid: Boolean indicating if domain is valid
- error_message: String with error message if not valid, None if valid
- error_code: Error code constant for programmatic handling, None if valid
"""
# Security: Check for empty or None domain
if not domain_name:
return False, "Domain name cannot be empty"
return False, "Domain name cannot be empty", DomainValidationError.EMPTY
# Security: Check maximum length (DNS standard is 253 characters)
if len(domain_name) > 253:
return False, "Domain name too long (max 253 characters)"
return False, "Domain name too long (max 253 characters)", DomainValidationError.TOO_LONG
# Security: Check for whitespace
if ' ' in domain_name or '\t' in domain_name or '\n' in domain_name:
return False, "Domain name cannot contain whitespace"
return False, "Domain name cannot contain whitespace", DomainValidationError.INVALID_CHARACTERS
# Security: Check for suspicious characters
if any(char in domain_name for char in ['<', '>', '"', "'", '\\', '|', ';', '&', '$', '`']):
return False, "Domain name contains invalid characters"
return False, "Domain name contains invalid characters", DomainValidationError.INVALID_CHARACTERS
# Check domain format with regex
# This regex allows domain names with alphanumeric characters, hyphens,
@@ -38,27 +50,27 @@ def validate_domain(domain_name: str, check_dns: bool = True) -> Tuple[bool, Opt
# Updated to be more strict and prevent potential attacks
domain_pattern = r'^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$'
if not re.match(domain_pattern, domain_name.lower()):
return False, "Invalid domain format"
return False, "Invalid domain format", DomainValidationError.INVALID_FORMAT
# Security: Check each label length (max 63 characters per label)
labels = domain_name.split('.')
for label in labels:
if len(label) > 63:
return False, f"Domain label too long: '{label}' (max 63 characters per label)"
return False, f"Domain label too long: '{label}' (max 63 characters per label)", DomainValidationError.LABEL_TOO_LONG
if label.startswith('-') or label.endswith('-'):
return False, f"Domain label cannot start or end with hyphen: '{label}'"
return False, f"Domain label cannot start or end with hyphen: '{label}'", DomainValidationError.INVALID_LABEL
# Check if domain exists by attempting to resolve DNS (optional)
if check_dns:
try:
socket.gethostbyname(domain_name)
return True, None
return True, None, None
except socket.gaierror:
# We could consider this valid if we don't require DNS resolution,
# but since DMARC requires valid DNS, we'll mark it as warning
return False, "Domain could not be resolved (DNS lookup failed)"
return False, "Domain could not be resolved (DNS lookup failed)", DomainValidationError.DNS_RESOLUTION_FAILED
return True, None
return True, None, None
def validate_domain_config(domain_data: Dict) -> Dict[str, Union[bool, str]]:
@@ -78,7 +90,7 @@ def validate_domain_config(domain_data: Dict) -> Dict[str, Union[bool, str]]:
# Validate domain name
if "name" in domain_data:
# Don't check DNS for domain config validation
is_valid, error_msg = validate_domain(domain_data["name"], check_dns=False)
is_valid, error_msg, error_code = validate_domain(domain_data["name"], check_dns=False)
if not is_valid:
errors["name"] = error_msg
else:
@@ -88,9 +100,11 @@ def validate_domain_config(domain_data: Dict) -> Dict[str, Union[bool, str]]:
if "description" in domain_data and domain_data["description"]:
if len(domain_data["description"]) > 500:
errors["description"] = "Description is too long (max 500 characters)"
# Security: Check for suspicious content in description
if any(char in domain_data["description"] for char in ['<script', '<iframe', 'javascript:']):
errors["description"] = "Description contains potentially unsafe content"
# Security: Use html.escape to prevent XSS
import html
escaped = html.escape(domain_data["description"])
if escaped != domain_data["description"]:
errors["description"] = "Description contains potentially unsafe HTML content"
# Return validation results
return {