Skip to content

fix: sanitize clipboard and notification HTML to prevent Qt6 StyledText crash - #7381

Open
markbus-ai wants to merge 1 commit into
basecamp:quattrofrom
markbus-ai:quattro
Open

fix: sanitize clipboard and notification HTML to prevent Qt6 StyledText crash#7381
markbus-ai wants to merge 1 commit into
basecamp:quattrofrom
markbus-ai:quattro

Conversation

@markbus-ai

Copy link
Copy Markdown
Contributor

Problem

Clipboard entries from browsers/messaging apps (WhatsApp, etc.) often contain raw HTML that triggers a Qt6 bug in QQuickTextPrivate::updateLayout() when rendered with Text.StyledText. The crash occurs because the nbImages counter gets out of sync when invalid <img> tags are deleted.

Root Cause

Upstream Qt6 bug in qquickstyledtext.cpp: when an <img> tag has an invalid URL, the code deletes the image object but does not increment nbImages. This causes the counter to desynchronize, leading to invalid memory access when subsequent <img> tags are processed.

Fix

This PR adds sanitization at two layers:

  1. Clipboard: Strip HTML tags and decode entities before storing entries. Existing entries are sanitized on load via parseHistory().

  2. Notifications: Improve sanitizeBody() to decode HTML entities before stripping <img> tags, catching encoded variants like &lt;img&gt;.

Testing

  • Copy HTML content from WhatsApp/browser and verify clipboard stores plain text
  • Send notification with HTML body and verify no crash
  • Verify existing clipboard history is sanitized on load

Upstream

A separate PR will be submitted to Qt6 to fix the root cause (nbImages++ missing in qquickstyledtext.cpp).

…xt crash

Clipboard entries from browsers/messaging apps (WhatsApp, etc.) often
contain raw HTML that triggers a Qt6 bug in QQuickTextPrivate::updateLayout()
when rendered with Text.StyledText. The crash occurs because the nbImages
counter gets out of sync when invalid img tags are deleted.

This fix adds sanitization at two layers:

1. Clipboard: Strip HTML tags and decode entities before storing entries.
   Existing entries are sanitized on load via parseHistory().

2. Notifications: Improve sanitizeBody() to decode HTML entities before
   stripping img tags, catching encoded variants like &lt;img&gt;.

The root cause is an upstream Qt6 bug (nbImages++ missing in
qquickstyledtext.cpp). A separate PR will be submitted to Qt.
Copilot AI balanced review requested due to automatic review settings August 18, 2026 12:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR hardens text sanitization for notifications and clipboard history by decoding common HTML entities and stripping HTML tags (especially <img>), aiming to prevent rendering crashes and unwanted rich content.

Changes:

  • Decode common HTML entities before sanitization to catch encoded tags.
  • Strip <img> tags in notification bodies more aggressively.
  • Sanitize clipboard text by decoding entities, stripping tags, and normalizing whitespace.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 6 comments.

File Description
shell/plugins/notifications/NotificationLogic.js Decodes HTML entities then strips <img> tags from notification bodies.
shell/plugins/clipboard/ClipboardHistory.js Introduces sanitizeText() and applies it to normalized clipboard “text” entries.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread shell/plugins/clipboard/ClipboardHistory.js
Comment on lines +13 to +16
text = text
.replace(/&lt;/gi, "<")
.replace(/&gt;/gi, ">")
.replace(/&amp;/gi, "&")
Comment on lines +5 to +15
function sanitizeText(text) {
var s = String(text || "")

// Decode HTML entities first
s = s
.replace(/&lt;/gi, "<")
.replace(/&gt;/gi, ">")
.replace(/&amp;/gi, "&")
.replace(/&quot;/gi, '"')
.replace(/&#39;/gi, "'")
.replace(/&nbsp;/gi, " ")
Comment on lines +17 to +18
// Strip HTML tags (including self-closing and malformed)
s = s.replace(/<[^>]+>/g, "")
Comment on lines 8 to +18
function sanitizeBody(body, app, appIcon) {
var text = String(body || "").replace(/<img[^>]*>/gi, "")
var text = String(body || "")

// Decode HTML entities first so we can catch encoded img tags
// e.g. &lt;img src="..."&gt; -> <img src="...">
text = text
.replace(/&lt;/gi, "<")
.replace(/&gt;/gi, ">")
.replace(/&amp;/gi, "&")
.replace(/&quot;/gi, '"')
.replace(/&#39;/gi, "'")
// Strip all img tags aggressively (handles multiline, malformed, etc.)
// This catches: <img ...>, <IMG ...>, <img/>, <img ... />, and any variation
// The \b word boundary ensures we match "img" but not "image" or other tags
text = text.replace(/<img\b[^>]*>/gi, "")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants