summaryrefslogtreecommitdiff
path: root/web-ui/app/js/helpers
diff options
context:
space:
mode:
authorJon Newson <jon_newson@ieee.org>2016-02-26 16:20:59 +1100
committerJon Newson <jon_newson@ieee.org>2016-02-26 16:20:59 +1100
commit05f4e2ca2d64eaba23c87df4d2e2cc9e09bba6de (patch)
tree50b2ccf6454f31a3f6bceaa997a5e2abbcb91a80 /web-ui/app/js/helpers
parent52467b9aef76c9aac2f250478befd3afb7b6aabd (diff)
parentdbb434b56e6b161a3b851ae6a81f96dff14a29da (diff)
Merge branch 'master' of https://github.com/pixelated/pixelated-user-agent
# By Felix Hammerl (5) and others # Via NavaL * 'master' of https://github.com/pixelated/pixelated-user-agent: serving the client directly, as the current dependency on proxy strips out xsrf cookies -fixing functional test only adding feature resource in root_resource test -- fixing build changed logout to post Issue #612 Backend and frontend protection against csrf attacks: - root resources changes the csrf token cookie everytime it is loaded, in particular during the intestitial load during login - it will also add that cookie on single user mode - initialize will still load all resources - but they you cant access them if the csrf token do not match - all ajax calls needs to add the token to the header - non ajax get requests do not need xsrf token validation - non ajax post will have to send the token in as a form input or in the content Consolidate stylesheets Remove unused font and stylesheetgit s Create a new deferred for all IMAPAccount calls Clean up jshintrc Recreate session on soledad problems issue #617: Remove old html whitelister Issue #617: Sanitize received content
Diffstat (limited to 'web-ui/app/js/helpers')
-rw-r--r--web-ui/app/js/helpers/browser.js9
-rw-r--r--web-ui/app/js/helpers/contenttype.js2
-rw-r--r--web-ui/app/js/helpers/monitored_ajax.js10
-rw-r--r--web-ui/app/js/helpers/sanitizer.js108
-rw-r--r--web-ui/app/js/helpers/view_helper.js37
5 files changed, 128 insertions, 38 deletions
diff --git a/web-ui/app/js/helpers/browser.js b/web-ui/app/js/helpers/browser.js
index e5be6667..dacf2263 100644
--- a/web-ui/app/js/helpers/browser.js
+++ b/web-ui/app/js/helpers/browser.js
@@ -23,7 +23,14 @@ define([], function () {
window.location.replace(url);
}
+ function getCookie(name) {
+ var value = '; ' + document.cookie;
+ var parts = value.split('; ' + name + '=');
+ if (parts.length === 2) { return parts.pop().split(';').shift(); }
+ }
+
return {
- redirect: redirect
+ redirect: redirect,
+ getCookie: getCookie
};
});
diff --git a/web-ui/app/js/helpers/contenttype.js b/web-ui/app/js/helpers/contenttype.js
index 92b456e9..a1e5361a 100644
--- a/web-ui/app/js/helpers/contenttype.js
+++ b/web-ui/app/js/helpers/contenttype.js
@@ -14,6 +14,8 @@
* You should have received a copy of the GNU Affero General Public License
* along with Pixelated. If not, see <http://www.gnu.org/licenses/>.
*/
+
+/* jshint curly: false */
define([], function () {
'use strict';
var exports = {};
diff --git a/web-ui/app/js/helpers/monitored_ajax.js b/web-ui/app/js/helpers/monitored_ajax.js
index 1cb720de..dc182d58 100644
--- a/web-ui/app/js/helpers/monitored_ajax.js
+++ b/web-ui/app/js/helpers/monitored_ajax.js
@@ -36,6 +36,8 @@ define(['page/events', 'views/i18n', 'helpers/browser'], function (events, i18n,
}
};
+ config.headers = {'X-XSRF-TOKEN': browser.getCookie('XSRF-TOKEN')};
+
var originalComplete = config.complete;
config.complete = function () {
if (originalComplete) {
@@ -46,15 +48,15 @@ define(['page/events', 'views/i18n', 'helpers/browser'], function (events, i18n,
return $.ajax(url, config).fail(function (xmlhttprequest, textstatus, message) {
if (!config.skipErrorMessage) {
var msg = (xmlhttprequest.responseJSON && xmlhttprequest.responseJSON.message) ||
- messages[textstatus] ||
- 'unexpected problem while talking to server';
- on.trigger(document, events.ui.userAlerts.displayMessage, {message: i18n(msg)});
+ messages[textstatus] ||
+ 'unexpected problem while talking to server';
+ on.trigger(document, events.ui.userAlerts.displayMessage, {message: i18n(msg), class: 'error'});
}
if (xmlhttprequest.status === 302) {
var redirectUrl = xmlhttprequest.getResponseHeader('Location');
browser.redirect(redirectUrl);
- }else if (xmlhttprequest.status === 401) {
+ } else if (xmlhttprequest.status === 401) {
browser.redirect('/');
}
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) {