chore: add escapeHtml option for secure HTML escaping in notifications

This commit is contained in:
Younes ENNAJI
2024-09-19 07:05:11 +01:00
parent 74a71b36dc
commit 889fc4701e
6 changed files with 79 additions and 8 deletions
+28 -2
View File
@@ -19,6 +19,7 @@ export default class FlasherPlugin extends AbstractPlugin {
direction: 'top',
rtl: false,
style: {} as Properties,
escapeHtml: false,
}
constructor(theme: Theme) {
@@ -31,11 +32,12 @@ export default class FlasherPlugin extends AbstractPlugin {
const render = () =>
envelopes.forEach((envelope) => {
// @ts-expect-error
const typeTimeout = this.options.timeout ?? this.options.timeouts[envelope.type] ?? 5000;
const typeTimeout = this.options.timeout ?? this.options.timeouts[envelope.type] ?? 5000
const options = {
...this.options,
...envelope.options,
timeout: envelope.options.timeout ?? typeTimeout,
escapeHtml: (envelope.options.escapeHtml ?? this.options.escapeHtml) as boolean,
}
this.addToContainer(this.createContainer(options), envelope, options)
@@ -64,7 +66,12 @@ export default class FlasherPlugin extends AbstractPlugin {
return container
}
private addToContainer(container: HTMLDivElement, envelope: Envelope, options: { direction: string, timeout: number, fps: number, rtl: boolean }): void {
private addToContainer(container: HTMLDivElement, envelope: Envelope, options: { direction: string, timeout: number, fps: number, rtl: boolean, escapeHtml: boolean }): void {
if (options.escapeHtml) {
envelope.title = this.escapeHtml(envelope.title)
envelope.message = this.escapeHtml(envelope.message)
}
const notification = this.stringToHTML(this.theme.render(envelope))
notification.classList.add(...`fl-container${options.rtl ? ' fl-rtl' : ''}`.split(' '))
@@ -126,4 +133,23 @@ export default class FlasherPlugin extends AbstractPlugin {
template.innerHTML = str.trim()
return template.content.firstElementChild as HTMLElement
}
private escapeHtml(str: string | null | undefined): string {
if (str == null) {
return ''
}
return str.replace(/[&<>"'`=\/]/g, (char) => {
return {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
'\'': '&#39;',
'`': '&#96;',
'=': '&#61;',
'/': '&#47;',
}[char] as string
})
}
}
+1
View File
@@ -15,4 +15,5 @@ export default class FlasherPlugin extends AbstractPlugin {
}): void;
private removeNotification;
private stringToHTML;
private escapeHtml;
}
+24 -2
View File
@@ -92,14 +92,15 @@ class FlasherPlugin extends AbstractPlugin {
direction: 'top',
rtl: false,
style: {},
escapeHtml: false,
};
this.theme = theme;
}
renderEnvelopes(envelopes) {
const render = () => envelopes.forEach((envelope) => {
var _a, _b, _c;
var _a, _b, _c, _d;
const typeTimeout = (_b = (_a = this.options.timeout) !== null && _a !== void 0 ? _a : this.options.timeouts[envelope.type]) !== null && _b !== void 0 ? _b : 5000;
const options = Object.assign(Object.assign(Object.assign({}, this.options), envelope.options), { timeout: (_c = envelope.options.timeout) !== null && _c !== void 0 ? _c : typeTimeout });
const options = Object.assign(Object.assign(Object.assign({}, this.options), envelope.options), { timeout: (_c = envelope.options.timeout) !== null && _c !== void 0 ? _c : typeTimeout, escapeHtml: ((_d = envelope.options.escapeHtml) !== null && _d !== void 0 ? _d : this.options.escapeHtml) });
this.addToContainer(this.createContainer(options), envelope, options);
});
document.readyState === 'loading' ? document.addEventListener('DOMContentLoaded', render) : render();
@@ -121,6 +122,10 @@ class FlasherPlugin extends AbstractPlugin {
}
addToContainer(container, envelope, options) {
var _a;
if (options.escapeHtml) {
envelope.title = this.escapeHtml(envelope.title);
envelope.message = this.escapeHtml(envelope.message);
}
const notification = this.stringToHTML(this.theme.render(envelope));
notification.classList.add(...`fl-container${options.rtl ? ' fl-rtl' : ''}`.split(' '));
options.direction === 'bottom' ? container.append(notification) : container.prepend(notification);
@@ -170,6 +175,23 @@ class FlasherPlugin extends AbstractPlugin {
template.innerHTML = str.trim();
return template.content.firstElementChild;
}
escapeHtml(str) {
if (str == null) {
return '';
}
return str.replace(/[&<>"'`=\/]/g, (char) => {
return {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
'\'': '&#39;',
'`': '&#96;',
'=': '&#61;',
'/': '&#47;',
}[char];
});
}
}
class Flasher extends AbstractPlugin {
+24 -2
View File
@@ -98,14 +98,15 @@
direction: 'top',
rtl: false,
style: {},
escapeHtml: false,
};
this.theme = theme;
}
renderEnvelopes(envelopes) {
const render = () => envelopes.forEach((envelope) => {
var _a, _b, _c;
var _a, _b, _c, _d;
const typeTimeout = (_b = (_a = this.options.timeout) !== null && _a !== void 0 ? _a : this.options.timeouts[envelope.type]) !== null && _b !== void 0 ? _b : 5000;
const options = Object.assign(Object.assign(Object.assign({}, this.options), envelope.options), { timeout: (_c = envelope.options.timeout) !== null && _c !== void 0 ? _c : typeTimeout });
const options = Object.assign(Object.assign(Object.assign({}, this.options), envelope.options), { timeout: (_c = envelope.options.timeout) !== null && _c !== void 0 ? _c : typeTimeout, escapeHtml: ((_d = envelope.options.escapeHtml) !== null && _d !== void 0 ? _d : this.options.escapeHtml) });
this.addToContainer(this.createContainer(options), envelope, options);
});
document.readyState === 'loading' ? document.addEventListener('DOMContentLoaded', render) : render();
@@ -127,6 +128,10 @@
}
addToContainer(container, envelope, options) {
var _a;
if (options.escapeHtml) {
envelope.title = this.escapeHtml(envelope.title);
envelope.message = this.escapeHtml(envelope.message);
}
const notification = this.stringToHTML(this.theme.render(envelope));
notification.classList.add(...`fl-container${options.rtl ? ' fl-rtl' : ''}`.split(' '));
options.direction === 'bottom' ? container.append(notification) : container.prepend(notification);
@@ -176,6 +181,23 @@
template.innerHTML = str.trim();
return template.content.firstElementChild;
}
escapeHtml(str) {
if (str == null) {
return '';
}
return str.replace(/[&<>"'`=\/]/g, (char) => {
return {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
'\'': '&#39;',
'`': '&#96;',
'=': '&#61;',
'/': '&#47;',
}[char];
});
}
}
class Flasher extends AbstractPlugin {
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long