FEATURE: allows html tooltips (#6665)

This commit is contained in:
Joffrey JAFFEUX 2018-11-26 11:15:23 +01:00 committed by GitHub
parent e47b478b83
commit 3453707784
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 57 additions and 18 deletions

View File

@ -3,10 +3,13 @@ import { escapeExpression } from "discourse/lib/utilities";
const fadeSpeed = 300;
const tooltipID = "#discourse-tooltip";
export function showTooltip() {
const $this = $(this);
export function showTooltip($this) {
const $parent = $this.offsetParent();
const content = escapeExpression($this.attr("data-tooltip"));
// html tooltip are risky try your best to sanitize anything
// displayed as html to avoid XSS attacks
const content = $this.attr("data-tooltip")
? escapeExpression($this.attr("data-tooltip"))
: $this.attr("data-html-tooltip") || "";
const retina =
window.devicePixelRatio && window.devicePixelRatio > 1
? "class='retina'"
@ -19,7 +22,7 @@ export function showTooltip() {
hideTooltip(tooltipID);
$(this).after(`
$this.after(`
<div id="discourse-tooltip" ${retina}>
<div class="tooltip-pointer"></div>
<div class="tooltip-content">${content}</div>
@ -74,7 +77,9 @@ export function hideTooltip() {
export function registerTooltip(jqueryContext) {
if (jqueryContext.length) {
jqueryContext.off("click").on("click", showTooltip);
jqueryContext
.off("click")
.on("click", event => showTooltip($(event.currentTarget)));
}
}

View File

@ -4,22 +4,56 @@ import { registerTooltip } from "discourse/lib/tooltip";
QUnit.module("lib:tooltip", {
beforeEach() {
fixture().html(
"<a class='test-link' data-tooltip='XSS<s onmouseover\=alert(document.domain)>XSS'>test</a>"
`
<a class='test-text-link' data-tooltip='XSS<s onmouseover\=alert(document.domain)>XSS'>test</a>
<a class='test-html-link' data-html-tooltip='<p>test</p>'>test</a>
`
);
}
});
QUnit.test("it prevents XSS injection", assert => {
const $testLink = fixture(".test-link");
registerTooltip($testLink);
$testLink.click();
QUnit.test("text support", async assert => {
const $testTextLink = fixture(".test-text-link");
registerTooltip($testTextLink);
andThen(() => {
assert.equal(
fixture(".tooltip-content")
.html()
.trim(),
"XSS&lt;s onmouseover=alert(document.domain)&gt;XSS"
);
});
await $testTextLink.click();
assert.equal(
fixture(".tooltip-content")
.html()
.trim(),
"XSS&lt;s onmouseover=alert(document.domain)&gt;XSS",
"it prevents XSS injection"
);
assert.equal(
fixture(".tooltip-content")
.text()
.trim(),
"XSS<s onmouseover=alert(document.domain)>XSS",
"it returns content as plain text"
);
});
QUnit.test("html support", async assert => {
const $testHtmlLink = fixture(".test-html-link");
registerTooltip($testHtmlLink);
await $testHtmlLink.click();
assert.equal(
fixture(".tooltip-content")
.html()
.trim(),
"<p>test</p>",
"it doesnt escape HTML"
);
assert.equal(
fixture(".tooltip-content")
.text()
.trim(),
"test",
"it returns content as plain text"
);
});