From 052b07a7f0335566e771c7292ed4ee4a31f4afb3 Mon Sep 17 00:00:00 2001 From: Renato Atilio Date: Tue, 28 Oct 2025 12:21:04 -0300 Subject: [PATCH] FIX: avoid ProseMirror handling paste when unauthorized (#35640) When `authorizesOneOrMoreExtensions` is `false`, we don't call `ComposerUpload#setup` to add the paste event listener, which originally handles the `preventDefault`: https://github.com/discourse/discourse/blob/d1e1c02fcb0cbc6722280afb6e086cb6a8b53882/frontend/discourse/app/components/composer-editor.gjs#L274-L276 https://github.com/discourse/discourse/blob/d1e1c02fcb0cbc6722280afb6e086cb6a8b53882/frontend/discourse/app/lib/uppy/composer-upload.js#L151-L153 https://github.com/discourse/discourse/blob/d1e1c02fcb0cbc6722280afb6e086cb6a8b53882/frontend/discourse/app/lib/uppy/composer-upload.js#L566-L569 This makes it so ProseMirror handles paste events itself, which leads to base64 data src images. This PR adds a custom paste handler for `prosemirror-editor` that prevents default in this case. --- .../components/prosemirror-editor.gjs | 15 ++++++++++++ .../composer/prosemirror_editor_spec.rb | 23 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/frontend/discourse/app/static/prosemirror/components/prosemirror-editor.gjs b/frontend/discourse/app/static/prosemirror/components/prosemirror-editor.gjs index cf1742cdd0d..fa5ce1641ca 100644 --- a/frontend/discourse/app/static/prosemirror/components/prosemirror-editor.gjs +++ b/frontend/discourse/app/static/prosemirror/components/prosemirror-editor.gjs @@ -25,6 +25,7 @@ import { EditorView } from "prosemirror-view"; import { getExtensions } from "discourse/lib/composer/rich-editor-extensions"; import { bind } from "discourse/lib/decorators"; import { i18n } from "discourse-i18n"; +import { authorizesOneOrMoreExtensions } from "../../../lib/uploads"; import { buildCommands, buildCustomState } from "../core/commands"; import { buildInputRules } from "../core/inputrules"; import { buildKeymap } from "../core/keymap"; @@ -77,6 +78,7 @@ export default class ProsemirrorEditor extends Component { @service site; @service siteSettings; @service appEvents; + @service currentUser; schema = createSchema(this.extensions, this.args.includeDefault); view; @@ -214,6 +216,19 @@ export default class ProsemirrorEditor extends Component { next(() => this.args.focusOut?.()); return false; }, + paste: (view, event) => { + // When !authorizesOneOrMoreExtensions, we don't ComposerUpload#setup, + // which is originally responsible for preventDefault. + if ( + event.clipboardData.files && + !authorizesOneOrMoreExtensions( + this.currentUser.staff, + this.siteSettings + ) + ) { + event.preventDefault(); + } + }, drop: (view, event) => { if ( [...event.dataTransfer.items].some((item) => item.kind === "file") diff --git a/spec/system/composer/prosemirror_editor_spec.rb b/spec/system/composer/prosemirror_editor_spec.rb index 4b475d09d66..1adb82ef382 100644 --- a/spec/system/composer/prosemirror_editor_spec.rb +++ b/spec/system/composer/prosemirror_editor_spec.rb @@ -1136,6 +1136,29 @@ describe "Composer - ProseMirror editor", type: :system do expect(rich).to have_css("img[alt='img1'][data-orig-src]", count: 2) end + it "avoids triggering upload when unauthorized" do + SiteSetting.authorized_extensions = "" + + valid_png_data_uri = + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==" + + cdp.allow_clipboard + + open_composer + + html = <<~HTML + img1 + HTML + + cdp.copy_paste(html, html: true) + + expect(rich).to have_no_css("img") + + composer.toggle_rich_editor + + expect(composer).to have_value("") + end + it "merges text with link marks created from parsing" do cdp.allow_clipboard open_composer