Skip to content

Commit acc0805

Browse files
committed
Sanitize copied chat message content
1 parent f646632 commit acc0805

File tree

1 file changed

+40
-2
lines changed

1 file changed

+40
-2
lines changed

src/lib/components/chat/ChatMessage.svelte

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@
5757
void _isAuthor;
5858
void _readOnly;
5959
});
60-
6160
function handleKeyDown(e: KeyboardEvent) {
6261
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
6362
editFormEl?.requestSubmit();
@@ -67,6 +66,45 @@
6766
}
6867
}
6968
69+
function handleCopy(event: ClipboardEvent) {
70+
if (!contentEl) return;
71+
72+
const selection = window.getSelection();
73+
if (!selection || selection.isCollapsed) return;
74+
if (!selection.anchorNode || !selection.focusNode) return;
75+
76+
const anchorInside = contentEl.contains(selection.anchorNode);
77+
const focusInside = contentEl.contains(selection.focusNode);
78+
if (!anchorInside && !focusInside) return;
79+
80+
if (!event.clipboardData) return;
81+
82+
const range = selection.getRangeAt(0);
83+
const wrapper = document.createElement("div");
84+
wrapper.appendChild(range.cloneContents());
85+
86+
wrapper.querySelectorAll("*").forEach((el) => {
87+
el.removeAttribute("style");
88+
el.removeAttribute("class");
89+
el.removeAttribute("color");
90+
el.removeAttribute("bgcolor");
91+
el.removeAttribute("background");
92+
93+
for (const attr of Array.from(el.attributes)) {
94+
if (attr.name === "id" || attr.name.startsWith("data-")) {
95+
el.removeAttribute(attr.name);
96+
}
97+
}
98+
});
99+
100+
const html = wrapper.innerHTML;
101+
const text = wrapper.textContent ?? "";
102+
103+
event.preventDefault();
104+
event.clipboardData.setData("text/html", html);
105+
event.clipboardData.setData("text/plain", text);
106+
}
107+
70108
let editContentEl: HTMLTextAreaElement | undefined = $state();
71109
let editFormEl: HTMLFormElement | undefined = $state();
72110
@@ -183,7 +221,7 @@
183221
{/if}
184222
{/if}
185223

186-
<div bind:this={contentEl}>
224+
<div bind:this={contentEl} oncopy={handleCopy}>
187225
{#if isLast && loading && message.content.length === 0}
188226
<IconLoading classNames="loading inline ml-2 first:ml-0" />
189227
{/if}

0 commit comments

Comments
 (0)