Fix trailing hard breaks being dropped on paste - #7840
Conversation
🦋 Changeset detectedLatest commit: f711838 The changes in this PR will be included in the next version bump. This PR includes changesets to release 72 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
✅ Deploy Preview for tiptap-embed ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
@coderabbitai review |
✅ Action performedReview finished.
|
📝 WalkthroughSummary
WalkthroughThe hard-break extension now preserves trailing hard breaks when pasting Tiptap clipboard HTML. A regression test verifies round-trip parsing for multiple trailing breaks, and a patch changeset documents the fix. ChangesHard-break paste preservation
Estimated code review effort: 2 (Simple) | ~10 minutes Suggested labels: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/extension-hard-break/src/hard-break.ts`:
- Line 8: Update the HTML parsing in the hard-break helper around DOMParser
usage to remain Trusted Types-safe: reuse ProseMirror’s existing clipboard
parsing path, or allow the helper to accept and pass through TrustedHTML.
Preserve the current parsed-document behavior while ensuring paste works when
Trusted Types enforcement is enabled.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 733f9d59-0fae-4540-ad53-6e977169fed4
📒 Files selected for processing (3)
.changeset/clear-hard-break-paste.mdpackages/extension-hard-break/__tests__/hard-break.spec.tspackages/extension-hard-break/src/hard-break.ts
| return html | ||
| } | ||
|
|
||
| const document = new window.DOMParser().parseFromString(html, 'text/html') |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
rg -n "trustedTypes|maybeWrapTrusted|DOMParser|parseFromClipboard" packagesRepository: ueberdosis/tiptap
Length of output: 3025
🏁 Script executed:
sed -n '1,220p' packages/extension-hard-break/src/hard-break.ts
printf '\n--- tests ---\n'
sed -n '1,220p' packages/extension-hard-break/__tests__/hard-break.spec.ts
printf '\n--- core elementFromString ---\n'
sed -n '1,200p' packages/core/src/utilities/elementFromString.ts
printf '\n--- pm view parseFromClipboard references in repo? ---\n'
rg -n "__parseFromClipboard|parseFromClipboard|trustedTypes|maybeWrapTrusted" packages/pm packages -g '!**/dist/**'Repository: ueberdosis/tiptap
Length of output: 6186
🏁 Script executed:
git ls-files 'packages/pm/**' 'packages/extension-hard-break/**' | sed -n '1,200p'Repository: ueberdosis/tiptap
Length of output: 928
🏁 Script executed:
git ls-files | rg '^packages/pm/'Repository: ueberdosis/tiptap
Length of output: 607
🌐 Web query:
ProseMirror parseFromClipboard Trusted Types DOMParser parseFromString source
💡 Result:
ProseMirror handles Trusted Types through its internal clipboard parsing logic within prosemirror-view [1]. When an environment enforces a strict Content Security Policy (CSP) with require-trusted-types-for 'script', standard browser APIs like innerHTML and DOMParser.parseFromString() will throw a TypeError if they receive a raw string instead of a TrustedHTML object [2][3][4]. To address this, ProseMirror uses a helper function (often named maybeWrapTrusted in prosemirror-view/src/clipboard.ts) to manage interaction with the browser's Trusted Types API [1][5]. Key points regarding ProseMirror's implementation: 1. Automatic Policy Usage: ProseMirror attempts to use trustedTypes.defaultPolicy if it is defined by the application [5][6]. This is the recommended approach, as it allows developers to define their own sanitization logic (e.g., using DOMPurify) and ensures that all HTML passed to ProseMirror's internal parsing sinks is properly sanitized [5]. 2. Fallback Policy: If no default policy exists, ProseMirror has historically created its own policy named "ProseMirrorClipboard" [5][3]. This policy performs no sanitization (it effectively acts as a pass-through) to allow pasting to function in restrictive environments [5]. Security researchers have noted that this "ProseMirrorClipboard" policy can bypass CSP protections if misused, as it provides a trusted sink for arbitrary HTML [5]. 3. DOMParser and Sinks: Both Element.innerHTML and DOMParser.parseFromString() are considered injection sinks under Trusted Types [4]. When ProseMirror parses HTML content—whether from the clipboard or via DOMParser—it must satisfy the browser's requirement that these sinks receive TrustedHTML [2][4]. 4. Best Practices: To ensure both security and functionality, you should define a trustedTypes.defaultPolicy in your application before the editor initializes [5]. This policy should ideally implement robust sanitization to prevent XSS attacks while allowing the necessary HTML structures required by your schema [5][7]. By defining a global defaultPolicy, you override the need for ProseMirror to create its own internal policy, satisfying the browser's security requirements while maintaining control over how HTML is processed [5][6].
Citations:
- 1: https://github.com/ProseMirror/prosemirror-view/blob/master/src/clipboard.ts
- 2: Trusted Types are breaking clipboard pasting ProseMirror/prosemirror#1485
- 3: TrustedTypes-Violation when pasting ProseMirror/prosemirror#1493
- 4: https://developer.mozilla.org/en-US/docs/Web/API/Trusted%5FTypes%5FAPI
- 5: Trusted-Types mechanism for clipboard handling is unsafe [Security issue] ProseMirror/prosemirror#1519
- 6: ProseMirror/prosemirror-view@4867a98
- 7: https://developer.mozilla.org/en-US/docs/Web/API/DOMParser/parseFromString
Keep this HTML parse Trusted Types-safe. DOMParser.parseFromString(html, 'text/html') is a Trusted Types sink, so paste can fail under require-trusted-types-for 'script'. Reuse the same clipboard parsing path ProseMirror uses, or make this helper accept TrustedHTML too.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/extension-hard-break/src/hard-break.ts` at line 8, Update the HTML
parsing in the hard-break helper around DOMParser usage to remain Trusted
Types-safe: reuse ProseMirror’s existing clipboard parsing path, or allow the
helper to accept and pass through TrustedHTML. Preserve the current
parsed-document behavior while ensuring paste works when Trusted Types
enforcement is enabled.
Source: MCP tools
Changes Overview
Preserve trailing hard breaks when HTML copied from a Tiptap editor is pasted back into a Tiptap editor.
Implementation Approach
The hard break extension now transforms ProseMirror clipboard HTML that contains
data-pm-slice. When an actual trailing<br>would otherwise be treated as ProseMirror's disposable trailing break, the transform appends a comment sentinel after it so the clipboard parser keeps the hardBreak node. Existing.ProseMirror-trailingBreakhack nodes are still ignored.Testing Done
corepack pnpm exec vitest run packages/extension-hard-break/__tests__/hard-break.spec.tscorepack pnpm exec vitest run packages/extension-hard-break/__tests__/hard-break.spec.ts packages/core/src/__tests__/transformPastedHTML.test.tscorepack pnpm exec prettier --check packages/extension-hard-break/src/hard-break.ts packages/extension-hard-break/__tests__/hard-break.spec.ts .changeset/clear-hard-break-paste.mdcorepack pnpm exec eslint packages/extension-hard-break/src/hard-break.ts packages/extension-hard-break/__tests__/hard-break.spec.tscorepack pnpm --filter @tiptap/extension-hard-break buildVerification Steps
Additional Notes
None.
AI Usage
Codex was used to inspect the ProseMirror clipboard behavior, draft the patch, and run local validation commands.
Checklist
Related Issues
Fixes #5418