diff --git a/docs/WEB.md b/docs/WEB.md
index d51b6908c..bc69f67fb 100644
--- a/docs/WEB.md
+++ b/docs/WEB.md
@@ -30,7 +30,7 @@ On web, HTML is sanitized automatically with [DOMPurify](https://github.com/cure
By default, sanitization strips links with non-standard protocols (e.g. `custom://…`). Both `EnrichedText` and `EnrichedTextInput` accept a web-only `sanitizationConfig` prop whose `linkRegex` field lets you control which link URIs survive.
-`linkRegex` maps directly to DOMPurify's [`ALLOWED_URI_REGEXP`](https://github.com/cure53/DOMPurify#can-i-configure-dompurify), so it **replaces** the default allow-list rather than extending it — remember to keep the standard protocols you still want to permit:
+`linkRegex` maps directly to DOMPurify's [`ALLOWED_URI_REGEXP`](https://github.com/cure53/DOMPurify#can-i-configure-dompurify), so it **replaces** the default allow-list rather than extending it. Because this regex affects all URI-containing attributes (e.g. `src` in `
`), remember to keep the standard protocols you still want to permit:
```tsx
sanitizationConfigRef.current, onChangeHtml);
useOnChangeText(editor, onChangeText);
useOnChangeState(editor, resolvedHtmlStyle, onChangeState);
useOnLinkDetected(editor, linkEmitterRef);
@@ -357,7 +357,7 @@ export const EnrichedTextInput = ({
Promise.resolve(
normalizeHtmlFromTiptap(
editor.getHTML(),
- sanitizationConfigRef.current
+ () => sanitizationConfigRef.current
)
),
toggleBold: () => runFocused(editor, (c) => c.toggleBold()),
diff --git a/src/web/__tests__/sanitization.test.ts b/src/web/__tests__/sanitization.test.ts
index 2814432c8..9cdb3c800 100644
--- a/src/web/__tests__/sanitization.test.ts
+++ b/src/web/__tests__/sanitization.test.ts
@@ -147,4 +147,16 @@ describe('sanitizeHtml with a custom linkRegex', () => {
// eslint-disable-next-line no-script-url
expect(out).not.toContain('javascript:');
});
+
+ it('keeps mention attributes when a custom config is provided', () => {
+ const out = sanitizeHtml(
+ '@Joe',
+ {
+ linkRegex,
+ }
+ );
+ expect(out).toContain('text="Joe"');
+ expect(out).toContain('indicator="@"');
+ expect(out).toContain('data-user-id="42"');
+ });
});
diff --git a/src/web/normalization/tiptapHtmlNormalizer.ts b/src/web/normalization/tiptapHtmlNormalizer.ts
index 976e1d316..cadbda43a 100644
--- a/src/web/normalization/tiptapHtmlNormalizer.ts
+++ b/src/web/normalization/tiptapHtmlNormalizer.ts
@@ -22,8 +22,9 @@ export function prepareHtmlForTiptap(
export function normalizeHtmlFromTiptap(
html: string,
- sanitizationConfig?: SanitizationConfig
+ getSanitizationConfig: () => SanitizationConfig | undefined
): string {
+ const sanitizationConfig = getSanitizationConfig();
html = sanitizeHtml(html, sanitizationConfig);
html = checkboxHtmlFromTiptap(html);
diff --git a/src/web/sanitization/htmlSanitizer.ts b/src/web/sanitization/htmlSanitizer.ts
index a57a56aa0..6b8acf442 100644
--- a/src/web/sanitization/htmlSanitizer.ts
+++ b/src/web/sanitization/htmlSanitizer.ts
@@ -10,6 +10,7 @@ export function sanitizeHtml(html: string, config?: SanitizationConfig) {
return DOMPurify.sanitize(html, {
ADD_TAGS: ['mention', 'codeblock'],
ADD_ATTR: MENTION_ATTRS,
+ ADD_URI_SAFE_ATTR: MENTION_ATTRS,
// if not supplied, fall back to DOMPurify's built-in default.
...(config?.linkRegex ? { ALLOWED_URI_REGEXP: config.linkRegex } : {}),
});
diff --git a/src/web/useOnChangeHtml.ts b/src/web/useOnChangeHtml.ts
index a172d0655..f3cb0991b 100644
--- a/src/web/useOnChangeHtml.ts
+++ b/src/web/useOnChangeHtml.ts
@@ -6,10 +6,10 @@ import { normalizeHtmlFromTiptap } from './normalization/tiptapHtmlNormalizer';
export const useOnChangeHtml = (
editor: Editor,
- onChangeHtml?: (e: NativeSyntheticEvent) => void,
- sanitizationConfig?: SanitizationConfig
+ getSanitizationConfig: () => SanitizationConfig | undefined,
+ onChangeHtml?: (e: NativeSyntheticEvent) => void
) => {
useOnEditorChange(editor, onChangeHtml, (e) =>
- normalizeHtmlFromTiptap(e.getHTML(), sanitizationConfig)
+ normalizeHtmlFromTiptap(e.getHTML(), getSanitizationConfig)
);
};