mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-11-06 21:22:23 +01:00
Merge current dev and refresh PR #6739 from the 7011 integration
This commit is contained in:
@@ -47,6 +47,7 @@ from src.endpoint_resolver import (
|
||||
build_headers,
|
||||
)
|
||||
from src.auth_helpers import _auth_disabled, effective_user, owner_filter
|
||||
from src.url_safety import check_outbound_url
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -2172,6 +2173,19 @@ def setup_model_routes(model_discovery):
|
||||
# keep those container-local when the frontend marks them as such.
|
||||
base_url = _rewrite_loopback_for_docker(base_url, container_local=_truthy(container_local))
|
||||
|
||||
# SSRF hardening: validate the user-supplied URL before any outbound
|
||||
# request. Local-first means loopback/LAN endpoints are allowed by
|
||||
# default; non-HTTP(S) schemes and the cloud metadata range are always
|
||||
# rejected. Set MODELENDPOINT_BLOCK_PRIVATE_IPS=true for full lockdown.
|
||||
# Note: the outbound fetches rely on httpx's default
|
||||
# follow_redirects=False — a redirect target is never dereferenced.
|
||||
ok, reason = check_outbound_url(
|
||||
base_url,
|
||||
block_private=os.getenv("MODELENDPOINT_BLOCK_PRIVATE_IPS", "false").lower() == "true",
|
||||
)
|
||||
if not ok:
|
||||
raise HTTPException(400, f"Rejected endpoint URL: {reason}")
|
||||
|
||||
is_featherless = _host_match(base_url, "featherless.ai") or _safe_detect_provider(base_url) == "featherless"
|
||||
# Auto-generate name from URL if not provided
|
||||
if not name.strip():
|
||||
@@ -2408,6 +2422,16 @@ def setup_model_routes(model_discovery):
|
||||
from src.endpoint_resolver import resolve_url
|
||||
base_url = resolve_url(base_url)
|
||||
base_url = _rewrite_loopback_for_docker(base_url)
|
||||
# SSRF hardening: validate the user-supplied URL before any outbound
|
||||
# request. Local-first means loopback/LAN endpoints are allowed by
|
||||
# default; non-HTTP(S) schemes and the cloud metadata range are always
|
||||
# rejected. Set MODELENDPOINT_BLOCK_PRIVATE_IPS=true for full lockdown.
|
||||
ok, reason = check_outbound_url(
|
||||
base_url,
|
||||
block_private=os.getenv("MODELENDPOINT_BLOCK_PRIVATE_IPS", "false").lower() == "true",
|
||||
)
|
||||
if not ok:
|
||||
raise HTTPException(400, f"Rejected endpoint URL: {reason}")
|
||||
requested_kind = _normalize_endpoint_kind(endpoint_kind)
|
||||
is_featherless = _host_match(base_url, "featherless.ai") or _safe_detect_provider(base_url) == "featherless"
|
||||
if is_featherless and requested_kind == "auto":
|
||||
@@ -2898,6 +2922,16 @@ def setup_model_routes(model_discovery):
|
||||
_new_base = _new_base[: -len(_suffix)].rstrip("/")
|
||||
_new_base = _normalize_base(_new_base)
|
||||
if _new_base:
|
||||
# SSRF hardening: PATCH rewrites the stored base_url, so
|
||||
# it needs the same guard as POST create/test — no
|
||||
# bypass through the update path. Follow-up fetches
|
||||
# rely on httpx's default follow_redirects=False.
|
||||
ok, reason = check_outbound_url(
|
||||
_new_base,
|
||||
block_private=os.getenv("MODELENDPOINT_BLOCK_PRIVATE_IPS", "false").lower() == "true",
|
||||
)
|
||||
if not ok:
|
||||
raise HTTPException(400, f"Rejected endpoint URL: {reason}")
|
||||
ep.base_url = _new_base
|
||||
else:
|
||||
ep.is_enabled = not ep.is_enabled
|
||||
|
||||
Reference in New Issue
Block a user