Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
OpenDAS
dynamo
Commits
56f02422
Unverified
Commit
56f02422
authored
Apr 14, 2026
by
Graham King
Committed by
GitHub
Apr 14, 2026
Browse files
fix: Reject local file inputs in ImageLoader (#8158)
Signed-off-by:
Graham King
<
grahamk@nvidia.com
>
parent
597b7249
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
36 additions
and
42 deletions
+36
-42
components/src/dynamo/common/multimodal/image_loader.py
components/src/dynamo/common/multimodal/image_loader.py
+12
-31
components/src/dynamo/common/tests/multimodal/test_image_loader.py
...s/src/dynamo/common/tests/multimodal/test_image_loader.py
+15
-3
examples/backends/vllm/launch/agg_omni_i2v.sh
examples/backends/vllm/launch/agg_omni_i2v.sh
+3
-1
tests/serve/test_vllm_omni.py
tests/serve/test_vllm_omni.py
+6
-7
No files found.
components/src/dynamo/common/multimodal/image_loader.py
View file @
56f02422
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import
asyncio
import
base64
...
...
@@ -142,6 +130,10 @@ class ImageLoader:
@
_nvtx
.
annotate
(
"mm:img:load_image"
,
color
=
"lime"
)
async
def
load_image
(
self
,
image_url
:
str
)
->
Image
.
Image
:
parsed_url
=
urlparse
(
image_url
)
if
parsed_url
.
scheme
in
(
""
,
"file"
):
raise
ValueError
(
"Invalid image source scheme: local file access is not allowed"
)
if
parsed_url
.
scheme
in
(
"http"
,
"https"
):
key
=
image_url
.
lower
()
...
...
@@ -164,8 +156,8 @@ class ImageLoader:
# shield so cancelling THIS caller doesn't cancel the shared task
return
await
asyncio
.
shield
(
self
.
_inflight
[
key
])
try
:
if
parsed_url
.
scheme
==
"data"
:
try
:
with
_nvtx
.
annotate
(
"mm:img:base64_decode"
,
color
=
"lime"
):
if
not
parsed_url
.
path
.
startswith
(
"image/"
):
raise
ValueError
(
"Data URL must be an image type"
)
...
...
@@ -179,24 +171,13 @@ class ImageLoader:
except
binascii
.
Error
as
e
:
raise
ValueError
(
f
"Invalid base64 encoding:
{
e
}
"
)
from
e
image_data
=
BytesIO
(
image_bytes
)
elif
parsed_url
.
scheme
in
(
""
,
"file"
):
path
=
image_url
if
parsed_url
.
scheme
==
""
else
parsed_url
.
path
def
_read_local_file
(
p
:
str
)
->
bytes
:
with
open
(
p
,
"rb"
)
as
f
:
return
f
.
read
()
image_bytes
=
await
asyncio
.
to_thread
(
_read_local_file
,
path
)
image_data
=
BytesIO
(
image_bytes
)
else
:
raise
ValueError
(
f
"Invalid image source scheme:
{
parsed_url
.
scheme
}
"
)
return
await
self
.
_open_image
(
image_data
)
except
Exception
as
e
:
logger
.
error
(
f
"
{
type
(
e
).
__name__
}
loading image: '
{
image_url
}
':
{
e
}
"
)
raise
ValueError
(
f
"Failed to load image: '
{
image_url
}
':
{
e
}
"
)
from
e
logger
.
error
(
f
"
{
type
(
e
).
__name__
}
decoding image: '
{
image_url
}
':
{
e
}
"
)
raise
ValueError
(
f
"Failed to decoding image: '
{
image_url
}
':
{
e
}
"
)
from
e
# It's not file:, http:, https:, or data:
raise
ValueError
(
f
"Invalid image source scheme:
{
parsed_url
.
scheme
}
"
)
async
def
load_image_batch
(
self
,
...
...
components/src/dynamo/common/tests/multimodal/test_image_loader.py
View file @
56f02422
...
...
@@ -176,12 +176,24 @@ async def test_retry_after_failure(loader: ImageLoader) -> None:
# --- Error contract preserved for non-HTTP ---
async
def
test_file_
not_found_normaliz
ed
(
loader
:
ImageLoader
)
->
None
:
"""file://
path that doesn't exist should raise ValueError, not FileNotFoundError
."""
with
pytest
.
raises
(
ValueError
,
match
=
"
Failed to load imag
e"
):
async
def
test_file_
url_is_reject
ed
(
loader
:
ImageLoader
)
->
None
:
"""file://
inputs should be rejected before any local file read is attempted
."""
with
pytest
.
raises
(
ValueError
,
match
=
"
Invalid image source schem
e"
):
await
loader
.
load_image
(
"file:///nonexistent/path/img.png"
)
@
pytest
.
mark
.
parametrize
(
"url_factory"
,
[
lambda
p
:
p
.
as_uri
(),
lambda
p
:
str
(
p
)])
async
def
test_local_file_inputs_are_rejected
(
loader
:
ImageLoader
,
tmp_path
,
url_factory
)
->
None
:
"""Local filesystem image inputs must be rejected for both file:// and bare paths."""
image_path
=
tmp_path
/
"secret.png"
Image
.
new
(
"RGB"
,
(
1
,
1
),
color
=
"red"
).
save
(
image_path
,
format
=
"PNG"
)
with
pytest
.
raises
(
ValueError
,
match
=
"Invalid image source scheme"
):
await
loader
.
load_image
(
url_factory
(
image_path
))
async
def
test_data_url_invalid_base64_normalized
(
loader
:
ImageLoader
)
->
None
:
"""Malformed base64 data URL should raise ValueError."""
with
pytest
.
raises
(
ValueError
,
match
=
"Invalid base64"
):
...
...
examples/backends/vllm/launch/agg_omni_i2v.sh
View file @
56f02422
...
...
@@ -17,6 +17,8 @@ SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
source
"
$SCRIPT_DIR
/../../../common/launch_utils.sh"
MODEL
=
"Wan-AI/Wan2.2-TI2V-5B-Diffusers"
# Not a valid PNG, example only
INPUT_REFERENCE_DATA_URL
=
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+aX3kAAAAASUVORK5CYII="
# Parse command line arguments
EXTRA_ARGS
=()
...
...
@@ -45,7 +47,7 @@ curl -s http://localhost:${HTTP_PORT}/v1/videos \\
-d '{
"model": "
${
MODEL
}
",
"prompt": "A bear sleeping",
"input_reference": "
/tmp/input.png
",
"input_reference": "
${
INPUT_REFERENCE_DATA_URL
}
",
"size": "832x480",
"response_format": "url",
"nvext": {
...
...
tests/serve/test_vllm_omni.py
View file @
56f02422
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
import
base64
import
dataclasses
import
logging
import
os
import
tempfile
from
dataclasses
import
dataclass
,
field
from
io
import
BytesIO
from
typing
import
Any
import
pytest
...
...
@@ -91,15 +92,13 @@ class VideoGenerationPayload(BasePayload):
class
I2VPayload
(
VideoGenerationPayload
):
"""Payload for image-to-video via /v1/videos with input_reference."""
_tmp_dir
:
Any
=
field
(
default
=
None
,
init
=
False
,
repr
=
False
,
compare
=
False
)
def
__post_init__
(
self
):
from
PIL
import
Image
self
.
_tmp_dir
=
tempfile
.
TemporaryDirectory
()
path
=
os
.
path
.
join
(
self
.
_tmp_dir
.
name
,
"input.png
"
)
I
mage
.
new
(
"RGB"
,
(
64
,
64
),
color
=
"red"
).
save
(
path
)
self
.
body
[
"input_reference"
]
=
path
image_buffer
=
BytesIO
()
Image
.
new
(
"RGB"
,
(
64
,
64
),
color
=
"red"
).
save
(
image_buffer
,
format
=
"PNG
"
)
i
mage
_b64
=
base64
.
b64encode
(
image_buffer
.
getvalue
()).
decode
(
"ascii"
)
self
.
body
[
"input_reference"
]
=
f
"data:image/png;base64,
{
image_b64
}
"
@
dataclass
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment