Skip to content

[BugFix] security: restrict untrusted image decoding formats - #9114

Merged
hsliuustc0106 merged 5 commits into
vllm-project:mainfrom
NickCao:codex/pillow-image-allowlist
Oct 23, 2026
Merged

hsliuustc0106 merged 5 commits into
vllm-project:mainfrom
NickCao:codex/pillow-image-allowlist

Conversation

@NickCao

@NickCao NickCao commented Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator

Contributor intent (required human answers)

All four questions must be answered by the human contributor before submission.
An agent may reuse explicit human answers with a public-safe source reference;
it must not invent, infer, or prefill missing answers. If an answer is missing
or materially ambiguous, ask the human and wait. Do not publish private
conversation content without authorization.

1. What outcome do you want to achieve?

Reduce the risk that crafted images trigger unsafe decoder behavior in publicly accessible vLLM-Omni services.

2. What is the highest-leverage variable for that outcome?

Whether untrusted image bytes reach Pillow through unrestricted plugin selection.

3. How will this work move that variable?

Route public image decoding through vLLM’s open_image, which restricts Pillow’s plugin choices at open time.

4. Whose customer or user needs does this address, and what are those needs?

Operators of public omni services need to reduce exposure to malicious uploads.

Purpose

Route public image and video ingress through the vLLM audited Pillow format allowlist.

Although listed by MiniMax H3 validation, HEIC/HEIF were not supported in a clean install because the project does not include a HEIF decoder dependency.

Test Plan

vLLM Version: 0.32.0

vLLM-Omni Commit: ce024e0

pytest -q \
  tests/entrypoints/openai_api/test_image_server.py \
  tests/entrypoints/openai_api/test_serving_chat_multistage_generation.py \
  tests/entrypoints/openai_api/test_video_api_utils.py \
  tests/entrypoints/openai_api/test_video_server.py \
  tests/entrypoints/openai_api/test_video_frame_filter.py \
  tests/entrypoints/openai_api/test_serving_video_stream.py \
  tests/model_executor/models/aura_omni/duplex/test_aura_plugin_contract.py \
  tests/model_executor/models/joyai_vl_interaction/test_duplex_plugin.py \
  tests/model_executor/models/minicpmo_4_5/duplex/test_input.py \
  tests/model_executor/models/qwen3_omni/test_duplex_plugin.py \
  tests/diffusion/models/minimax_h3/test_minimax_h3_contract.py \
  tests/entrypoints/openai_api/test_untrusted_image.py
pytest -s -v tests/e2e/online_serving/test_minicpmo_4_5.py::test_image_to_text_audio_001
pytest -s -v tests/e2e/online_serving/test_boogu_image_edit.py::test_images_edits_endpoint_001 -m "slow and diffusion and H100"
pytest -s -v tests/e2e/online_serving/test_lingbot_video.py::test_video_generation_modes_001 \
  -m "advanced_model and diffusion" --run-level=advanced_model

Test Result

passed

BEFORE SUBMITTING: read CONTRIBUTING.md and run the precheck-pr skill with the code agent for a self-check against project conventions.

Note

To avoid agent spam, vLLM-Omni limits contributors without write access to 5 open PRs. If this blocks well-intentioned critical work, please contact a committer to request bypass list access.
(anything written below this line will be removed by GitHub Actions)

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ⚠️ Failed 2026-10-19T14:44:48.753796Z 4791631 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@amy-why-3459 amy-why-3459 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for routing these through the vLLM allowlist, the call-site swap itself looks correct (open_image is a thin Image.open(fp, formats=ALLOWED_IMAGE_FORMATS), and denied formats still surface as UnidentifiedImageError/OSError, so the existing except clauses keep working).

A few things worth addressing before merge:

  1. Ascend image compatibility: docker/Dockerfile.npu still builds on vllm-ascend:v0.31.0, and vllm/multimodal/image.py in vLLM v0.31.0 has no open_image (it was added in v0.32.0). If that image still ships vLLM 0.31, the new module-level imports in serving_chat.py, images/helpers.py, video_api_utils.py and the duplex plugins would fail at import time and the NPU server would not start. Could you confirm the NPU image, or add a small local fallback (Image.open(fp, formats=...) with the same allowlist) until the Ascend base moves to 0.32?
  2. MiniMax H3 HEIC/HEIF: see inline. The allowlist has no HEIF, so these formats can no longer be decoded even with pillow-heif installed, but they are still advertised.
  3. Tests: none of the listed tests exercises a denied format. A small parametrized case that feeds an EPS header (b"%!PS-Adobe-3.0 EPSF-3.0...") into _decode_image_bytes, _load_input_images and the H3 upload validator, and asserts a 400 / UnidentifiedImageError, would lock in the security property so a future Image.open regression gets caught.

