fix(image-gen/video-gen): guard local provider inputs against credential reads via one shared chokepoint (salvages #57698, consolidates #57695) - #57726
Merged
Conversation
… chokepoint + cover xai (NousResearch#57698) Follow-up to the per-provider guards. Three improvements from review: 1. Extract agent.file_safety.raise_if_read_blocked() as a single shared chokepoint and route the OpenAI, OpenRouter, and (newly) xAI image providers through it, replacing the 3x-duplicated inline try/except. Fixes the whole bug class: xai/_xai_image_field read a model-supplied local path via open() with no guard — the same vulnerability the PR fixed for OpenAI/OpenRouter, in a sibling provider it missed. 2. Strengthen the regression tests from pass-on-any-ValueError to true security invariants: spy open()/read_bytes() and assert the blocked credential is NEVER read; add negative controls (legit local image still loads; remote/data: URIs pass through unguarded) so a block-everything regression can't pass. 3. Guard is best-effort by design (defense-in-depth, not a security boundary) — documented on the shared helper. - agent/file_safety.py: raise_if_read_blocked() - plugins/image_gen/{openai,openrouter,xai}: route through helper - tests: no-read spies + negative controls across all three providers
kshitijk4poor
enabled auto-merge (rebase)
July 3, 2026 13:06
kshitijk4poor
disabled auto-merge
July 3, 2026 13:09
Fold the xAI video credential-read guard into the same shared agent.file_safety.raise_if_read_blocked chokepoint this PR introduces for the image providers, so the whole image+video bug class is covered by one enforced boundary. Consolidates the parallel salvage of NousResearch#57695 (xAI image+video) into this PR; NousResearch#57727 is now redundant and will be closed. - video_gen/xai: guard _image_ref_to_xai_url and _video_ref_to_xai_url (the video image + video byte-read chokepoints) via the shared helper. - Regression tests: symlinked auth.json with .png/.mp4 names are blocked across both video read paths (mutation-checked).
kshitijk4poor
enabled auto-merge (rebase)
July 3, 2026 13:12
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Image and video generation providers read model/tool-supplied local
image_url/reference_image_urls(and video) paths and base64-inline them into the outbound provider request without Hermes'get_read_block_error()read guard. A path to a Hermes credential store (auth.json,.anthropic_oauth.json,.env, …) — including via aleak.png -> auth.jsonsymlink — could be read and embedded in the payload. The OpenAI Codex image provider already guards this; this PR mirrors that boundary to every image + video provider through one shared chokepoint.Salvages #57698 (@necoweb3, openai+openrouter image) and consolidates #57695 (@necoweb3, xAI image+video) — cherry-picked to preserve authorship, extended to the whole bug class, verified end-to-end.
Changes
agent/file_safety.py: new sharedraise_if_read_blocked(path)chokepoint (co-located withget_read_block_error). Best-effort/defense-in-depth: a real block raisesValueError; unexpected internal errors fail open.plugins/image_gen/openai,openrouter,xai: route local-input reads through the shared helper (after thehttp/https/data:early-returns — remote URLs/data URIs untouched).plugins/video_gen/xai: guard_image_ref_to_xai_url+_video_ref_to_xai_url(video image + video byte-reads) through the same shared helper.ValueErrorto real security invariants: no-read spies (blocked credential never read into memory), negative controls (legit local image loads; remote/data URIs pass through), and symlink→auth.jsonregression tests for image + video paths.Validation
.png/.mp4symlink →auth.json(any provider)ValueError: Access denied— never readmain;get_read_block_error.resolve()s symlinks and matches the profile-aware credential denylist.agent/file_safety.pyhelper they all call (the helper is the whole point of the consolidation; it lives in core so every provider shares one auditable boundary).Closes #57698. Closes #57695.