Skip to content

Commit add6bdc

Browse files
authored
FIX: avoid dangling base64 images on rich editor (#36615)
When using the rich editor, when you paste an HTML containing a base64 image, the `dataImageUploader` ProseMirror plugin triggers uploads for each image and replaces it inline after completed. Uploads won't succeed if `authorized_extensions` doesn't allow any image extensions, and in this case the base64 images are kept in the document, an unintended outcome. This PR updates the `dataImageUploader` handler to replace any base64 images by an "image" string before starting any upload process.
1 parent 6325777 commit add6bdc

File tree

3 files changed

+64
-18
lines changed

3 files changed

+64
-18
lines changed

config/locales/client.en.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3158,6 +3158,8 @@ en:
31583158
add_to_grid: "Add to grid"
31593159
move_outside_grid: "Move outside grid"
31603160

3161+
image: "image"
3162+
31613163
grid_label: "This will be displayed as a grid"
31623164

31633165
image_scale_button: "Scale image to %{percent}%"

frontend/discourse/app/static/prosemirror/extensions/image.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
} from "pretty-text/upload-short-url";
55
import { ajax } from "discourse/lib/ajax";
66
import discourseDebounce from "discourse/lib/debounce";
7+
import { authorizesOneOrMoreImageExtensions } from "discourse/lib/uploads";
78
import { isNumeric } from "discourse/lib/utilities";
89
import { i18n } from "discourse-i18n";
910
import ImageNodeView from "../components/image-node-view";
@@ -351,6 +352,27 @@ const extension = {
351352
return;
352353
}
353354

355+
const staff = getContext().currentUser?.staff;
356+
// stop earlier if image extensions aren't allowed
357+
if (!authorizesOneOrMoreImageExtensions(staff, siteSettings)) {
358+
const tr = view.state.tr;
359+
const dataURIMap = dataImageUploader.getState(view.state);
360+
361+
dataURIMap.get(dataURI)?.forEach((pos) => {
362+
const node = view.state.doc.nodeAt(pos);
363+
tr.replaceWith(
364+
pos,
365+
pos + node.nodeSize,
366+
view.state.schema.text(i18n("composer.image"))
367+
);
368+
});
369+
370+
if (tr.docChanged) {
371+
view.dispatch(tr);
372+
}
373+
return;
374+
}
375+
354376
processingDataURIs.add(dataURI);
355377

356378
const mimeMatch = dataURI.match(/data:image\/([^;]+)/);

spec/system/composer/prosemirror_editor_spec.rb

Lines changed: 40 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1136,36 +1136,58 @@ def body(title)
11361136
expect(rich).to have_css("img[alt='img1'][data-orig-src]", count: 2)
11371137
end
11381138

1139-
it "avoids triggering upload when unauthorized" do
1140-
SiteSetting.authorized_extensions = ""
1139+
context "when unauthorized to upload" do
1140+
before { SiteSetting.authorized_extensions = "" }
11411141

1142-
open_composer
1142+
it "allows pasting text" do
1143+
cdp.allow_clipboard
11431144

1144-
cdp.allow_clipboard
1145-
cdp.copy_test_image
1146-
cdp.paste
1145+
open_composer
11471146

1148-
expect(rich).to have_no_css("img")
1147+
cdp.copy_paste("Just some text")
11491148

1150-
composer.toggle_rich_editor
1149+
expect(rich).to have_css("p", text: "Just some text")
11511150

1152-
expect(composer).to have_value("")
1153-
end
1151+
composer.toggle_rich_editor
11541152

1155-
it "allows pasting text when unauthorized to upload" do
1156-
SiteSetting.authorized_extensions = ""
1153+
expect(composer).to have_value("Just some text")
1154+
end
11571155

1158-
cdp.allow_clipboard
1156+
it "avoids triggering upload for paste" do
1157+
open_composer
11591158

1160-
open_composer
1159+
cdp.allow_clipboard
1160+
cdp.copy_test_image
1161+
cdp.paste
11611162

1162-
cdp.copy_paste("Just some text")
1163+
expect(rich).to have_no_css("img")
11631164

1164-
expect(rich).to have_css("p", text: "Just some text")
1165+
composer.toggle_rich_editor
11651166

1166-
composer.toggle_rich_editor
1167+
expect(composer).to have_value("")
1168+
end
1169+
1170+
it "avoids triggering upload for base64" do
1171+
valid_png_data_uri =
1172+
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg=="
11671173

1168-
expect(composer).to have_value("Just some text")
1174+
cdp.allow_clipboard
1175+
1176+
open_composer
1177+
1178+
html = <<~HTML
1179+
<img src="#{valid_png_data_uri}" alt="img1" width="100" height="100">
1180+
HTML
1181+
1182+
cdp.copy_paste(html, html: true)
1183+
1184+
expect(rich).to have_no_css("img")
1185+
expect(rich).to have_text("image")
1186+
1187+
composer.toggle_rich_editor
1188+
1189+
expect(composer).to have_value("image")
1190+
end
11691191
end
11701192

11711193
it "merges text with link marks created from parsing" do

0 commit comments

Comments
 (0)