For reference, the remaining Image.open call sites I checked look fine to leave as-is: the diffusion pipelines only open str/path inputs from offline callers, and experimental/fullduplex/video_stacking.py is client-side (used by clients/duplex.py and the demo).

@amy-why-3459 amy-why-3459 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inline notes for points 1 and 2 above.

@vllm-omni-review-bot

Copy link
Copy Markdown

This PR appears to belong to: docs/design/module/ar_runtime.md, docs/design/module/observability.md, docs/design/module/model_integration.md.

Module owners: @fake0fan @Gaohan123 @gcanlin

Routing: @fake0fan via module of the changed files; @Gaohan123 via module of the changed files; @gcanlin via module of the changed files

@NickCao, please review your own changes and leave a short self-review comment describing what you checked. PRs without author self-review may not be assigned a reviewer.

Please take a look when you have a chance. If you would like an automated review, mention @vllm-omni-review-bot in a comment.

@vllm-omni-review-bot

vllm-omni-review-bot commented Oct 19, 2026 •

Copy link
Copy Markdown

Omni ReviewBot triage note

Automated triage of commit ce024e07931d produced:

  • Priority: high. Prompt maintainer attention is suggested.

These are automated triage suggestions only — the final decision belongs to the maintainers.

@NickCao
NickCao requested a review from yenuo26 as a code owner October 19, 2026 15:24
@NickCao

NickCao commented Oct 19, 2026

Copy link
Copy Markdown
Collaborator Author

Also added some regression test for the denial case.

@amy-why-3459 amy-why-3459 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the follow-up. Looked at the two new commits (8a2934b, e3c212e): the HEIC/HEIF cleanup in both MINIMAX_H3_REFERENCE_IMAGE_FORMATS constants and error messages looks right, and the new test_untrusted_image.py is a good regression guard. Patching EpsImageFile._open to raise proves the EPS decoder is never reached, not just that some error came back, and since Pillow re-raises non-syntax exceptions from plugin _open, a future bare Image.open would fail these tests loudly.

A few remaining items:

  1. Docs still advertise HEIC/HEIF (outside the diff, so noting here): docs/serving/videos_api.md ("Images use JPEG/PNG/WEBP/HEIC/HEIF (30 MiB)") and recipes/MiniMaxAI/MiniMax-H3.md ("Reference images accept JPG/JPEG, PNG, WEBP, HEIC, or HEIF up to 30 MiB"). Worth updating them in this PR so the docs match the new behavior.
  2. open_image on the Ascend image: this push doesn't touch the serving_chat.py import thread, so the vllm-ascend v0.31.0 question from the earlier review is still open. Just flagging it so it doesn't get lost before merge, since the import now sits at module level in the OpenAI entrypoints as well as the duplex plugins.
  3. Minor, see inline: HEIC/HEIF uploads now get the generic "Invalid uploaded image reference" error instead of the format list.

@NickCao
NickCao requested a review from david6666666 as a code owner October 19, 2026 16:32

@amy-why-3459 amy-why-3459 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked d7d5342f and 9f5581ee. The docs and the H3 recipe no longer advertise HEIC/HEIF, and a failed decode now returns the supported-format list, with a test for the .heic case. Thanks. The one item from my earlier review that's still open is the module-level from vllm.multimodal.image import open_image (in helpers.py and serving_chat.py). docker/Dockerfile.npu is still on vllm-ascend:v0.31.0 at this head, so that import path depends on the NPU base moving to vLLM 0.32 first.

@hsliuustc0106 hsliuustc0106 added the high priority high priority issue, needs to be done asap label Oct 20, 2026 — with ChatGPT Codex Connector
NickCao and others added 5 commits October 20, 2026 08:24
Route public image and video ingress through the vLLM audited Pillow format allowlist.

Although listed by MiniMax H3 validation, HEIC/HEIF were not supported in a clean install because the project does not include a HEIF decoder dependency.

Co-authored-by: Codex <noreply@openai.com>
Signed-off-by: Nick Cao <ncao@redhat.com>
HEIC and HEIF are excluded by vLLM's image decoder allowlist, so remove them from H3's accepted format sets and error text. Keep their suffixes classified as images so uploads use image validation and size limits.

Co-authored-by: Codex <noreply@openai.com>
Signed-off-by: Nick Cao <ncao@redhat.com>
Cover EPS rejection at the public image, data URL, and MiniMax H3 upload paths. Assert that rejected image formats never reach the EPS decoder and that MiniMax H3 error messages list only supported formats.

