summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/lib/dompurify.js
blob: 76624c81ed5be8f4ec835c35d2c45cb590d8d6f3 (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
import { sanitize as dompurifySanitize, addHook } from 'dompurify';
import { getBaseURL, relativePathToAbsolute } from '~/lib/utils/url_utility';

// Safely allow SVG <use> tags

const defaultConfig = {
  ADD_TAGS: ['use'],
};

// Only icons urls from `gon` are allowed
const getAllowedIconUrls = (gon = window.gon) =>
  [gon.sprite_file_icons, gon.sprite_icons].filter(Boolean);

const isUrlAllowed = (url) => getAllowedIconUrls().some((allowedUrl) => url.startsWith(allowedUrl));

const isHrefSafe = (url) =>
  isUrlAllowed(url) || isUrlAllowed(relativePathToAbsolute(url, getBaseURL()));

const removeUnsafeHref = (node, attr) => {
  if (!node.hasAttribute(attr)) {
    return;
  }

  if (!isHrefSafe(node.getAttribute(attr))) {
    node.removeAttribute(attr);
  }
};

/**
 * Sanitize icons' <use> tag attributes, to safely include
 * svgs such as in:
 *
 * <svg viewBox="0 0 100 100">
 *   <use href="/assets/icons-xxx.svg#icon_name"></use>
 * </svg>
 *
 * @param {Object} node - Node to sanitize
 */
const sanitizeSvgIcon = (node) => {
  removeUnsafeHref(node, 'href');

  // Note: `xlink:href` is deprecated, but still in use
  // https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/xlink:href
  removeUnsafeHref(node, 'xlink:href');
};

addHook('afterSanitizeAttributes', (node) => {
  if (node.tagName.toLowerCase() === 'use') {
    sanitizeSvgIcon(node);
  }
});

export const sanitize = (val, config = defaultConfig) => dompurifySanitize(val, config);