Skip to content

fix(ssrf): close audio_url gap + redirect-based bypass across all URL tools - #8

Closed
Deesmo wants to merge 2 commits into
mainfrom
cursor/fix-transcribe-audio-ssrf-f6bf
Closed

fix(ssrf): close audio_url gap + redirect-based bypass across all URL tools#8
Deesmo wants to merge 2 commits into
mainfrom
cursor/fix-transcribe-audio-ssrf-f6bf

Conversation

@Deesmo

@Deesmo Deesmo commented Jun 25, 2026

Copy link
Copy Markdown
Owner

Summary

Closes two SSRF holes in the tool API:

  1. transcribe-audio had no SSRF check at all — it fetched the user-supplied audio_url directly, unlike every other URL-fetching tool. Added the missing validateUrl() guard.
  2. Redirect-based bypass affected every URL toolvalidateUrl() only checked the caller-supplied URL, but fetch/axios follow redirects by default. A public host could 30x-redirect to an internal address (cloud metadata 169.254.169.254, localhost, RFC1918, Render-internal 100.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

  • New centralized helpers in api/src/lib/ssrf.ts:
    • safeFetch(url, init) and safeAxiosGet(url, config) follow redirects manually, re-validating every hop with validateUrl(), capped at 5 redirects.
    • safeAxiosGet disables axios's own redirect following and uses a permissive validateStatus only to inspect 3xx; it still throws on 4xx/5xx so callers' existing error handling is unchanged.
  • Routed 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 the redirect, never follows) — already safe, left as-is.
  • Fixed-host API calls (rdap.org, ip-api.com, CoinGecko, etc.) are not SSRF vectors (caller can't choose the host) and were left untouched.

Legitimate public redirects (e.g. httphttps, CDN/shortlink hops) still resolve normally.

Verification

  • tsc --noEmit clean for changed files (remaining repo errors are pre-existing uninstalled optional deps in the sandbox: viem, posthog-node, @coinbase/cdp-sdk/*, @x402/*).
  • End-to-end runtime test through a public redirector:
    • redirect → 169.254.169.254: blocked
    • redirect → 127.0.0.1: blocked
    • normal public redirect (302 → 200): follows, returns final URL
    • direct internal URL: blocked

Note (not changed here)

The committed Prisma client in node_modules is stale and produced false-positive type errors until prisma generate was re-run; the build script already runs prisma generate && tsc, so it's not a runtime issue. Left untouched to keep this change isolated.

Open in Web Open in Cursor 

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>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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>
@cursor cursor Bot changed the title fix(transcribe-audio): add SSRF validation on audio_url before fetch fix(ssrf): close audio_url gap + redirect-based bypass across all URL tools Jun 25, 2026
@Deesmo

Deesmo commented Jul 26, 2026

Copy link
Copy Markdown
Owner Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants