fix: Allow HTTP for all private network ranges in Supabase URLs (#417)
* fix: Allow HTTP for all private network ranges in Supabase URLs - Extend HTTP support to all RFC 1918 private IP ranges - Class A: 10.0.0.0 to 10.255.255.255 (10.0.0.0/8) - Class B: 172.16.0.0 to 172.31.255.255 (172.16.0.0/12) - Class C: 192.168.0.0 to 192.168.255.255 (192.168.0.0/16) - Also includes link-local (169.254.0.0/16) addresses - Uses Python's ipaddress module for robust IP validation - Maintains HTTPS requirement for public/production URLs - Backwards compatible with existing localhost exceptions * security: Fix URL validation vulnerabilities - Replace substring matching with exact hostname matching to prevent bypass attacks - Exclude unspecified address (0.0.0.0) from allowed HTTP hosts - Add support for .localhost domains per RFC 6761 - Improve error messages with hostname context for better debugging Addresses security concerns raised in PR review regarding: - Malicious domains like 'localhost.attacker.com' bypassing HTTPS requirements - Unspecified address being incorrectly allowed as valid connection target --------- Co-authored-by: tazmon95 <tazmon95@users.noreply.github.com> Co-authored-by: root <root@supatest2.jtpa.net>
This commit is contained in:
parent
6f7c08eeb7
commit
eb526af689
@ -3,6 +3,7 @@ Environment configuration management for the MCP server.
|
||||
"""
|
||||
|
||||
import os
|
||||
import ipaddress
|
||||
from dataclasses import dataclass
|
||||
from urllib.parse import urlparse
|
||||
|
||||
@ -104,8 +105,29 @@ def validate_supabase_url(url: str) -> bool:
|
||||
# Require HTTPS for production (non-local) URLs
|
||||
if parsed.scheme == "http":
|
||||
hostname = parsed.hostname or ""
|
||||
if not any(local in hostname for local in ["localhost", "127.0.0.1", "host.docker.internal", "0.0.0.0"]):
|
||||
raise ConfigurationError("Supabase URL must use HTTPS for non-local environments")
|
||||
|
||||
# Check for exact localhost and Docker internal hosts (security: prevent subdomain bypass)
|
||||
local_hosts = ["localhost", "127.0.0.1", "host.docker.internal"]
|
||||
if hostname in local_hosts or hostname.endswith(".localhost"):
|
||||
return True
|
||||
|
||||
# Check if hostname is a private IP address
|
||||
try:
|
||||
ip = ipaddress.ip_address(hostname)
|
||||
# Allow HTTP for private IP addresses (RFC 1918)
|
||||
# Class A: 10.0.0.0/8
|
||||
# Class B: 172.16.0.0/12
|
||||
# Class C: 192.168.0.0/16
|
||||
# Also includes link-local (169.254.0.0/16) and loopback
|
||||
# Exclude unspecified address (0.0.0.0) for security
|
||||
if (ip.is_private or ip.is_loopback or ip.is_link_local) and not ip.is_unspecified:
|
||||
return True
|
||||
except ValueError:
|
||||
# hostname is not a valid IP address, could be a domain name
|
||||
pass
|
||||
|
||||
# If not a local host or private IP, require HTTPS
|
||||
raise ConfigurationError(f"Supabase URL must use HTTPS for non-local environments (hostname: {hostname})")
|
||||
|
||||
if not parsed.netloc:
|
||||
raise ConfigurationError("Invalid Supabase URL format")
|
||||
|
||||
Loading…
Reference in New Issue
Block a user