diff options
author | Jon Newson <jon_newson@ieee.org> | 2016-02-26 16:20:59 +1100 |
---|---|---|
committer | Jon Newson <jon_newson@ieee.org> | 2016-02-26 16:20:59 +1100 |
commit | 05f4e2ca2d64eaba23c87df4d2e2cc9e09bba6de (patch) | |
tree | 50b2ccf6454f31a3f6bceaa997a5e2abbcb91a80 /web-ui/app/js/helpers | |
parent | 52467b9aef76c9aac2f250478befd3afb7b6aabd (diff) | |
parent | dbb434b56e6b161a3b851ae6a81f96dff14a29da (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.js | 9 | ||||
-rw-r--r-- | web-ui/app/js/helpers/contenttype.js | 2 | ||||
-rw-r--r-- | web-ui/app/js/helpers/monitored_ajax.js | 10 | ||||
-rw-r--r-- | web-ui/app/js/helpers/sanitizer.js | 108 | ||||
-rw-r--r-- | web-ui/app/js/helpers/view_helper.js | 37 |
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(/(
)?
/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 = { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - '\'':''', - '/': '/' - }; - - 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) { |