Skip to content

Commit a22e139

Browse files
john-westcott-ivClaude (Assistant)
andauthored
[AAP-50837] Adding domain validation function (ansible#792)
Co-Authored by Claud AI via Cursor ## Description <!-- Mandatory: Provide a clear, concise description of the changes and their purpose --> - What is being changed? Adding a domain validation function and testing. - Why is this change needed? CSRF_TRUSTED_ORIGINS need to be valid top level domain - How does this change address the issue? This function will be part of CSRF_TRUSTED_ORIGINS validation by other components. ## Type of Change <!-- Mandatory: Check one or more boxes that apply --> - [ ] Bug fix (non-breaking change which fixes an issue) - [X] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] Documentation update - [X] Test update - [ ] Refactoring (no functional changes) - [ ] Development environment change - [ ] Configuration change ## Self-Review Checklist <!-- These items help ensure quality - they complement our automated CI checks --> - [X] I have performed a self-review of my code - [X] I have added relevant comments to complex code sections - [X] I have updated documentation where needed - [X] I have considered the security impact of these changes - [X] I have considered performance implications - [X] I have thought about error handling and edge cases - [X] I have tested the changes in my local environment ## Testing Instructions <!-- Optional for test-only changes. Mandatory for all other changes --> <!-- Must be detailed enough for reviewers to reproduce --> ### Prerequisites <!-- List any specific setup required --> ### Steps to Test 1. 2. 3. ### Expected Results <!-- Describe what should happen after following the steps --> ## Additional Context <!-- Optional but helpful information --> ### Required Actions <!-- Check if changes require work in other areas --> <!-- Remove section if no external actions needed --> - [ ] Requires documentation updates <!-- API docs, feature docs, deployment guides --> - [ ] Requires downstream repository changes <!-- Specify repos: django-ansible-base, eda-server, etc. --> - [ ] Requires infrastructure/deployment changes <!-- CI/CD, installer updates, new services --> - [ ] Requires coordination with other teams <!-- UI team, platform services, infrastructure --> - [ ] Blocked by PR/MR: #XXX <!-- Reference blocking PRs/MRs with brief context --> ### Screenshots/Logs <!-- Add if relevant to demonstrate the changes --> --------- Co-authored-by: Claude (Assistant) <[email protected]>
1 parent 20dc761 commit a22e139

File tree

2 files changed

+638
-1
lines changed

2 files changed

+638
-1
lines changed

ansible_base/lib/utils/validation.py

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import re
44
import secrets
55
from pathlib import Path
6+
from typing import Any
67
from urllib.parse import urlparse, urlunsplit
78

89
from cryptography.exceptions import InvalidSignature
@@ -144,6 +145,114 @@ def validate_image_data(data: str) -> None:
144145
raise ValidationError(_("Invalid base64-encoded data in data URL."))
145146

146147

148+
def _is_valid_domain_format(domain: str) -> bool:
149+
"""Check basic domain format requirements."""
150+
return isinstance(domain, str) and bool(domain) and len(domain) <= 255 and '.' in domain
151+
152+
153+
def _normalize_domain(domain: str) -> str:
154+
"""Normalize domain by removing trailing dot if present."""
155+
return domain[:-1] if domain.endswith('.') else domain
156+
157+
158+
def _is_valid_label(label: str) -> bool:
159+
"""Validate a single domain label according to LDH (Letter, Digit, Hyphen) rule."""
160+
return bool(label) and len(label) <= 63 and re.match(r'^[a-zA-Z0-9-]+$', label) is not None and not label.startswith('-') and not label.endswith('-')
161+
162+
163+
def _is_valid_tld(tld: str) -> bool:
164+
"""Validate the top-level domain."""
165+
return len(tld) >= 2 and not tld.isdigit() and re.search(r'[a-zA-Z]', tld) is not None
166+
167+
168+
def validate_domain_name(domain: str) -> bool:
169+
"""
170+
Validate a domain name according to RFC standards.
171+
172+
Validates domain names according to RFC 1035, 1123, and 2181 specifications.
173+
174+
Args:
175+
domain: The domain name to validate
176+
177+
Returns:
178+
bool: True if the domain name is valid, False otherwise
179+
180+
Checks:
181+
- Length limits (labels ≤ 63 chars, total ≤ 255 chars)
182+
- LDH rule (Letters, Digits, Hyphens only)
183+
- No leading/trailing hyphens in labels
184+
- Valid TLD format (not all-numeric, at least 2 chars)
185+
- At least one dot (fully qualified domain name)
186+
"""
187+
# Basic format validation
188+
if not _is_valid_domain_format(domain):
189+
return False
190+
191+
# Normalize and split domain into labels
192+
normalized_domain = _normalize_domain(domain)
193+
labels = normalized_domain.split('.')
194+
195+
# Must have at least domain.tld
196+
if len(labels) < 2:
197+
return False
198+
199+
# Validate each label
200+
for label in labels:
201+
if not _is_valid_label(label):
202+
return False
203+
204+
# Validate TLD (last label)
205+
return _is_valid_tld(labels[-1])
206+
207+
208+
def validate_port(port: Any) -> bool:
209+
"""
210+
Validate a network port number.
211+
212+
Accepts port numbers as integers or strings and validates they are within
213+
the valid TCP/UDP port range (1-65535).
214+
215+
Args:
216+
port: Port number as int, str, or other type
217+
218+
Returns:
219+
bool: True if the port is valid, False otherwise
220+
221+
Examples:
222+
validate_port(80) # True
223+
validate_port("443") # True
224+
validate_port("0") # False (port 0 is reserved)
225+
validate_port("65536") # False (above valid range)
226+
validate_port(None) # False (invalid type)
227+
validate_port("abc") # False (non-numeric string)
228+
"""
229+
# Handle None and non-string/non-integer types
230+
if port is None:
231+
return False
232+
233+
# Explicitly reject boolean types (even though they're technically integers in Python)
234+
if isinstance(port, bool):
235+
return False
236+
237+
# Convert to integer if it's a string
238+
if isinstance(port, str):
239+
# Reject strings with leading/trailing whitespace for stricter validation
240+
if port != port.strip():
241+
return False
242+
try:
243+
port_int = int(port)
244+
except ValueError:
245+
return False
246+
elif isinstance(port, int):
247+
port_int = port
248+
else:
249+
# Reject other types (float, list, dict, etc.)
250+
return False
251+
252+
# Validate port range (1-65535)
253+
return 1 <= port_int <= 65535
254+
255+
147256
def to_python_boolean(value, allow_none=False):
148257
value = str(value)
149258
if value.lower() in ('true', '1', 't'):

0 commit comments

Comments
 (0)