fix mask editor on cloud by allowing crossorigin on image data (#5957)

## Summary

Fixed cross-origin canvas taint
[error](https://comfy-org.sentry.io/issues/6927234287/?referrer=slack&notification_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)
This commit is contained in:
Christian Byrne
2025-10-07 18:04:38 -07:00
committed by GitHub
parent 99b3a59679
commit fe0eaaefb3

View File

@@ -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<HTMLImageElement>((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<HTMLImageElement>(
(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<HTMLImageElement> {
return new Promise((resolve, reject) => {
const image = new Image() as HTMLImageElement
image.crossOrigin = 'anonymous'
image.onload = function () {
resolve(image)
}