fix(studio): support hostname-based enterprise proxies (#7416)

* fix(studio): support hostname-based enterprise proxies

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fix(studio): strip userinfo from proxy fetch targets

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Lee Jackson <130007945+Imagineer99@users.noreply.github.com>
This commit is contained in:
alkinun 2026-07-26 04:53:00 +03:00 committed by GitHub
commit 97475be347
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 126 additions and 8 deletions

View file

@ -1285,6 +1285,12 @@ def studio_default(
help = "Force server-side tools (web search, code execution) on or off for "
"every request. Default: on for every bind, with the per-chat UI toggle honored.",
),
disable_dns_pinning: bool = typer.Option(
False,
"--disable-dns-pinning",
help = "Allow hostname-based web fetches for enterprise proxies. WARNING: weakens "
"DNS-rebinding protection; hostname and redirect validation remain enabled.",
),
password: str = typer.Option(
"",
"--password",
@ -1354,6 +1360,15 @@ def studio_default(
err = True,
)
raise typer.Exit(2)
if disable_dns_pinning:
typer.echo(
"Error: --disable-dns-pinning on `unsloth studio` applies to the "
f"plain-server path only. For `unsloth studio {ctx.invoked_subcommand}`, "
f"put it after the subcommand: `unsloth studio {ctx.invoked_subcommand} "
"--disable-dns-pinning ...`",
err = True,
)
raise typer.Exit(2)
# Same for --api-only: dropping it here would silently serve the UI.
if api_only:
typer.echo(
@ -1398,6 +1413,10 @@ def studio_default(
# default (plain-server path; the `run` subcommand has its own --verbose).
if verbose:
_enable_verbose_access_logs()
if disable_dns_pinning:
os.environ["UNSLOTH_STUDIO_DISABLE_DNS_PINNING"] = "1"
else:
os.environ.setdefault("UNSLOTH_STUDIO_DISABLE_DNS_PINNING", "0")
# Use the studio venv if present and not already in it. Resolve the child
# launcher BEFORE the gate: a headless gate strips the seeded
@ -1739,6 +1758,13 @@ def run(
"every request. Default: on for every bind."
),
),
disable_dns_pinning: bool = typer.Option(
False,
"--disable-dns-pinning",
rich_help_panel = _RUN_PANEL_TOOLS,
help = "Allow hostname-based web fetches for enterprise proxies. WARNING: weakens "
"DNS-rebinding protection; hostname and redirect validation remain enabled.",
),
tool_call_healing: Optional[bool] = typer.Option(
None,
"--enable-tool-call-healing/--disable-tool-call-healing",
@ -1944,6 +1970,10 @@ def run(
_enable_verbose_access_logs()
if not any(a in ("--verbose", "-v", "--log-verbose") for a in extra_llama_args):
extra_llama_args.append("--log-verbose")
if disable_dns_pinning:
os.environ["UNSLOTH_STUDIO_DISABLE_DNS_PINNING"] = "1"
else:
os.environ.setdefault("UNSLOTH_STUDIO_DISABLE_DNS_PINNING", "0")
# Promote legacy exact `-m`/`-hfr`/`-f` back into typer params;
# clusters stay in extras.