Bug Description
The /api/ai/test and /api/ai/chat endpoints accept a baseUrl field from the request body and use it directly to construct outbound HTTP requests, with no validation of the destination host. This allows any caller to make the UltraRAG server issue requests to arbitrary internal or external URLs.
Location
ui/backend/app.py, /api/ai/test handler (~line 2360):
base_url = payload.get("baseUrl", "").rstrip("/")
test_url = f"{base_url}/models"
resp = requests.get(test_url, headers=headers, timeout=10)
ui/backend/app.py, /api/ai/chat handler (~line 2510):
base_url = settings.get("baseUrl", "").rstrip("/")
chat_url = f"{base_url}/chat/completions"
resp = requests.post(chat_url, headers=headers, ...)
Reproduction
curl -X POST http://<ultrarag-host>/api/ai/test \
-H "Content-Type: application/json" \
-d '{"provider":"openai","baseUrl":"http://169.254.169.254/latest","apiKey":"x","model":"m"}'
The server will issue GET http://169.254.169.254/latest/models, reaching the cloud instance metadata service or any internal endpoint.
Impact
Attackers can use the UltraRAG server as an SSRF proxy to enumerate and access internal services, cloud metadata endpoints (AWS/GCP/Azure IMDS), and other hosts unreachable from the public internet.
Suggested Fix
Validate baseUrl against a strict allowlist of permitted AI provider domains, and reject private/loopback/link-local addresses after DNS resolution before making any outbound request:
from urllib.parse import urlparse
import ipaddress
ALLOWED_HOSTS = {"api.openai.com", "api.anthropic.com", ...}
parsed = urlparse(base_url)
if parsed.hostname not in ALLOWED_HOSTS:
return jsonify({"success": False, "error": "baseUrl not permitted"})
Found via automated codebase analysis. Happy to submit a PR if this is confirmed.
Bug Description
The
/api/ai/testand/api/ai/chatendpoints accept abaseUrlfield from the request body and use it directly to construct outbound HTTP requests, with no validation of the destination host. This allows any caller to make the UltraRAG server issue requests to arbitrary internal or external URLs.Location
ui/backend/app.py,/api/ai/testhandler (~line 2360):ui/backend/app.py,/api/ai/chathandler (~line 2510):Reproduction
The server will issue
GET http://169.254.169.254/latest/models, reaching the cloud instance metadata service or any internal endpoint.Impact
Attackers can use the UltraRAG server as an SSRF proxy to enumerate and access internal services, cloud metadata endpoints (AWS/GCP/Azure IMDS), and other hosts unreachable from the public internet.
Suggested Fix
Validate
baseUrlagainst a strict allowlist of permitted AI provider domains, and reject private/loopback/link-local addresses after DNS resolution before making any outbound request:Found via automated codebase analysis. Happy to submit a PR if this is confirmed.