Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fix-svelte-csp-violations.md
Original file line number Diff line number Diff line change
@@ -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.
16 changes: 15 additions & 1 deletion packages/svelte/src/lib/base/image.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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;
})
)
);
</script>

<img
{...props}
{...filteredProps}
{style}
{loading}
{width}
Expand Down
16 changes: 15 additions & 1 deletion packages/svelte/src/lib/base/source.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,20 @@
...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 filteredProps = $derived(
Object.fromEntries(
Object.entries(transformedProps).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;
})
)
);
</script>

<source {...transformedProps} />
<source {...filteredProps} />
16 changes: 15 additions & 1 deletion packages/svelte/src/lib/image.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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;
})
)
);
</script>

<img
{...rest}
{...filteredRest}
{style}
{loading}
{width}
Expand Down
16 changes: 15 additions & 1 deletion packages/svelte/src/lib/source.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,20 @@
...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 filteredProps = $derived(
Object.fromEntries(
Object.entries(transformedProps).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;
})
)
);
</script>

<source {...transformedProps} />
<source {...filteredProps} />
41 changes: 41 additions & 0 deletions packages/svelte/test/svelte.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<HTMLImageElement>("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<HTMLImageElement>(
"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
});
});