Skip to content

fix(worker): handle data: URL images in processImage #194

Open
bbornino wants to merge 1 commit into
playfulprogramming:mainfrom
bbornino:fix/inline-data-url-images
Open

fix(worker): handle data: URL images in processImage #194
bbornino wants to merge 1 commit into
playfulprogramming:mainfrom
bbornino:fix/inline-data-url-images

Conversation

@bbornino

Copy link
Copy Markdown
Contributor

What

processImage assumed every image URL it received was fetchable over HTTP. Favicons and og:image banners are sometimes embedded directly as data: URLs instead — inline SVG text or base64-encoded raster data — and those got handed straight to fetchAsBot, which has no handling for the data: scheme and fails with a null body.

This adds an early branch in processImage, keyed off url.protocol === "data:", that decodes the payload directly instead of making a network request.

How

  • Parses the data:<mediatype>[;base64],<payload> shape from url.href, not url.pathname. The WHATWG URL parser treats data: as an opaque-path scheme, so an unescaped # in the payload gets silently split off into url.hashurl.pathname alone loses everything after it. Covered by the first test below.
  • SVG is detected from the parsed media type (not a fetched content-type header, which doesn't exist for data: URLs).
  • SVG payloads reuse the exact same svgo.optimize + image/svg+xml upload logic as the fetch path.
  • Raster payloads are base64-decoded into a Buffer, wrapped in a Readable, and fed through the same sharp metadata/resize/upload pipeline already used for fetched images.
  • The compareLastModified freshness check is skipped entirely for data: URLs — there's no HTTP resource or last-modified header to compare against. This is safe and idempotent: the upload key is already an md5 hash of url.href, and for a data: URL that href includes the actual payload, so identical embedded images naturally hash to the same key and a re-upload is a harmless no-op.
  • The existing http(s) fetch path is unchanged in behavior. To satisfy both "leave the fetch path untouched" and "reuse logic rather than duplicate it," I extracted two small private helpers (uploadSvg, uploadRasterImage) out of the original processImage body — same steps, same order, same conditionals, just callable from both the fetch branch and the new data: 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.ts before): apps/worker/src/tasks/url-metadata/utils/processImage.test.ts

  • Inline percent-encoded SVG data URL, with an unescaped # in the payload — this , since it's the case where url.pathname truncates and url.href doesn't.
  • Inline base64-encoded PNG data URL, through the sharp metadata/resize/upload pipeline.
  • An http(s)-fetched PNG, to confirm the existing network path still works and this change didn't regress it.

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

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
@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@bbornino, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 37 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8e5ac851-25d5-4ff9-9370-1d79a00fd86a

📥 Commits

Reviewing files that changed from the base of the PR and between d1b4e96 and 53432c3.

📒 Files selected for processing (2)
  • apps/worker/src/tasks/url-metadata/utils/processImage.test.ts
  • apps/worker/src/tasks/url-metadata/utils/processImage.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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.

Handle inline svg/png images in url-metadata

1 participant