SECURITY: properly escape user input
This would only cause an issue with CSP disabled.
This commit is contained in:
parent
3adf5b8300
commit
bd83fb0625
|
@ -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,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue