fix(platforms): block image upload redirects to private URLs - #62932
fix(platforms): block image upload redirects to private URLs#62932necoweb3 wants to merge 1 commit into
Conversation
Related to the open SSRF redirect-guard cluster in the platform image-upload paths: #24831 (Mattermost media redirects), #43938 (Yuanbao |
teknium1
left a comment
There was a problem hiding this comment.
Thanks for addressing a real redirect-based SSRF gap. Current main still follows redirects after only an initial URL preflight in Slack batch uploads (plugins/platforms/slack/adapter.py:1751-1779) and Discord URL-image downloads (plugins/platforms/discord/adapter.py:2534-2543).
Problems
- The new Discord helper converts aiohttp's case-insensitive headers to
dictand then checks onlyheaders.get("location")(plugins/platforms/discord/adapter.py:150in this PR). A normalLocationheader can be missed, so safe redirects fail as HTTP 302 rather than being followed. - Discord GIF uploads still preflight only their initial URL and then use redirect-following
session.get(animation_url)(plugins/platforms/discord/adapter.py:3748); this sibling SSRF path is unchanged. - The new tests exercise only
send_multiple_images, although this PR also changessend_image; neithersend_imagenorsend_animationgets direct coverage.AGENTS.md:84-87also requires real-path validation for security-sensitive network I/O.
Suggested changes
- Preserve case-insensitive response-header lookup or normalize keys, and test both public
Locationredirects and private targets. - Reuse the guard for
send_animation, then add directsend_imageand animation regressions.
Automated hermes-sweeper review.
| async with session.get( | ||
| current_url, | ||
| timeout=timeout, | ||
| allow_redirects=False, |
There was a problem hiding this comment.
aiohttp headers are case-insensitive, but converting them to dict makes this subsequent headers.get("location") lookup case-sensitive. Preserve the original header mapping or normalize keys; otherwise normal Location headers can make safe redirects fail as HTTP 302.
5ec182d to
90ab530
Compare
|
Updated the branch to address the review feedback. Changes:
Validation: |
|
Merged via #69482 — your commit was cherry-picked/reapplied onto current main with your authorship preserved in git history: your SSRF redirect guard was cherry-picked — redirect re-validation on every hop. Thanks for the contribution! |
Summary
This closes redirect-based SSRF gaps in Slack and Discord outbound image upload paths.
Slack and Discord already preflight remote image URLs with
is_safe_url(), but the platform upload paths then allowed the HTTP client to follow redirects without revalidating the final target. A public URL could therefore redirect to a private/internal address and have Hermes fetch those bytes before uploading them back to Slack or Discord.Why
These paths run on behalf of messaging-platform output and perform host-side network fetches:
send_multiple_images()downloads URL images withhttpx.AsyncClient(follow_redirects=True)beforefiles_upload_v2.send_image()andsend_multiple_images()download URL images with aiohttp before sending attachments.Without redirect revalidation, the initial public URL check does not protect the final fetch target.
Changes
_ssrf_redirect_guard.Tests