From 010668255d7888f486ca2c58cbdf628ba400ddd2 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Mon, 20 Jul 2026 17:55:47 -0700 Subject: [PATCH] fix(editor): validate preserved break source Use the editor model's supported DOM parse-rule type instead of a private duplicate. Exercise the rendered-DOM round trip and reject forged preservation attributes before they can become hidden Markdown source. --- .../raw-markdown-html-line-break.test.ts | 31 +++++++++++++++++-- .../editor/raw-markdown-html-source-dom.ts | 25 +++++++-------- 2 files changed, 39 insertions(+), 17 deletions(-) diff --git a/src/renderer/src/components/editor/raw-markdown-html-line-break.test.ts b/src/renderer/src/components/editor/raw-markdown-html-line-break.test.ts index 079aa0a085a..f9a0a86dd49 100644 --- a/src/renderer/src/components/editor/raw-markdown-html-line-break.test.ts +++ b/src/renderer/src/components/editor/raw-markdown-html-line-break.test.ts @@ -1,3 +1,5 @@ +// @vitest-environment happy-dom + import { describe, expect, it } from 'vitest' import { Editor } from '@tiptap/core' import type { Node as ProseMirrorNode } from '@tiptap/pm/model' @@ -62,16 +64,39 @@ describe('raw markdown
line breaks in the rich editor', () => { } }) - it('round-trips the verbatim
tags in a table cell on save', () => { + it('round-trips verbatim
tags after the rendered DOM is parsed again', () => { const editor = createEditor(TABLE_WITH_BREAKS) try { + const rendered = document.createElement('div') + rendered.innerHTML = editor.getHTML() + expect(rendered.querySelectorAll('br[data-raw-markdown-html-inline]')).toHaveLength(3) + expect(rendered.textContent).not.toContain('') - expect(output).toContain('
') + expect(output.match(//g)).toEqual(['
', '
', '
']) // The break must not silently collapse into a space on serialization. expect(output).not.toContain('`normal` `defect`') } finally { editor.destroy() } }) + + it('does not accept arbitrary Markdown source from a forged break marker', () => { + const editor = createEditor('Before
after\n') + try { + const rendered = document.createElement('div') + rendered.innerHTML = editor.getHTML() + rendered + .querySelector('br[data-raw-markdown-html-inline]') + ?.setAttribute('data-raw-markdown-html-value', '') + + editor.commands.setContent(rendered.innerHTML) + const output = (editor as Editor & { getMarkdown(): string }).getMarkdown() + expect(output).toContain('Before
after') + expect(output).not.toContain('