diff --git a/src/agents/extensions/sandbox/daytona/sandbox.py b/src/agents/extensions/sandbox/daytona/sandbox.py index d1335df377..2067cf913f 100644 --- a/src/agents/extensions/sandbox/daytona/sandbox.py +++ b/src/agents/extensions/sandbox/daytona/sandbox.py @@ -343,8 +343,10 @@ async def _resolve_exposed_port(self, port: int) -> ExposedPortEndpoint: host = split.hostname if host is None: raise ValueError("missing hostname") - port_value = split.port or (443 if split.scheme == "https" else 80) - return ExposedPortEndpoint(host=host, port=port_value, tls=split.scheme == "https") + if split.scheme != "https": + raise ValueError(f"non-https preview scheme: {split.scheme!r}") + port_value = split.port or 443 + return ExposedPortEndpoint(host=host, port=port_value, tls=True) except Exception as e: raise ExposedPortUnavailableError( port=port, diff --git a/tests/extensions/sandbox/test_daytona.py b/tests/extensions/sandbox/test_daytona.py index 5665e60cf4..c77a6ac0e8 100644 --- a/tests/extensions/sandbox/test_daytona.py +++ b/tests/extensions/sandbox/test_daytona.py @@ -904,6 +904,39 @@ async def _bad_preview_url( assert exc_info.value.context["detail"] == "invalid_preview_url" + @pytest.mark.asyncio + async def test_resolve_exposed_port_rejects_non_https_preview_url( + self, + monkeypatch: pytest.MonkeyPatch, + ) -> None: + """Verify Daytona rejects plaintext preview URLs instead of silently downgrading TLS.""" + + daytona_module = _load_daytona_module(monkeypatch) + + async with daytona_module.DaytonaSandboxClient() as client: + session = await client.create( + options=daytona_module.DaytonaSandboxClientOptions(exposed_ports=(4500,)), + ) + sandbox = _FakeAsyncDaytona.current_sandbox + assert sandbox is not None + + async def _http_preview_url( + port: int, + expires_in_seconds: int | None = None, + ) -> object: + _ = (port, expires_in_seconds) + return types.SimpleNamespace( + url=f"http://{port}-token.daytonaproxy01.net/", + token="signed-token", + ) + + sandbox.create_signed_preview_url = _http_preview_url # type: ignore[method-assign] + + with pytest.raises(daytona_module.ExposedPortUnavailableError) as exc_info: + await session.resolve_exposed_port(4500) + + assert exc_info.value.context["detail"] == "invalid_preview_url" + @pytest.mark.asyncio async def test_normalize_path_rejects_workspace_escape_and_allows_absolute_in_root( self,