oobabooga/textgen · error · ValueError
Invalid URL: userinfo (credentials) in URLs is not allowed
Error message
Invalid URL: userinfo (credentials) in URLs is not allowed
What it means
Raised by _validate_url() when the URL's network location contains an '@', i.e. embedded userinfo ('user:pass@host'). Different URL parsers and HTTP clients disagree about where the host begins when userinfo is present, which has been used to bypass SSRF host checks (the validator sees one host, the fetcher connects to another). It is rejected unconditionally, even for userinfo without a password.
Source
Thrown at modules/web_search.py:27
from ddgs import DDGS
from modules import shared
from modules.logging_colors import logger
def _validate_url(url):
"""Validate that a URL is safe to fetch (not targeting private/internal networks)."""
# Reject characters that cause parsing discrepancies between urlparse and requests,
# which can be exploited to bypass SSRF protections (GHSA-27xf-58m5-vxmc).
if '\\' in url:
raise ValueError("Invalid URL: backslashes are not allowed")
parsed = urlparse(url)
if parsed.scheme not in ('http', 'https'):
raise ValueError(f"Unsupported URL scheme: {parsed.scheme}")
if '@' in parsed.netloc:
raise ValueError("Invalid URL: userinfo (credentials) in URLs is not allowed")
hostname = parsed.hostname
if not hostname:
raise ValueError("No hostname in URL")
# Resolve hostname and check all returned addresses
try:
for family, _, _, _, sockaddr in socket.getaddrinfo(hostname, None):
ip = ipaddress.ip_address(sockaddr[0])
if not ip.is_global:
raise ValueError(f"Access to non-public address {ip} is blocked")
except socket.gaierror:
raise ValueError(f"Could not resolve hostname: {hostname}")
def safe_get(url, headers=None, timeout=10, max_redirects=5):
"""Fetch a URL with SSRF-safe redirect handling. Validates every hop."""
_validate_url(url)View on GitHub (pinned to ed888c71f2)
Solutions
- Remove the userinfo and pass credentials via headers instead (e.g. Authorization header), then re-submit the clean URL.
- If credentials are genuinely needed, strip them from the URL and configure HTTP Basic auth at the request layer outside the guarded fetch path.
- Treat this error on a redirect hop as a security signal — do not retry; report the offending upstream URL.
Example fix
# before
resp = safe_get('https://user:pass@example.com/api') # raises ValueError
# after
from urllib.parse import urlsplit, urlunsplit
parts = urlsplit('https://user:pass@example.com/api')
clean = urlunsplit((parts.scheme, parts.netloc.rpartition('@')[2], parts.path, parts.query, ''))
resp = safe_get(clean, headers={'Authorization': 'Basic <b64 user:pass>'}) Defensive patterns
Strategy: validation
Validate before calling
from urllib.parse import urlsplit, urlunsplit
def strip_userinfo(url: str) -> str:
parts = urlsplit(url)
if '@' in parts.netloc:
host = parts.netloc.rpartition('@')[2]
parts = parts._replace(netloc=host)
return urlunsplit(parts)
url = strip_userinfo(url) # then pass credentials via headers Try / catch
try:
resp = safe_get(url)
except ValueError as e:
if 'userinfo' in str(e):
raise RuntimeError('Embedded credentials are not supported; move them to an Authorization header') from e
raise Prevention
- Never embed credentials in URLs; use Authorization headers.
- Sanitize copied-from-browser URLs (they often carry encoded userinfo) before programmatic use.
- Treat userinfo in redirect Location headers as an SSRF-bypass attempt, not a feature.
When it happens
Trigger: Fetching 'https://user:pass@example.com/data', 'https://token@example.com', or any URL where the netloc contains '@'. Also triggered by a redirect Location containing embedded credentials, which is a classic SSRF-bypass attempt.
Common situations: User pastes an authenticated URL copied from a browser or API doc (basic-auth style); scripts that embed API tokens in the URL; malicious redirect chains trying to smuggle a private host past validation.
Related errors
- Invalid URL: backslashes are not allowed
- Unsupported URL scheme: {parsed.scheme}
- No hostname in URL
- Access to non-public address {ip} is blocked
- Could not resolve hostname: {hostname}
AI-assisted analysis of oobabooga/textgen@ed888c71f2 (2026-08-15).
Data as JSON: /api/errors/89c75c1993290808.
Report an issue: GitHub.