Skip to content

Commit

Permalink
Sanitize HTML strings passed to buefy.toast().
Browse files Browse the repository at this point in the history
The buefy toast component does not sanitize HTML leaving it open
to XSS. This patch centralised all toast calls in the app to a util
function which sanitizes HTML strings before passing to toast().

Closes #357.
  • Loading branch information
knadh committed May 16, 2021
1 parent cf0c8f3 commit ed57ecc
Show file tree
Hide file tree
Showing 8 changed files with 34 additions and 90 deletions.
16 changes: 15 additions & 1 deletion frontend/src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,17 @@ dayjs.extend(relativeTime);

const reEmail = /(.+?)@(.+?)/ig;

const htmlEntities = {
'&': '&',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#39;',
'/': '&#x2F;',
'`': '&#x60;',
'=': '&#x3D;',
};

export default class Utils {
constructor(i18n) {
this.i18n = i18n;
Expand Down Expand Up @@ -67,6 +78,9 @@ export default class Utils {
return out.toFixed(2) + pfx;
}

// https://stackoverflow.com/a/12034334
escapeHTML = (html) => html.replace(/[&<>"'`=/]/g, (s) => htmlEntities[s]);

// UI shortcuts.
confirm = (msg, onConfirm, onCancel) => {
Dialog.confirm({
Expand Down Expand Up @@ -98,7 +112,7 @@ export default class Utils {

toast = (msg, typ, duration) => {
Toast.open({
message: msg,
message: this.escapeHTML(msg),
type: !typ ? 'is-success' : typ,
queue: false,
duration: duration || 2000,
Expand Down
6 changes: 1 addition & 5 deletions frontend/src/views/Import.vue
Original file line number Diff line number Diff line change
Expand Up @@ -278,11 +278,7 @@ export default Vue.extend({
// Post.
this.$api.importSubscribers(params).then(() => {
// On file upload, show a confirmation.
this.$buefy.toast.open({
message: this.$t('import.importStarted'),
type: 'is-success',
queue: false,
});
this.$utils.toast(this.$t('import.importStarted'));
// Start polling status.
this.pollStatus();
Expand Down
12 changes: 2 additions & 10 deletions frontend/src/views/ListForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -84,23 +84,15 @@ export default Vue.extend({
this.$api.createList(this.form).then((data) => {
this.$emit('finished');
this.$parent.close();
this.$buefy.toast.open({
message: this.$t('globals.messages.created', { name: data.name }),
type: 'is-success',
queue: false,
});
this.$utils.toast(this.$t('globals.messages.created', { name: data.name }));
});
},
updateList() {
this.$api.updateList({ id: this.data.id, ...this.form }).then((data) => {
this.$emit('finished');
this.$parent.close();
this.$buefy.toast.open({
message: this.$t('globals.messages.updated', { name: data.name }),
type: 'is-success',
queue: false,
});
this.$utils.toast(this.$t('globals.messages.updated', { name: data.name }));
});
},
},
Expand Down
6 changes: 1 addition & 5 deletions frontend/src/views/Lists.vue
Original file line number Diff line number Diff line change
Expand Up @@ -181,11 +181,7 @@ export default Vue.extend({
this.$api.deleteList(list.id).then(() => {
this.getLists();
this.$buefy.toast.open({
message: this.$t('globals.messages.deleted', { name: list.name }),
type: 'is-success',
queue: false,
});
this.$utils.toast(this.$t('globals.messages.deleted', { name: list.name }));
});
},
);
Expand Down
27 changes: 5 additions & 22 deletions frontend/src/views/SubscriberForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,7 @@ export default Vue.extend({
this.$api.createSubscriber(data).then((d) => {
this.$emit('finished');
this.$parent.close();
this.$buefy.toast.open({
message: this.$t('globals.messages.created', { name: d.name }),
type: 'is-success',
queue: false,
});
this.$utils.toast(this.$t('globals.messages.created', { name: d.name }));
});
},
Expand All @@ -150,11 +146,7 @@ export default Vue.extend({
this.$api.updateSubscriber(data).then((d) => {
this.$emit('finished');
this.$parent.close();
this.$buefy.toast.open({
message: this.$t('globals.messages.updated', { name: d.name }),
type: 'is-success',
queue: false,
});
this.$utils.toast(this.$t('globals.messages.updated', { name: d.name }));
});
},
Expand All @@ -164,21 +156,12 @@ export default Vue.extend({
try {
attribs = JSON.parse(str);
} catch (e) {
this.$buefy.toast.open({
message: `${this.$t('subscribers.invalidJSON')}: ${e.toString()}`,
type: 'is-danger',
duration: 3000,
queue: false,
});
this.$utils.toast(`${this.$t('subscribers.invalidJSON')}: ${e.toString()}`,
'is-danger', 3000);
return null;
}
if (attribs instanceof Array) {
this.$buefy.toast.open({
message: 'Attributes should be a map {} and not an array []',
type: 'is-danger',
duration: 3000,
queue: false,
});
this.$utils.toast('Attributes should be a map {} and not an array []', 'is-danger', 3000);
return null;
}
Expand Down
25 changes: 5 additions & 20 deletions frontend/src/views/Subscribers.vue
Original file line number Diff line number Diff line change
Expand Up @@ -355,11 +355,7 @@ export default Vue.extend({
this.$api.deleteSubscriber(sub.id).then(() => {
this.querySubscribers();
this.$buefy.toast.open({
message: this.$t('globals.messages.deleted', { name: sub.name }),
type: 'is-success',
queue: false,
});
this.$utils.toast(this.$t('globals.messages.deleted', { name: sub.name }));
});
},
);
Expand Down Expand Up @@ -406,11 +402,7 @@ export default Vue.extend({
.then(() => {
this.querySubscribers();
this.$buefy.toast.open({
message: this.$t('subscribers.subscribersDeleted', { num: this.numSelectedSubscribers }),
type: 'is-success',
queue: false,
});
this.$utils.toast(this.$t('subscribers.subscribersDeleted', { num: this.numSelectedSubscribers }));
});
};
} else {
Expand All @@ -422,11 +414,8 @@ export default Vue.extend({
}).then(() => {
this.querySubscribers();
this.$buefy.toast.open({
message: this.$t('subscribers.subscribersDeleted', { num: this.numSelectedSubscribers }),
type: 'is-success',
queue: false,
});
this.$utils.toast(this.$t('subscribers.subscribersDeleted',
{ num: this.numSelectedSubscribers }));
});
};
}
Expand Down Expand Up @@ -454,11 +443,7 @@ export default Vue.extend({
fn(data).then(() => {
this.querySubscribers();
this.$buefy.toast.open({
message: this.$t('subscribers.listChangeApplied'),
type: 'is-success',
queue: false,
});
this.$utils.toast(this.$t('subscribers.listChangeApplied'));
});
},
},
Expand Down
12 changes: 2 additions & 10 deletions frontend/src/views/TemplateForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,7 @@ export default Vue.extend({
this.$api.createTemplate(data).then((d) => {
this.$emit('finished');
this.$parent.close();
this.$buefy.toast.open({
message: this.$t('globals.messages.created', { name: d.name }),
type: 'is-success',
queue: false,
});
this.$utils.toast(this.$t('globals.messages.created', { name: d.name }));
});
},
Expand All @@ -116,11 +112,7 @@ export default Vue.extend({
this.$api.updateTemplate(data).then((d) => {
this.$emit('finished');
this.$parent.close();
this.$buefy.toast.open({
message: `'${d.name}' updated`,
type: 'is-success',
queue: false,
});
this.$utils.toast(`'${d.name}' updated`);
});
},
},
Expand Down
20 changes: 3 additions & 17 deletions frontend/src/views/Templates.vue
Original file line number Diff line number Diff line change
Expand Up @@ -143,35 +143,21 @@ export default Vue.extend({
this.$api.createTemplate(data).then((d) => {
this.$api.getTemplates();
this.$emit('finished');
this.$buefy.toast.open({
message: `'${d.name}' created`,
type: 'is-success',
queue: false,
});
this.$utils.toast(`'${d.name}' created`);
});
},
makeTemplateDefault(tpl) {
this.$api.makeTemplateDefault(tpl.id).then(() => {
this.$api.getTemplates();
this.$buefy.toast.open({
message: this.$t('globals.messages.created', { name: tpl.name }),
type: 'is-success',
queue: false,
});
this.$utils.toast(this.$t('globals.messages.created', { name: tpl.name }));
});
},
deleteTemplate(tpl) {
this.$api.deleteTemplate(tpl.id).then(() => {
this.$api.getTemplates();
this.$buefy.toast.open({
message: this.$t('globals.messages.deleted', { name: tpl.name }),
type: 'is-success',
queue: false,
});
this.$utils.toast(this.$t('globals.messages.deleted', { name: tpl.name }));
});
},
},
Expand Down

0 comments on commit ed57ecc

Please sign in to comment.