FossilRepo

fossilrepo / core / url_validation.py
Blame History Raw 62 lines
1
"""URL validation for outbound requests (webhooks, etc.)."""
2
3
import ipaddress
4
import socket
5
from urllib.parse import urlparse
6
7
8
def is_safe_outbound_url(url: str) -> tuple[bool, str]:
9
"""Validate a webhook URL is safe for server-side requests.
10
11
Blocks:
12
- Non-HTTP(S) protocols
13
- Localhost and loopback addresses
14
- Private/internal IP ranges (10.x, 172.16-31.x, 192.168.x, etc.)
15
- Link-local addresses
16
- AWS metadata endpoint (169.254.169.254)
17
18
Returns (is_safe, error_message).
19
"""
20
if not url:
21
return False, "URL is required."
22
23
parsed = urlparse(url)
24
25
if parsed.scheme not in ("http", "https"):
26
return False, "Only http:// and https:// URLs are allowed."
27
28
hostname = parsed.hostname
29
if not hostname:
30
return False, "URL must include a hostname."
31
32
# Block obvious localhost variants
33
if hostname in ("localhost", "127.0.0.1", "::1", "0.0.0.0"):
34
return False, "Localhost URLs are not allowed."
35
36
# Resolve hostname and check the IP
37
try:
38
addr_info = socket.getaddrinfo(hostname, None, socket.AF_UNSPEC, socket.SOCK_STREAM)
39
except socket.gaierror:
40
return False, f"Could not resolve hostname: {hostname}"
41
42
for _family, _type, _proto, _canonname, sockaddr in addr_info:
43
ip_str = sockaddr[0]
44
try:
45
ip = ipaddress.ip_address(ip_str)
46
except ValueError:
47
continue
48
49
if ip.is_loopback:
50
return False, "Loopback addresses are not allowed."
51
if ip.is_private:
52
return False, "Private/internal IP addresses are not allowed."
53
if ip.is_link_local:
54
return False, "Link-local addresses are not allowed."
55
if ip.is_reserved:
56
return False, "Reserved IP addresses are not allowed."
57
# AWS metadata endpoint
58
if ip_str == "169.254.169.254":
59
return False, "Cloud metadata endpoints are not allowed."
60
61
return True, ""
62

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button