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('