Files
plane/apps
Manish Gupta bfdc654699 fix(live): close redirect-follow gap in PDF image SSRF guard
isSafeImageSrc only validated the URL handed to <Image src>, before render.
The actual fetch happens inside @react-pdf/image's fetchRemoteFile, which
follows redirects by default and never re-consults the guard on the
redirect target — so a URL on an ordinary public host could 302 to an
internal address (cloud metadata, a Docker service name, loopback) and be
fetched anyway, no DNS control required.

Close it the same way imageComponent asset images are already handled:
pre-fetch image-node srcs before rendering starts, instead of handing a raw
URL to the renderer. Added fetchImageSrcSafely, which fetches with
redirect: "manual" and re-validates every hop against isSafeImageSrc,
capped at 5 redirects. pdf-export.service.ts now extracts external image
srcs alongside asset ids and resolves both into the same data-URI map
before the synchronous render pass runs; node-renderers.tsx's image
renderer looks up that pre-resolved value instead of taking the raw src.

Also switched the pre-existing per-asset URL resolution loop in
processImages to Promise.all, since it was already independent per asset
and needed to satisfy the same lint rule the new redirect-following code
does (which, unlike that loop, is genuinely sequential by nature).

Documented the residual DNS-rebinding TOCTOU that remains on the final,
non-redirect hop: isSafeImageSrc judges a hostname once, and the actual
fetch resolves DNS again independently, so a name that changes address
between those two lookups is still unguarded. Re-validating every redirect
hop closes the far more easily exploited "one crafted HTTP response" gap;
it does not add DNS pinning.

Co-authored-by: Plane AI <noreply@plane.so>
2026-08-27 10:52:06 +05:30
..
2026-07-28 15:50:53 +05:30