#259·MegaParse

Unauthenticated full-read SSRF via the parse-from-URL endpoint (POST /v1/url)

Author: geo-chenCreated Jun 12, 2026Updated Jun 12, 2026
Labelsbug

Summary

MegaParse's API exposes POST /v1/url, which fetches a caller-supplied URL server-side (via httpx for PDFs, or a Playwright loader otherwise) and returns the parsed content. There is no authentication and no validation of the URL host or scheme. An unauthenticated client can therefore make the server fetch internal-only services and cloud instance-metadata endpoints, and the response body is returned in the JSON response (full-read SSRF). Confirmed against the real app: the endpoint fetched an internal listener and returned its secret body to the caller.

Details

libs/megaparse/src/megaparse/api/app.py, POST /v1/url (~lines 122 to 157):

python
async def upload_url(url: str, playwright_loader = Depends(get_playwright_loader)):
    if url ends with .pdf:
        response = await client.get(url)          # ~line 131, raw httpx GET
    else:
        ... playwright_loader.aload()              # ~line 144, loads attacker URL
        return {"result": <page text>}             # ~lines 154-157, body returned

The url query parameter is taken raw, with no scheme allowlist and no private/loopback/link-local/metadata filtering anywhere in the codebase. The route has no authentication dependency or middleware, and the app binds 0.0.0.0:8000 (~line 161).

Impact

An unauthenticated network client can coerce the MegaParse server into fetching internal-only services and cloud instance-metadata endpoints and read their responses (returned in the result field), enabling internal reconnaissance and theft of cloud-metadata credentials. This is unauthenticated full-read SSRF on the default configuration.

Remediation

Before fetching, parse the URL and reject loopback, private (RFC1918), link-local (169.254.0.0/16, fe80::/10), unique-local, and other reserved ranges plus cloud-metadata hostnames; enforce an http/https allowlist; disable or re-validate redirects and pin the validated IP (close DNS rebinding). Require authentication on the API.