diff --git a/.changeset/fix-svelte-csp-violations.md b/.changeset/fix-svelte-csp-violations.md new file mode 100644 index 00000000..3670e1d1 --- /dev/null +++ b/.changeset/fix-svelte-csp-violations.md @@ -0,0 +1,5 @@ +--- +"@unpic/svelte": patch +--- + +Fix CSP violations by filtering undefined event handlers in Svelte 5. When event handler properties like `onload` and `onerror` are present but undefined in spread props, Svelte 5 adds internal event bindings that trigger CSP violations. The fix filters out only undefined event handlers while preserving explicitly passed handlers. diff --git a/packages/svelte/src/lib/base/image.svelte b/packages/svelte/src/lib/base/image.svelte index 55aa5087..fff6fe36 100644 --- a/packages/svelte/src/lib/base/image.svelte +++ b/packages/svelte/src/lib/base/image.svelte @@ -24,10 +24,24 @@ .filter(Boolean) .join(";"), ); + + // Filter out undefined event handlers to prevent CSP violations in Svelte 5 + // When event handler properties are present but undefined in the spread object, + // Svelte 5 adds internal event bindings that violate CSP policies + const filteredProps = $derived( + Object.fromEntries( + Object.entries(props).filter(([key, value]) => { + // Keep non-event-handler properties + if (!key.startsWith("on")) return true; + // Keep event handlers that are explicitly defined by the user + return value !== undefined; + }) + ) + ); { + // Keep non-event-handler properties + if (!key.startsWith("on")) return true; + // Keep event handlers that are explicitly defined by the user + return value !== undefined; + }) + ) + ); - + diff --git a/packages/svelte/src/lib/image.svelte b/packages/svelte/src/lib/image.svelte index 91447fa5..a41312ea 100644 --- a/packages/svelte/src/lib/image.svelte +++ b/packages/svelte/src/lib/image.svelte @@ -43,10 +43,24 @@ aspectRatio: _aspectRatio, ...rest } = props; + + // Filter out undefined event handlers to prevent CSP violations in Svelte 5 + // When event handler properties are present but undefined in the spread object, + // Svelte 5 adds internal event bindings that violate CSP policies + const filteredRest = $derived( + Object.fromEntries( + Object.entries(rest).filter(([key, value]) => { + // Keep non-event-handler properties + if (!key.startsWith("on")) return true; + // Keep event handlers that are explicitly defined by the user + return value !== undefined; + }) + ) + ); { + // Keep non-event-handler properties + if (!key.startsWith("on")) return true; + // Keep event handlers that are explicitly defined by the user + return value !== undefined; + }) + ) + ); - + diff --git a/packages/svelte/test/svelte.test.ts b/packages/svelte/test/svelte.test.ts index 03101979..24eed4c0 100644 --- a/packages/svelte/test/svelte.test.ts +++ b/packages/svelte/test/svelte.test.ts @@ -27,4 +27,45 @@ describe("the Svelte component", () => { expectSourcePropsToMatchTransformed(source, props); }); } + + test("filters out undefined event handlers to prevent CSP violations", () => { + const props = { + src: "https://cdn.shopify.com/static/sample-images/bath_grande_crop_center.jpeg", + layout: "constrained" as const, + width: 800, + height: 600, + alt: "Test image", + // Simulating the case where TypeScript types include these but they're undefined + onload: undefined, + onerror: undefined, + }; + render(Image, { props }); + const img = screen.getByAltText("Test image"); + expect(img).toBeTruthy(); + // Verify undefined event handlers are not present as attributes + expect(img.getAttribute("onload")).toBeNull(); + expect(img.getAttribute("onerror")).toBeNull(); + }); + + test("preserves explicitly defined event handlers", () => { + const onloadHandler = () => console.log("loaded"); + const onerrorHandler = () => console.log("error"); + const props = { + src: "https://cdn.shopify.com/static/sample-images/bath_grande_crop_center.jpeg", + layout: "constrained" as const, + width: 800, + height: 600, + alt: "Test image with handlers", + onload: onloadHandler, + onerror: onerrorHandler, + }; + render(Image, { props }); + const img = screen.getByAltText( + "Test image with handlers", + ); + expect(img).toBeTruthy(); + // Verify explicit event handlers are present + // In Svelte 5, these will be bound using Svelte's event system + // We can't directly check the handler functions, but we verify the component renders without error + }); });