Co-authored-by: Codex <noreply@openai.com>
Signed-off-by: Nick Cao <ncao@redhat.com>
Report the accepted formats for MiniMax H3 image decode failures and update the serving docs to match the decoder allowlist. HEIC/HEIF decoding was not supported in this path because no decoder dependency is installed.

Co-authored-by: Codex <noreply@openai.com>
Signed-off-by: Nick Cao <ncao@redhat.com>
Assert that image decode failures return the supported MiniMax H3 reference formats, including uploads whose names use an unsupported image suffix.

Co-authored-by: Codex <noreply@openai.com>
Signed-off-by: Nick Cao <ncao@redhat.com>
@NickCao
NickCao force-pushed the codex/pillow-image-allowlist branch from 9f5581e to ce024e0 Compare October 20, 2026 12:24
@NickCao NickCao added the ready label to trigger buildkite CI label Oct 20, 2026
@NickCao

NickCao commented Oct 20, 2026

Copy link
Copy Markdown
Collaborator Author

Checked d7d5342f and 9f5581ee. The docs and the H3 recipe no longer advertise HEIC/HEIF, and a failed decode now returns the supported-format list, with a test for the .heic case. Thanks. The one item from my earlier review that's still open is the module-level from vllm.multimodal.image import open_image (in helpers.py and serving_chat.py). docker/Dockerfile.npu is still on vllm-ascend:v0.31.0 at this head, so that import path depends on the NPU base moving to vLLM 0.32 first.

NPU updated in #9143

@hsliuustc0106 hsliuustc0106 added the bug Something isn't working label Oct 21, 2026

@hsliuustc0106 hsliuustc0106 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@NickCao

  • Please attach execution evidence for the new EPS-denial regressions at ce024e07931ddf683fcf086a3d9380865a8ab0a9. The Test Plan currently pins 47916310 and does not list test_untrusted_image.py. Please include the command (pytest -q tests/entrypoints/openai_api/test_untrusted_image.py), Python/vLLM/Pillow versions and actual result, or link the existing job/log if these tests already ran. For the claimed security property, show the same three EPS cases fail with the unrestricted baseline decoder and pass with the reviewed allowlist path; identify the baseline revision or controlled revert used. Acceptance: the EPS decoder is not entered on the reviewed head, and the negative control catches restoring unrestricted decoding.

The regression code and public-ingress call-site changes have been inspected statically; no tests were executed here. Current CUDA is green, but its aggregate status does not identify whether these named tests ran. This is a specific verification gap, not a newly demonstrated code defect.

@NickCao

NickCao commented Oct 22, 2026

Copy link
Copy Markdown
Collaborator Author

@NickCao

