fix(worker): handle data: URL images in processImage #194
Conversation
favicons and og:image banners are sometimes embedded directly as data: URLs (inline SVG or base64 raster) rather than linked, and processImage handed those straight to fetchAsBot, which has no data: scheme handling and fails with a null body. Adds an early branch, keyed off url.protocol === "data:", that decodes the payload directly instead of making a network request. Parses the data:<mediatype>[;base64],<payload> shape from url.href rather than url.pathname, since the WHATWG URL parser treats data: as an opaque-path scheme and silently truncates url.pathname at an unescaped `#`. SVG payloads reuse the existing svgo.optimize + s3 upload logic; raster payloads are base64-decoded into a Buffer, wrapped in a Readable, and fed through the existing sharp metadata/resize/upload pipeline. The compareLastModified freshness check is skipped for data: URLs (no HTTP resource to compare against) - safe since the upload key is already an md5 hash of url.href, so identical embedded images naturally collide onto the same key. Fixes playfulprogramming#28
|
Warning Review limit reached
Next review available in: 37 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
What
processImageassumed every image URL it received was fetchable over HTTP. Favicons andog:imagebanners are sometimes embedded directly asdata:URLs instead — inline SVG text or base64-encoded raster data — and those got handed straight tofetchAsBot, which has no handling for thedata:scheme and fails with a null body.This adds an early branch in
processImage, keyed offurl.protocol === "data:", that decodes the payload directly instead of making a network request.How
data:<mediatype>[;base64],<payload>shape fromurl.href, noturl.pathname. The WHATWG URL parser treatsdata:as an opaque-path scheme, so an unescaped#in the payload gets silently split off intourl.hash—url.pathnamealone loses everything after it. Covered by the first test below.content-typeheader, which doesn't exist fordata:URLs).svgo.optimize+image/svg+xmlupload logic as the fetch path.Buffer, wrapped in aReadable, and fed through the same sharp metadata/resize/upload pipeline already used for fetched images.compareLastModifiedfreshness check is skipped entirely fordata:URLs — there's no HTTP resource orlast-modifiedheader to compare against. This is safe and idempotent: the upload key is already an md5 hash ofurl.href, and for adata:URL thathrefincludes the actual payload, so identical embedded images naturally hash to the same key and a re-upload is a harmless no-op.uploadSvg,uploadRasterImage) out of the originalprocessImagebody — same steps, same order, same conditionals, just callable from both the fetch branch and the newdata:branch. Flagging this explicitly since it's a judgment call between two things you might want independently; happy to switch to a literal copy-paste (zero-diff on the fetch path) if you'd rather.Tests
New test file (none existed for
processImage.tsbefore):apps/worker/src/tasks/url-metadata/utils/processImage.test.ts#in the payload — this , since it's the case whereurl.pathnametruncates andurl.hrefdoesn't.I don't have the literal text of the issue's reproduced error, so the SVG test uses a representative data URL rather than a verbatim copy — let me know if the real repro string differs meaningfully.
Closes #28