summaryrefslogtreecommitdiff
path: root/web-ui/app/js/helpers/sanitizer.js
blob: eea1f0f7789a691cb3f5b0f9a944972c50c77854 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
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;
});