* [ ]  Please attach execution evidence for the new [EPS-denial regressions](https://github.com/vllm-project/vllm-omni/blob/ce024e07931ddf683fcf086a3d9380865a8ab0a9/tests/entrypoints/openai_api/test_untrusted_image.py#L31-L58) at `ce024e07931ddf683fcf086a3d9380865a8ab0a9`. The Test Plan currently pins `47916310` and does not list `test_untrusted_image.py`. Please include the command (`pytest -q tests/entrypoints/openai_api/test_untrusted_image.py`), Python/vLLM/Pillow versions and actual result, or link the existing job/log if these tests already ran. For the claimed security property, show the same three EPS cases fail with the unrestricted baseline decoder and pass with the reviewed allowlist path; identify the baseline revision or controlled revert used. Acceptance: the EPS decoder is not entered on the reviewed head, and the negative control catches restoring unrestricted decoding.

The regression code and public-ingress call-site changes have been inspected statically; no tests were executed here. Current CUDA is green, but its aggregate status does not identify whether these named tests ran. This is a specific verification gap, not a newly demonstrated code defect.

Updated and retested.

Negative control:

git revert --no-commit b7815b0d2
pytest -q tests/entrypoints/openai_api/test_untrusted_image.py

Failing as expected.

@linyueqian linyueqian left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed ce024e0. Every Image.open on a request path under vllm_omni/ now routes through open_image, covering the OpenAI image, chat, and video entrypoints, the streaming video handler, the MiniMax-H3 upload and preprocessing paths, and the Qwen3-Omni, MiniCPM-o 4.5, JoyAI, and Aura duplex plugins. Current main (88c9ccd) has no other raw Image.open on those paths; the remaining calls are in diffusion pipelines that open str/path inputs from offline callers, the benchmarks, and the client-side video_stacking.py.

Existing except clauses still catch the rejection because a disallowed format raises UnidentifiedImageError, which is an OSError. Patching EpsImageFile._open to fail in the new EPS tests proves the decoder is never entered, rather than only checking that an error was returned. The earlier NPU concern is resolved on main because docker/Dockerfile.npu moved to vllm-ascend v0.32.0 in #9143, which includes open_image.

Static review only; I did not run the tests locally. Main CUDA (build 17678) and the NPU lane pass at ce024e0.

@hsliuustc0106
hsliuustc0106 merged commit ea719bc into vllm-project:main Oct 23, 2026
9 of 11 checks passed
github-actions Bot added a commit to shantanusharma/recipes that referenced this pull request Oct 23, 2026
vllm-project/vllm-omni@main:recipes 17bd19f3d775 → cbe4cc4b7c11 [latest.vllm-omni]
vllm-project/vllm-omni@17bd19f...cbe4cc4

Commits:
- vllm-project/vllm-omni@45d7c0051ec6 [Feat] Add Cosmos3-Nano Pipeline Parallel support (vllm-project/vllm-omni#4637)
- vllm-project/vllm-omni@ea719bc6b99b [BugFix] security: restrict untrusted image decoding formats (vllm-project/vllm-omni#9114)

Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
github-actions Bot added a commit to vllm-project/recipes that referenced this pull request Oct 23, 2026
vllm-project/vllm-omni@main:recipes c829af446829 → 0131ae1e7330 [latest.vllm-omni]
vllm-project/vllm-omni@c829af4...0131ae1

Commits:
- vllm-project/vllm-omni@45d7c0051ec6 [Feat] Add Cosmos3-Nano Pipeline Parallel support (vllm-project/vllm-omni#4637)
- vllm-project/vllm-omni@ea719bc6b99b [BugFix] security: restrict untrusted image decoding formats (vllm-project/vllm-omni#9114)

Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
github-actions Bot added a commit to CuiZ2007/recipes that referenced this pull request Oct 23, 2026
…mni@78610269b446

vllm-project/vllm@main:recipes c3117c7441dd → 37cf71255259 [latest.vllm]
vllm-project/vllm@c3117c7...37cf712

Commits:
- vllm-project/vllm@74ba6384c2f0 [Doc][ROCm] Document DeepSeek-V4.1-Flash MI355X AgentX TP2 profiles (vllm-project/vllm#63344)
- vllm-project/vllm@ecd1e12553e6 [Recipe] MiniMax-M3: add B200 NVFP4 AgentX aggregated profile (vllm-project/vllm#63163)

Recipes:
- MiniMaxAI/MiniMax-M3 (modified): https://recipes.vllm.ai/MiniMaxAI/MiniMax-M3
- deepseek-ai/DeepSeek-V4.1-Flash (modified): https://recipes.vllm.ai/deepseek-ai/DeepSeek-V4.1-Flash

vllm-project/vllm-omni@main:recipes c829af446829 → 78610269b446 [latest.vllm-omni]
vllm-project/vllm-omni@c829af4...7861026

Commits:
- vllm-project/vllm-omni@45d7c0051ec6 [Feat] Add Cosmos3-Nano Pipeline Parallel support (vllm-project/vllm-omni#4637)
- vllm-project/vllm-omni@ea719bc6b99b [BugFix] security: restrict untrusted image decoding formats (vllm-project/vllm-omni#9114)

Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
tzhouam pushed a commit to tzhouam/vllm-omni that referenced this pull request Oct 25, 2026
…oject#9114)

Signed-off-by: Nick Cao <ncao@redhat.com>
Co-authored-by: Codex <noreply@openai.com>
(cherry picked from commit ea719bc)
Signed-off-by: tzhouam <tzhouam@connect.ust.hk>
hsliuustc0106 pushed a commit that referenced this pull request Oct 25, 2026
 #9339 (#9352)

Signed-off-by: Chendi Xue <chendi.xue@intel.com>
Signed-off-by: tzhouam <tzhouam@connect.ust.hk>
Signed-off-by: amy-why-3459 <wuhaiyan17@huawei.com>
Signed-off-by: Nick Cao <ncao@redhat.com>
Signed-off-by: Cao Xuanning <2107254305@qq.com>
Signed-off-by: khairulkabir1661 <khairulkabir1661@users.noreply.github.com>
Signed-off-by: lm10-arg <347490427+lm10-arg@users.noreply.github.com>
Co-authored-by: Chendi.Xue <chendi.xue@intel.com>
Co-authored-by: amy-why-3459 <wuhaiyan17@huawei.com>
Co-authored-by: Nick Cao <ncao@redhat.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Cao Xuanning <109304380+advanced233@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Khairul Kabir <250204754+khairulkabir1661@users.noreply.github.com>
Co-authored-by: khairulkabir1661 <khairulkabir1661@users.noreply.github.com>
Co-authored-by: lm10-arg <linluo@icloud.com>
Co-authored-by: lm10-arg <347490427+lm10-arg@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working high priority high priority issue, needs to be done asap ready label to trigger buildkite CI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants