Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#7673: Avoid CORS issues in ImageReader #7972

Merged
merged 4 commits into from
Mar 19, 2024
Merged

Conversation

fregante
Copy link
Contributor

@fregante fregante commented Mar 19, 2024

What does this PR do?

Demo

Repro available on the original issue

Screen.Recording.mov

Future Work

Checklist

Copy link

codecov bot commented Mar 19, 2024

Codecov Report

Attention: Patch coverage is 13.63636% with 38 lines in your changes are missing coverage. Please review.

Project coverage is 72.90%. Comparing base (7565b6c) to head (2a156f1).
Report is 3 commits behind head on main.

❗ Current head 2a156f1 differs from pull request most recent head 765565f. Consider uploading reports for the commit 765565f to get more accurate results

Files Patch % Lines
src/utils/imageUtils.ts 9.75% 37 Missing ⚠️
src/bricks/readers/ImageExifReader.ts 50.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #7972      +/-   ##
==========================================
- Coverage   72.96%   72.90%   -0.06%     
==========================================
  Files        1295     1293       -2     
  Lines       40248    40253       +5     
  Branches     7490     7489       -1     
==========================================
- Hits        29366    29347      -19     
- Misses      10882    10906      +24     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

*
* This supports all URLs, including data: and blob: (neither one of which would trigger an HTTP request)
*/
export async function loadImageBinaryData(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Original implementation:

// TODO: I think axios alone can handle all of this, or FileReader can handle both data and blob in a CSP-safe way
async function getData(img: HTMLImageElement): Promise<ArrayBuffer> {
// Adapted from https://github.com/exif-js/exif-js/blob/master/exif.js#L384
if (/^data:/i.test(img.src)) {
// Data URI
return convertDataUrl(img.src, "ArrayBuffer");
}
if (/^blob:/i.test(img.src)) {
// Object URL
const blob = await fetch(img.src).then(async (r) => r.blob());
return blob.arrayBuffer();
}
const response = await axios.get<ArrayBuffer>(img.src, {
responseType: "arraybuffer",
});
if (response.status !== 200) {
throw new Error(`Error fetching image ${img.src}: ${response.statusText}`);
}
return response.data;
}

Changes:

  • accept a string instead of <img> since img is not strictly required (can't read binary from it)
  • throw proper error on SVG images
  • drop redundant axios-based fetch
  • drop TODO

* @warning The base64 encoding is not guaranteed to match the original image binary. For that, use `loadImageBinaryData`
* @warning If the loaded image is not CORS-safe, a new HTTP request will be made.
*/
export async function loadImageAsBase64(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Previous implementation:

// Copied from https://stackoverflow.com/questions/934012/get-image-data-url-in-javascript
// TODO: Replace with `getData` from ImageExifReader?
function getBase64Image(img: HTMLImageElement) {
// Create an empty canvas element
const canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
// Copy the image contents to the canvas
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-unnecessary-type-assertion -- 2d always exists
const context = canvas.getContext("2d")!;
context.drawImage(img, 0, 0);
// Get the data-URL formatted image
// Firefox supports PNG and JPEG. You could check img.src to
// guess the original format, but be aware the using "image/jpg"
// will re-encode the image.
const dataURL = canvas.toDataURL("image/png");
return dataURL.replace(/^data:image\/(png|jpg);base64,/, "");
}

Changes:

  • clarify expectations of function, add warnings
  • if already a base64, skip conversion
  • use natural image size instead of the authored <img width height>
  • detect CORS issues and re-load image anonymously

@fregante fregante marked this pull request as ready for review March 19, 2024 07:26
@fregante fregante added the enhancement New feature or request label Mar 19, 2024
Comment on lines +68 to +70
const parsed = parseDataUrl(dataURL);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-unnecessary-type-assertion -- The browser just generated it, it's safe. If it fails, then it's likely a bug in `parseDataUrl` and it should be fixed.
return parsed!.encodedBody;
Copy link
Collaborator

Choose a reason for hiding this comment

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

If parseDataUrl does fail, it would be good to report the dataURL to DataDog to make it easier to debug. I don't think it's very likely though.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Let's wait until that error shows up in the logs before we spend time on it

Copy link

No loom links were found in the first post. Please add one there if you'd like to it to appear on Slack.

Do not edit this comment manually.

@fregante fregante enabled auto-merge (squash) March 19, 2024 17:49
@fregante fregante merged commit 1802a49 into main Mar 19, 2024
17 of 18 checks passed
@fregante fregante deleted the F/bug/cors-ImageReader branch March 19, 2024 17:57
@grahamlangford grahamlangford added this to the 1.8.11 milestone Mar 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Image Reader: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.
2 participants