Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
ec5be86
fix(sglang): validate diffusion input_reference against traversal and…
nnshah1 Sep 8, 2026
4db60f8
fix(sglang): extend input_reference validation to video diffusion; sh…
nnshah1 Sep 8, 2026
92201a2
fix(common-http): percent-decode file:// media paths (review)
nnshah1 Sep 8, 2026
5070d4c
fix(sglang): fetch diffusion input_reference URLs SSRF-safe (review)
nnshah1 Sep 10, 2026
f7e48c0
Merge remote-tracking branch 'origin/main' into pr14435-fix
dmitry-tokarev-nv Sep 11, 2026
0debb20
fix(common-http): bound the media reference echoed into errors and logs
dmitry-tokarev-nv Sep 11, 2026
b716ec5
fix(common-http): let a caller cap how many bytes fetch_bytes will bu…
dmitry-tokarev-nv Sep 11, 2026
df50430
fix(common-http): make local_media_reference yield only a local path
dmitry-tokarev-nv Sep 11, 2026
1d215cd
fix(sglang): return 400 for a rejected diffusion input_reference
dmitry-tokarev-nv Sep 11, 2026
82aeb11
fix(common-http): bound the URL echoed by validate_url and the redire…
dmitry-tokarev-nv Sep 11, 2026
02346fe
fix(common-http): close four gaps from a second review pass
dmitry-tokarev-nv Sep 11, 2026
dbf9e1d
fix(common-http): bound HttpStatusError where the binding actually re…
dmitry-tokarev-nv Sep 11, 2026
4abc822
Merge remote-tracking branch 'origin/main' into pr14435-fix
dmitry-tokarev-nv Sep 11, 2026
6ffa332
fix(common-http): stop the HttpStatusError bound eating actionable gu…
dmitry-tokarev-nv Sep 11, 2026
ec106be
Merge remote-tracking branch 'origin/main' into pr14435-fix
dmitry-tokarev-nv Sep 14, 2026
1fad15c
Merge branch 'main' into pr14435-fix
dmitry-tokarev-nv Sep 15, 2026
3f7e3db
fix(common-http): bound the OSError diagnostic and the data-URI media…
dmitry-tokarev-nv Sep 15, 2026
3230408
fix(common-http): name aiohttp, not the removed backend, in two comments
dmitry-tokarev-nv Sep 16, 2026
75c1a6d
feat(common-http): make the media download cap operator-tunable
dmitry-tokarev-nv Sep 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,17 @@
import time
import uuid
from typing import Any, AsyncGenerator, Optional
from urllib.parse import urlparse

import torch
from PIL import Image

from dynamo._core import Context
from dynamo.common.http.url_validator import (
UrlValidationPolicy,
validate_local_path,
validate_url,
)
from dynamo.common.protocols.image_protocol import ImageNvExt
from dynamo.common.storage import upload_to_fs
from dynamo.sglang.args import Config
Expand All @@ -23,6 +29,20 @@

logger = logging.getLogger(__name__)


async def _validate_input_reference(reference: str) -> str:
"""Validate a client-supplied I2I image reference before it becomes the
generator's ``image_path``. Local paths are confined to ``DYN_MM_LOCAL_PATH``
(traversal rejected); URLs are blocked from resolving to internal addresses.
"""
policy = UrlValidationPolicy.from_env()
parsed = urlparse(reference)
if parsed.scheme.lower() in ("", "file"):
raw = parsed.path if parsed.scheme else reference
return str(validate_local_path(raw, policy))
return await validate_url(reference, policy)


MAX_NUM_INFERENCE_STEPS = 50
DEFAULT_NUM_INFERENCE_STEPS = 50
DEFAULT_GUIDANCE_SCALE = 7.5
Expand Down Expand Up @@ -179,7 +199,7 @@ async def _generate_images(
if input_reference is not None:
if not input_reference.strip():
raise ValueError("input_reference must be a non-empty string")
args["image_path"] = input_reference
args["image_path"] = await _validate_input_reference(input_reference)
Comment thread
dmitry-tokarev-nv marked this conversation as resolved.
Resolved

result = await asyncio.to_thread(
self.generator.generate,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -392,32 +392,54 @@ async def test_generate_with_nvext(self, handler, mock_context):

@pytest.mark.asyncio
async def test_generate_i2i_passes_image_path(
self, handler, mock_context, tmp_path
self, handler, mock_context, tmp_path, monkeypatch
):
"""Test that input_reference is passed as image_path to the generator."""
"""input_reference inside DYN_MM_LOCAL_PATH is resolved and passed as image_path."""
Comment thread
dmitry-tokarev-nv marked this conversation as resolved.
Resolved
test_image = Image.new("RGB", (256, 256), color="green")

handler.generator.generate = Mock(
return_value=SimpleNamespace(frames=[test_image])
)

input_ref = str(tmp_path / "test_input.png")
monkeypatch.setenv("DYN_MM_LOCAL_PATH", str(tmp_path))
input_ref = tmp_path / "test_input.png"
input_ref.write_bytes(b"x")
request = {
"prompt": "Transform this image",
"model": "test-model",
"size": "256x256",
"response_format": "b64_json",
"input_reference": input_ref,
"input_reference": str(input_ref),
}

results = []
async for result in handler.generate(request, mock_context):
results.append(result)

# Verify image_path was passed to the generator
call_args = handler.generator.generate.call_args
sampling_params = call_args[1]["sampling_params_kwargs"]
assert sampling_params["image_path"] == input_ref
sampling_params = handler.generator.generate.call_args[1][
"sampling_params_kwargs"
]
assert sampling_params["image_path"] == str(input_ref.resolve())

@pytest.mark.asyncio
async def test_generate_i2i_rejects_path_outside_allowed_dir(
self, handler, mock_context, tmp_path, monkeypatch
):
"""A path outside DYN_MM_LOCAL_PATH is rejected before the generator runs."""
Comment thread
dmitry-tokarev-nv marked this conversation as resolved.
Resolved
handler.generator.generate = Mock()
monkeypatch.setenv("DYN_MM_LOCAL_PATH", str(tmp_path))
request = {
"prompt": "x",
"model": "test-model",
"size": "256x256",
"response_format": "b64_json",
"input_reference": "/etc/passwd",
}

results = [r async for r in handler.generate(request, mock_context)]

assert any("error" in r for r in results)
handler.generator.generate.assert_not_called()

@pytest.mark.asyncio
async def test_generate_t2i_no_image_path(self, handler, mock_context):
Expand Down
Loading