From fe0eaaefb34ee47de4429837dcbea3529d7adb6d Mon Sep 17 00:00:00 2001 From: Christian Byrne Date: Tue, 7 Oct 2025 18:04:38 -0700 Subject: [PATCH] fix mask editor on cloud by allowing crossorigin on image data (#5957) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Fixed cross-origin canvas taint [error](https://comfy-org.sentry.io/issues/6927234287/?referrer=slack¬ification_uuid=e2ac931f-c955-43a2-a345-76fa8b164504&alert_rule_id=16146009&alert_type=issue) in mask editor by adding CORS support to image loading. ## Background When images from different origins are drawn to canvas without CORS headers, browsers "taint" the canvas to prevent data extraction attacks. This breaks `getImageData()` calls with a SecurityError. The [W3C standard solution](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/crossOrigin) is `crossOrigin = 'anonymous'`. Intended flow: 1. Frontend sets img.crossOrigin = 'anonymous' 2. Browser sends CORS preflight to GCS: "Can cloud.comfy.org access this image?" 3. GCS must respond: "Yes, that origin is allowed" 4. Browser loads image with CORS headers enabled 5. Canvas operations work without taint ## Review Focus Canvas security model compliance and compatibility with cloud deployment image redirects to GCS. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-5957-fix-mask-editor-on-cloud-by-allowing-crossorigin-on-image-data-2856d73d3650819a84b2fed19d85d815) by [Unito](https://www.unito.io) --- src/extensions/core/maskeditor.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/extensions/core/maskeditor.ts b/src/extensions/core/maskeditor.ts index 2497fd287..f8dc58524 100644 --- a/src/extensions/core/maskeditor.ts +++ b/src/extensions/core/maskeditor.ts @@ -1168,6 +1168,7 @@ class MaskEditorDialog extends ComfyDialog { if (ComfyApp.clipspace?.imgs && paintedIndex !== undefined) { // Create and set new image const newImage = new Image() + newImage.crossOrigin = 'anonymous' newImage.src = mkFileUrl({ ref: filepath, preview: true }) ComfyApp.clipspace.imgs[paintedIndex] = newImage @@ -1209,6 +1210,7 @@ class MaskEditorDialog extends ComfyDialog { if (!ComfyApp.clipspace?.imgs || indexToSaveTo === undefined) return // Create and set new image const newImage = new Image() + newImage.crossOrigin = 'anonymous' newImage.src = mkFileUrl({ ref: filepath, preview: true }) ComfyApp.clipspace.imgs[indexToSaveTo] = newImage @@ -4162,6 +4164,7 @@ class UIManager { this.image = await new Promise((resolve, reject) => { const img = new Image() + img.crossOrigin = 'anonymous' img.onload = () => resolve(img) img.onerror = reject img.src = rgb_url.toString() @@ -4173,6 +4176,7 @@ class UIManager { this.paint_image = await new Promise( (resolve, reject) => { const img = new Image() + img.crossOrigin = 'anonymous' img.onload = () => resolve(img) img.onerror = reject img.src = paintURL.toString() @@ -4308,6 +4312,7 @@ class UIManager { private loadImage(imagePath: URL): Promise { return new Promise((resolve, reject) => { const image = new Image() as HTMLImageElement + image.crossOrigin = 'anonymous' image.onload = function () { resolve(image) }