Skip to content
Merged
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
2 changes: 1 addition & 1 deletion docs/WEB.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<img>`), remember to keep the standard protocols you still want to permit:

```tsx
<EnrichedText
Expand Down
4 changes: 2 additions & 2 deletions src/web/EnrichedTextInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ export const EnrichedTextInput = ({
);

useMentionEvents(editor, getMentionCallbacks);
useOnChangeHtml(editor, onChangeHtml, sanitizationConfig);
useOnChangeHtml(editor, () => sanitizationConfigRef.current, onChangeHtml);
useOnChangeText(editor, onChangeText);
useOnChangeState(editor, resolvedHtmlStyle, onChangeState);
useOnLinkDetected(editor, linkEmitterRef);
Expand Down Expand Up @@ -357,7 +357,7 @@ export const EnrichedTextInput = ({
Promise.resolve(
normalizeHtmlFromTiptap(
editor.getHTML(),
sanitizationConfigRef.current
() => sanitizationConfigRef.current
)
),
toggleBold: () => runFocused(editor, (c) => c.toggleBold()),
Expand Down
12 changes: 12 additions & 0 deletions src/web/__tests__/sanitization.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
'<mention text="Joe" indicator="@" data-user-id="42">@Joe</mention>',
{
linkRegex,
}
);
expect(out).toContain('text="Joe"');
expect(out).toContain('indicator="@"');
expect(out).toContain('data-user-id="42"');
});
});
3 changes: 2 additions & 1 deletion src/web/normalization/tiptapHtmlNormalizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
1 change: 1 addition & 0 deletions src/web/sanitization/htmlSanitizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 } : {}),
});
Expand Down
6 changes: 3 additions & 3 deletions src/web/useOnChangeHtml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import { normalizeHtmlFromTiptap } from './normalization/tiptapHtmlNormalizer';

export const useOnChangeHtml = (
editor: Editor,
onChangeHtml?: (e: NativeSyntheticEvent<OnChangeHtmlEvent>) => void,
sanitizationConfig?: SanitizationConfig
getSanitizationConfig: () => SanitizationConfig | undefined,
onChangeHtml?: (e: NativeSyntheticEvent<OnChangeHtmlEvent>) => void
) => {
useOnEditorChange(editor, onChangeHtml, (e) =>
normalizeHtmlFromTiptap(e.getHTML(), sanitizationConfig)
normalizeHtmlFromTiptap(e.getHTML(), getSanitizationConfig)
);
};
Loading