FIX: prevent lightbox images from double escaping titles (#23458)

This change fixes an issue where lightbox images are showing escaped text in the link title and lightbox image description area.
This commit is contained in:
David Battersby 2023-09-13 14:33:08 +08:00 committed by GitHub
parent 267e8ebaa6
commit 6e2b484f12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 6 additions and 28 deletions

View File

@ -1,7 +1,4 @@
import { import { postRNWebviewMessage } from "discourse/lib/utilities";
escapeExpression,
postRNWebviewMessage,
} from "discourse/lib/utilities";
import I18n from "I18n"; import I18n from "I18n";
import User from "discourse/models/user"; import User from "discourse/models/user";
@ -119,7 +116,7 @@ export default function lightbox(elem, siteSettings) {
titleSrc(item) { titleSrc(item) {
const href = item.el.data("download-href") || item.src; const href = item.el.data("download-href") || item.src;
let src = [ let src = [
escapeExpression(item.el.attr("title")), item.el.attr("title"),
$("span.informations", item.el).text(), $("span.informations", item.el).text(),
]; ];
if ( if (

View File

@ -1,5 +1,4 @@
import { SELECTORS } from "./constants"; import { SELECTORS } from "./constants";
import { escapeExpression } from "discourse/lib/utilities";
import { htmlSafe } from "@ember/template"; import { htmlSafe } from "@ember/template";
export async function processHTML({ container, selector, clickTarget }) { export async function processHTML({ container, selector, clickTarget }) {
@ -41,7 +40,7 @@ export async function processHTML({ container, selector, clickTarget }) {
null; null;
const _title = const _title =
item.title || item.alt || innerImage.title || innerImage.alt || null; item.title || item.alt || innerImage.title || innerImage.alt || "";
const _aspectRatio = const _aspectRatio =
item.dataset?.aspectRatio || item.dataset?.aspectRatio ||
@ -67,7 +66,7 @@ export async function processHTML({ container, selector, clickTarget }) {
fullsizeURL: encodeURI(_fullsizeURL), fullsizeURL: encodeURI(_fullsizeURL),
smallURL: encodeURI(_smallURL), smallURL: encodeURI(_smallURL),
downloadURL: encodeURI(_downloadURL), downloadURL: encodeURI(_downloadURL),
title: escapeExpression(_title), title: _title,
fileDetails: _fileDetails, fileDetails: _fileDetails,
dominantColor: _dominantColor, dominantColor: _dominantColor,
aspectRatio: _aspectRatio, aspectRatio: _aspectRatio,

View File

@ -127,24 +127,6 @@ module("Unit | lib | Experimental lightbox | processHTML()", function () {
assert.strictEqual(items[0].title, ""); assert.strictEqual(items[0].title, "");
}); });
test("correctly escapes the title", async function (assert) {
const container = wrap.cloneNode(true);
container
.querySelector("a")
.setAttribute("title", `"><\x00script>javascript:alert(1)</script>`);
const { items } = await processHTML({
container,
selector,
});
assert.strictEqual(
items[0].title,
`&quot;&gt;&lt;\x00script&gt;javascript:alert(1)&lt;/script&gt;`
);
});
test("handles missing aspect ratio", async function (assert) { test("handles missing aspect ratio", async function (assert) {
const container = wrap.cloneNode(true); const container = wrap.cloneNode(true);

View File

@ -292,7 +292,7 @@ class CookedPostProcessor
informations = +"#{original_width}×#{original_height}" informations = +"#{original_width}×#{original_height}"
informations << " #{upload.human_filesize}" if upload informations << " #{upload.human_filesize}" if upload
a["title"] = CGI.escapeHTML(img["title"] || img["alt"] || filename) a["title"] = img["title"] || img["alt"] || filename
meta.add_child create_icon_node("far-image") meta.add_child create_icon_node("far-image")
meta.add_child create_span_node("filename", a["title"]) meta.add_child create_span_node("filename", a["title"])

View File

@ -649,7 +649,7 @@ RSpec.describe CookedPostProcessor do
cpp.post_process cpp.post_process
expect(cpp.html).to match_html <<~HTML expect(cpp.html).to match_html <<~HTML
<p><div class="lightbox-wrapper"><a class="lightbox" href="//test.localhost/subfolder#{upload.url}" data-download-href="//test.localhost/subfolder/#{upload_path}/#{upload.sha1}" title="&amp;gt;&amp;lt;img src=x onerror=alert(&amp;#39;haha&amp;#39;)&amp;gt;.png"><img src="//test.localhost/subfolder/#{upload_path}/optimized/1X/#{upload.sha1}_#{OptimizedImage::VERSION}_690x788.png" width="690" height="788"><div class="meta"><svg class="fa d-icon d-icon-far-image svg-icon" aria-hidden="true"><use href="#far-image"></use></svg><span class="filename">&amp;gt;&amp;lt;img src=x onerror=alert(&amp;#39;haha&amp;#39;)&amp;gt;.png</span><span class="informations">1750×2000 1.21 KB</span><svg class="fa d-icon d-icon-discourse-expand svg-icon" aria-hidden="true"><use href="#discourse-expand"></use></svg></div></a></div></p> <p><div class="lightbox-wrapper"><a class="lightbox" href="//test.localhost/subfolder#{upload.url}" data-download-href="//test.localhost/subfolder/#{upload_path}/#{upload.sha1}" title="><img src=x onerror=alert('haha')>.png"><img src="//test.localhost/subfolder/#{upload_path}/optimized/1X/#{upload.sha1}_#{OptimizedImage::VERSION}_690x788.png" width="690" height="788"><div class="meta"><svg class="fa d-icon d-icon-far-image svg-icon" aria-hidden="true"><use href="#far-image"></use></svg><span class="filename">&gt;&lt;img src=x onerror=alert('haha')&gt;.png</span><span class="informations">1750×2000 1.21 KB</span><svg class="fa d-icon d-icon-discourse-expand svg-icon" aria-hidden="true"><use href="#discourse-expand"></use></svg></div></a></div></p>
HTML HTML
end end
end end