diff --git a/.changeset/fix-remove-rel-target-outgoing-links.md b/.changeset/fix-remove-rel-target-outgoing-links.md new file mode 100644 index 000000000..c76cef9ba --- /dev/null +++ b/.changeset/fix-remove-rel-target-outgoing-links.md @@ -0,0 +1,5 @@ +--- +default: patch +--- + +Remove target and rel attributes from outgoing html links. diff --git a/src/app/components/editor/output.ts b/src/app/components/editor/output.ts index 0a06a2330..d8be87e30 100644 --- a/src/app/components/editor/output.ts +++ b/src/app/components/editor/output.ts @@ -82,7 +82,7 @@ const elementToCustomHtml = ( case BlockType.Link: return testMatrixTo(node.href) ? sanitizeText(node.href) - : `${children}`; + : `${children}`; case BlockType.Command: return `/${sanitizeText(node.command)}`; default: diff --git a/src/app/plugins/markdown/markdownToHtml.test.ts b/src/app/plugins/markdown/markdownToHtml.test.ts index f6599622a..6ed37ae0f 100644 --- a/src/app/plugins/markdown/markdownToHtml.test.ts +++ b/src/app/plugins/markdown/markdownToHtml.test.ts @@ -35,6 +35,8 @@ describe('markdownToHtml', () => { it('converts links', () => { const result = markdownToHtml('[link](https://example.com)'); expect(result).toContain(' { diff --git a/src/app/plugins/markdown/markdownToHtml.ts b/src/app/plugins/markdown/markdownToHtml.ts index c7761859e..89e09e0bd 100644 --- a/src/app/plugins/markdown/markdownToHtml.ts +++ b/src/app/plugins/markdown/markdownToHtml.ts @@ -154,12 +154,8 @@ export function markdownToHtml(markdown: string, options?: MarkdownToHtmlOptions const allowlistedHtml = escapeNonAllowlistedHtmlTags(unescapedInline); - // Force all links to open in a new tab, validate
    . + // Validate
      after sanitization. DOMPurify.addHook('afterSanitizeAttributes', (node) => { - if (node.tagName === 'A' && node.getAttribute('href')) { - node.setAttribute('target', '_blank'); - node.setAttribute('rel', 'noreferrer noopener'); - } if (node.tagName === 'OL') { const start = node.getAttribute('start'); if (start !== null && !ORDERED_LIST_START_REGEX.test(start)) { @@ -177,8 +173,6 @@ export function markdownToHtml(markdown: string, options?: MarkdownToHtmlOptions 'title', 'height', 'width', - 'target', - 'rel', 'data-mx-emoticon', 'data-mx-spoiler', 'data-mx-maths', @@ -194,9 +188,8 @@ export function markdownToHtml(markdown: string, options?: MarkdownToHtmlOptions // Ensure these safe attrs survive sanitization even when the input HTML // originates from markdown-embedded tags (e.g. custom emoji ). // `start` must be URI-safe or DOMPurify drops it when ALLOWED_URI_REGEXP is set. - ADD_ATTR: ['target', 'rel', 'height', 'width'], + ADD_ATTR: ['height', 'width'], ADD_URI_SAFE_ATTR: ['start'], - // Force all links to have safe rel attribute FORCE_BODY: false, ALLOWED_URI_REGEXP: /^(?:https?|ftp|mailto|magnet|mxc):/i, }); diff --git a/src/app/utils/sanitize.test.ts b/src/app/utils/sanitize.test.ts index b4839f926..6df18dbb1 100644 --- a/src/app/utils/sanitize.test.ts +++ b/src/app/utils/sanitize.test.ts @@ -53,8 +53,8 @@ describe('sanitizeCustomHtml', () => { expect(result).toContain('data-mx-maths="x"'); expect(result).toContain('data-md="**"'); expect(result).toContain('href="https://example.com"'); - expect(result).toContain('target="_blank"'); expect(result).toContain('data-md="[]()"'); + expect(result).not.toContain('target='); expect(result).not.toContain('rel='); expect(result).toContain('
        '); expect(result).not.toContain('type='); diff --git a/src/app/utils/sanitize.ts b/src/app/utils/sanitize.ts index 0e56d550c..3b21ca39b 100644 --- a/src/app/utils/sanitize.ts +++ b/src/app/utils/sanitize.ts @@ -46,7 +46,7 @@ const permittedHtmlTags = [ const permittedTagToAttributes = { span: ['data-mx-bg-color', 'data-mx-color', 'data-mx-spoiler', 'data-mx-maths', 'data-md'], - a: ['target', 'href', 'data-md'], + a: ['href', 'data-md'], img: ['width', 'height', 'alt', 'title', 'src', 'data-mx-emoticon'], // data-mx-emoticon is for MSC2545 ol: ['start', 'data-md'], ul: ['data-md'],