summaryrefslogtreecommitdiff
path: root/web-ui/app/js
diff options
context:
space:
mode:
Diffstat (limited to 'web-ui/app/js')
-rw-r--r--web-ui/app/js/helpers/sanitizer.js108
-rw-r--r--web-ui/app/js/helpers/view_helper.js37
-rw-r--r--web-ui/app/js/mail_view/ui/mail_view.js1
-rw-r--r--web-ui/app/js/main.js2
4 files changed, 115 insertions, 33 deletions
diff --git a/web-ui/app/js/helpers/sanitizer.js b/web-ui/app/js/helpers/sanitizer.js
new file mode 100644
index 00000000..eea1f0f7
--- /dev/null
+++ b/web-ui/app/js/helpers/sanitizer.js
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2016 ThoughtWorks, Inc.
+ *
+ * Pixelated is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Pixelated is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Pixelated. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+define(['DOMPurify', 'he'], function (DOMPurify, he) {
+ 'use strict';
+
+ /**
+ * Sanitizes a mail body to safe-to-display HTML
+ */
+ var sanitizer = {};
+
+ /**
+ * Adds html line breaks to a plaintext with line breaks (incl carriage return)
+ *
+ * @param {string} textPlainBody Plaintext input
+ * @returns {string} Plaintext with HTML line breals (<br/>)
+ */
+ sanitizer.addLineBreaks = function (textPlainBody) {
+ return textPlainBody.replace(/(\r)?\n/g, '<br/>').replace(/(&#xD;)?&#xA;/g, '<br/>');
+ };
+
+ /**
+ * Runs a given dirty body through DOMPurify, thereby removing
+ * potentially hazardous XSS attacks. Please be advised that this
+ * will not act as a privacy leak prevention. Contained contents
+ * will still point to remote sources.
+ *
+ * For future reference: Running DOMPurify with these parameters
+ * can help mitigate some of the most widely used privacy leaks.
+ * FORBID_TAGS: ['style', 'svg', 'audio', 'video', 'math'],
+ * FORBID_ATTR: ['src']
+ *
+ * @param {string} dirtyBody The unsanitized string
+ * @return {string} Safe-to-display HTML string
+ */
+ sanitizer.purifyHtml = function (dirtyBody) {
+ return DOMPurify.sanitize(dirtyBody, {
+ SAFE_FOR_JQUERY: true,
+ SAFE_FOR_TEMPLATES: true
+ });
+ };
+
+ /**
+ * Runs a given dirty body through he, thereby encoding everything
+ * as HTML entities.
+ *
+ * @param {string} dirtyBody The unsanitized string
+ * @return {string} Safe-to-display HTML string
+ */
+ sanitizer.purifyText = function (dirtyBody) {
+ return he.encode(dirtyBody, {
+ encodeEverything: true
+ });
+ };
+
+ /**
+ * Calls #purify and #addLineBreaks to turn untrusted mail body content
+ * into safe-to-display HTML.
+ *
+ * NB: HTML content is preferred to plaintext content.
+ *
+ * @param {object} mail Pixelated Mail Object
+ * @return {string} Safe-to-display HTML string
+ */
+ sanitizer.sanitize = function (mail) {
+ var body;
+
+ if (mail.htmlBody) {
+ body = this.purifyHtml(mail.htmlBody);
+ } else {
+ body = this.purifyText(mail.textPlainBody);
+ body = this.addLineBreaks(body);
+ }
+
+ return body;
+ };
+
+ /**
+ * Add hooks to DOMPurify for opening links in new windows
+ */
+ DOMPurify.addHook('afterSanitizeAttributes', function (node) {
+ // set all elements owning target to target=_blank
+ if ('target' in node) {
+ node.setAttribute('target', '_blank');
+ }
+
+ // set non-HTML/MathML links to xlink:show=new
+ if (!node.hasAttribute('target') && (node.hasAttribute('xlink:href') || node.hasAttribute('href'))) {
+ node.setAttribute('xlink:show', 'new');
+ }
+ });
+
+ return sanitizer;
+});
diff --git a/web-ui/app/js/helpers/view_helper.js b/web-ui/app/js/helpers/view_helper.js
index e4e9277d..e8d517a5 100644
--- a/web-ui/app/js/helpers/view_helper.js
+++ b/web-ui/app/js/helpers/view_helper.js
@@ -17,12 +17,12 @@
define(
[
'helpers/contenttype',
- 'lib/html_whitelister',
'views/i18n',
'quoted-printable/quoted-printable',
- 'utf8/utf8'
+ 'utf8/utf8',
+ 'helpers/sanitizer'
],
- function(contentType, htmlWhitelister, i18n, quotedPrintable, utf8) {
+ function(contentType, i18n, quotedPrintable, utf8, sanitizer) {
'use strict';
function formatStatusClasses(ss) {
@@ -31,37 +31,8 @@ define(
}).join(' ');
}
- function addParagraphsToPlainText(textPlainBody) {
- return textPlainBody.replace(/^(.*?)$/mg, '$1<br/>');
- }
-
- function escapeHtmlTags(body) {
-
- var escapeIndex = {
- '&': '&amp;',
- '<': '&lt;',
- '>': '&gt;',
- '"': '&quot;',
- '\'':'&#39;',
- '/': '&#x2f;'
- };
-
- return body.replace(/["'<>\/&]/g, function(char){
- return escapeIndex[char];
- });
-
- }
-
- function escapeHtmlAndAddParagraphs(body) {
- var escapedBody = escapeHtmlTags(body);
- return addParagraphsToPlainText(escapedBody);
- }
-
function formatMailBody(mail) {
- var body = mail.htmlBody ?
- htmlWhitelister.sanitize(mail.htmlBody, htmlWhitelister.tagPolicy) :
- escapeHtmlAndAddParagraphs(mail.textPlainBody);
- return $('<div>' + body + '</div>');
+ return sanitizer.sanitize(mail);
}
function moveCaretToEnd(el) {
diff --git a/web-ui/app/js/mail_view/ui/mail_view.js b/web-ui/app/js/mail_view/ui/mail_view.js
index d4f5dd9e..8465b45a 100644
--- a/web-ui/app/js/mail_view/ui/mail_view.js
+++ b/web-ui/app/js/mail_view/ui/mail_view.js
@@ -72,6 +72,7 @@ define(
}));
this.$node.find('.bodyArea').html(viewHelpers.formatMailBody(data.mail));
+
this.trigger(document, events.search.highlightResults, {where: '.bodyArea'});
this.trigger(document, events.search.highlightResults, {where: '.subjectArea'});
this.trigger(document, events.search.highlightResults, {where: '.msg-header .recipients'});
diff --git a/web-ui/app/js/main.js b/web-ui/app/js/main.js
index 5fb2e46f..e093e790 100644
--- a/web-ui/app/js/main.js
+++ b/web-ui/app/js/main.js
@@ -22,6 +22,8 @@ requirejs.config({
'page': 'js/page',
'feedback': 'js/feedback',
'flight': 'bower_components/flight',
+ 'DOMPurify': 'bower_components/DOMPurify/dist/purify.min',
+ 'he': 'bower_components/he/he',
'hbs': 'js/generated/hbs',
'helpers': 'js/helpers',
'lib': 'js/lib',