fix(ssrf): close audio_url gap + redirect-based bypass across all URL tools - #8
fix(ssrf): close audio_url gap + redirect-based bypass across all URL tools#8Deesmo wants to merge 2 commits into
Conversation
transcribe-audio fetched the user-supplied audio_url directly without the validateUrl() SSRF guard that every other URL-fetching tool uses (web-scrape, extract-page, extract-pdf, ocr-extract, image-remove-bg, url-health-check, webhook-send, etc.). This allowed requests to internal/private addresses (cloud metadata, localhost, RFC1918), violating the documented 'SSRF protection on all web-facing tools' guarantee. Adds the same validateUrl() check used by sibling tools. Co-authored-by: Deesmo <Deesmo@users.noreply.github.com>
There was a problem hiding this comment.
Code Review
This pull request introduces URL validation for the audio_url parameter in the /transcribe-audio endpoint, returning a 400 Bad Request error if the validation fails. There are no review comments, so I have no feedback to provide.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
validateUrl() only guarded the caller-supplied URL, but fetch/axios follow redirects by default — so a public host could 30x-redirect to an internal address (cloud metadata 169.254.169.254, localhost, RFC1918) and bypass the check. Add centralized safeFetch()/safeAxiosGet() helpers in lib/ssrf.ts that follow redirects manually and re-validate every hop (capped at 5). Route every user-URL tool through them: web-scrape, extract-page, extract-metadata, extract-pdf, rss-parse, ocr-extract, browser-task, screenshot-capture, html-to-markdown, image-remove-bg, transcribe-audio, url-health-check. webhook-send already used maxRedirects:0 (reports, never follows) so it was already safe. Verified e2e: a public redirector pointing to 169.254.169.254 / 127.0.0.1 is now blocked, while normal public redirects still resolve. Co-authored-by: Deesmo <Deesmo@users.noreply.github.com>
|
Superseded — verified on current main: api/src/lib/ssrf.ts now does per-hop redirect re-validation + size caps + IPv6/CGNAT blocking, and every URL tool call site uses safeAxiosGet/validateUrl+safeFetch (incl. transcribe-audio audio_url via #36). Closing; the audio_url gap + redirect bypass this PR targeted are covered. Reopen if a specific call site is found still unprotected. |
Summary
Closes two SSRF holes in the tool API:
transcribe-audiohad no SSRF check at all — it fetched the user-suppliedaudio_urldirectly, unlike every other URL-fetching tool. Added the missingvalidateUrl()guard.validateUrl()only checked the caller-supplied URL, butfetch/axiosfollow redirects by default. A public host could 30x-redirect to an internal address (cloud metadata169.254.169.254,localhost, RFC1918, Render-internal100.64/10) and slip past the check.Impact
Server-side request forgery: a caller could make the server reach internal-only endpoints — most dangerously cloud instance-metadata credentials. Directly contradicts the documented guarantee "SSRF protection on all web-facing tools" (README / SECURITY).
Fix
api/src/lib/ssrf.ts:safeFetch(url, init)andsafeAxiosGet(url, config)follow redirects manually, re-validating every hop withvalidateUrl(), capped at 5 redirects.safeAxiosGetdisables axios's own redirect following and uses a permissivevalidateStatusonly to inspect 3xx; it still throws on 4xx/5xx so callers' existing error handling is unchanged.web-scrape,extract-page,extract-metadata,extract-pdf,rss-parse,ocr-extract,browser-task,screenshot-capture,html-to-markdown,image-remove-bg,transcribe-audio,url-health-check.webhook-sendalready usedmaxRedirects: 0(reports the redirect, never follows) — already safe, left as-is.Legitimate public redirects (e.g.
http→https, CDN/shortlink hops) still resolve normally.Verification
tsc --noEmitclean for changed files (remaining repo errors are pre-existing uninstalled optional deps in the sandbox:viem,posthog-node,@coinbase/cdp-sdk/*,@x402/*).169.254.169.254: blocked ✅127.0.0.1: blocked ✅Note (not changed here)
The committed Prisma client in
node_modulesis stale and produced false-positive type errors untilprisma generatewas re-run; the build script already runsprisma generate && tsc, so it's not a runtime issue. Left untouched to keep this change isolated.