SECURITY: properly escape user input

This would only cause an issue with CSP disabled.
This commit is contained in:
Joffrey JAFFEUX 2022-01-05 09:41:17 +01:00
parent 3adf5b8300
commit bd83fb0625
1 changed files with 28 additions and 25 deletions

View File

@ -1,3 +1,4 @@
import { escapeExpression } from "discourse/lib/utilities";
import showModal from "discourse/lib/show-modal"; import showModal from "discourse/lib/show-modal";
import { withPluginApi } from "discourse/lib/plugin-api"; import { withPluginApi } from "discourse/lib/plugin-api";
import { later, debounce } from "@ember/runloop"; import { later, debounce } from "@ember/runloop";
@ -46,14 +47,14 @@ function buildSelect(key, placeholder) {
if (placeholder.description) { if (placeholder.description) {
addSelectOption(select, { addSelectOption(select, {
value: "none", value: "none",
description: placeholder.description description: placeholder.description,
}); });
} }
placeholder.defaults.forEach(value => placeholder.defaults.forEach((value) =>
addSelectOption(select, { addSelectOption(select, {
value, value,
selected: placeholder.default === value selected: placeholder.default === value,
}) })
); );
@ -64,7 +65,7 @@ export default {
name: "discourse-placeholder-theme-component", name: "discourse-placeholder-theme-component",
initialize() { initialize() {
withPluginApi("0.8.7", api => { withPluginApi("0.8.7", (api) => {
api.decorateCooked( api.decorateCooked(
($cooked, postWidget) => { ($cooked, postWidget) => {
if (!postWidget) return; if (!postWidget) return;
@ -74,9 +75,9 @@ export default {
const placeholders = {}; const placeholders = {};
function processChange(inputEvent) { function processChange(inputEvent) {
const value = inputEvent.target.value; const value = escapeExpression(inputEvent.target.value);
const key = inputEvent.target.dataset.key; const key = inputEvent.target.dataset.key;
const placeholder = placeholders[inputEvent.target.dataset.key]; const placeholder = placeholders[key];
const placeholderIdentifier = `${postIdentifier}${key}`; const placeholderIdentifier = `${postIdentifier}${key}`;
if (value) { if (value) {
@ -103,7 +104,7 @@ export default {
let replaced = false; let replaced = false;
let newInnnerHTML = elem.innerHTML; let newInnnerHTML = elem.innerHTML;
mapping.forEach(m => { mapping.forEach((m) => {
if ( if (
m.pattern !== m.pattern !==
`${placeholder.delimiter}${key}${placeholder.delimiter}` `${placeholder.delimiter}${key}${placeholder.delimiter}`
@ -136,7 +137,7 @@ export default {
const keys = Object.keys(placeholders); const keys = Object.keys(placeholders);
const pattern = keys const pattern = keys
.map(key => { .map((key) => {
const placeholder = placeholders[key]; const placeholder = placeholders[key];
return `(${placeholder.delimiter}${key}${placeholder.delimiter})`; return `(${placeholder.delimiter}${key}${placeholder.delimiter})`;
}) })
@ -152,7 +153,7 @@ export default {
mappings[index].push({ mappings[index].push({
pattern: match[0], pattern: match[0],
position: match.index, position: match.index,
length: match[0].length length: match[0].length,
}); });
} }
}); });
@ -163,7 +164,7 @@ export default {
processPlaceholders(placeholders, $cooked, mappings); processPlaceholders(placeholders, $cooked, mappings);
// trigger fake event to setup initial state // trigger fake event to setup initial state
Object.keys(placeholders).forEach(placeholderKey => { Object.keys(placeholders).forEach((placeholderKey) => {
const placeholder = placeholders[placeholderKey]; const placeholder = placeholders[placeholderKey];
const placeholderIdentifier = `${postIdentifier}${placeholderKey}`; const placeholderIdentifier = `${postIdentifier}${placeholderKey}`;
const value = const value =
@ -171,12 +172,12 @@ export default {
processChange({ processChange({
target: { target: {
value, value: escapeExpression(value),
dataset: { dataset: {
key: placeholderKey, key: placeholderKey,
delimiter: placeholder.delimiter delimiter: placeholder.delimiter,
} },
} },
}); });
}); });
} }
@ -186,13 +187,15 @@ export default {
".d-wrap[data-wrap=placeholder]:not(.placeholdered)" ".d-wrap[data-wrap=placeholder]:not(.placeholdered)"
); );
placeholderNodes.forEach(elem => { placeholderNodes.forEach((elem) => {
const dataKey = elem.dataset.key; const dataKey = elem.dataset.key;
if (!dataKey) return; if (!dataKey) return;
const placeholderIdentifier = `${postIdentifier}${dataKey}`; const placeholderIdentifier = `${postIdentifier}${dataKey}`;
const valueFromCookie = cookie(placeholderIdentifier); const valueFromCookie = escapeExpression(
cookie(placeholderIdentifier)
);
const defaultValues = (elem.dataset.defaults || "") const defaultValues = (elem.dataset.defaults || "")
.split(",") .split(",")
.filter(Boolean); .filter(Boolean);
@ -201,7 +204,7 @@ export default {
default: valueFromCookie || elem.dataset.default, default: valueFromCookie || elem.dataset.default,
defaults: defaultValues, defaults: defaultValues,
delimiter: elem.dataset.delimiter || DELIMITER, delimiter: elem.dataset.delimiter || DELIMITER,
description: elem.dataset.description description: elem.dataset.description,
}; };
const span = document.createElement("span"); const span = document.createElement("span");
@ -225,10 +228,10 @@ export default {
}); });
$cooked $cooked
.on("input", ".discourse-placeholder-value", inputEvent => .on("input", ".discourse-placeholder-value", (inputEvent) =>
debounce(this, processChange, inputEvent, 150) debounce(this, processChange, inputEvent, 150)
) )
.on("change", ".discourse-placeholder-select", inputEvent => .on("change", ".discourse-placeholder-select", (inputEvent) =>
debounce(this, processChange, inputEvent, 150) debounce(this, processChange, inputEvent, 150)
); );
@ -241,7 +244,7 @@ export default {
return { return {
action: "insertPlaceholder", action: "insertPlaceholder",
icon: "file", icon: "file",
label: themePrefix("toolbar.builder") label: themePrefix("toolbar.builder"),
}; };
}); });
@ -252,12 +255,12 @@ export default {
insertPlaceholder() { insertPlaceholder() {
showModal("discourse-placeholder-builder", { showModal("discourse-placeholder-builder", {
model: { model: {
toolbarEvent: this.toolbarEvent toolbarEvent: this.toolbarEvent,
} },
}); });
} },
} },
}); });
}); });
} },
}; };