Merge pull request #6551 from backstage/iameap/dompurify
[TechDocs] Use "secure by default" sanitization library
This commit is contained in:
@@ -44,6 +44,7 @@
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@material-ui/lab": "4.0.0-alpha.45",
|
||||
"@material-ui/styles": "^4.10.0",
|
||||
"dompurify": "^2.2.9",
|
||||
"eventsource": "^1.1.0",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
@@ -51,8 +52,7 @@
|
||||
"react-router": "6.0.0-beta.0",
|
||||
"react-router-dom": "6.0.0-beta.0",
|
||||
"react-use": "^17.2.4",
|
||||
"react-text-truncate": "^0.16.0",
|
||||
"sanitize-html": "^2.3.2"
|
||||
"react-text-truncate": "^0.16.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.7.5",
|
||||
@@ -63,6 +63,7 @@
|
||||
"@testing-library/react": "^11.2.5",
|
||||
"@testing-library/react-hooks": "^3.4.2",
|
||||
"@testing-library/user-event": "^13.1.8",
|
||||
"@types/dompurify": "^2.2.2",
|
||||
"@types/eventsource": "^1.1.5",
|
||||
"@types/jest": "^26.0.7",
|
||||
"@types/node": "^14.14.32",
|
||||
|
||||
+3
-3
@@ -14,9 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createTestShadowDom, FIXTURES } from '../../../test-utils';
|
||||
import { Transformer } from '../index';
|
||||
import { sanitizeDOM } from './index';
|
||||
import { createTestShadowDom, FIXTURES } from '../../test-utils';
|
||||
import { Transformer } from './index';
|
||||
import { sanitizeDOM } from './sanitizeDOM';
|
||||
|
||||
const injectMaliciousLink = (): Transformer => dom => {
|
||||
const link = document.createElement('a');
|
||||
+7
-14
@@ -14,23 +14,16 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// @ts-ignore
|
||||
import sanitizeHtml from 'sanitize-html';
|
||||
import type { Transformer } from '../transformer';
|
||||
import { TECHDOCS_ALLOWED_TAGS } from './tags';
|
||||
import { TECHDOCS_ALLOWED_ATTRIBUTES } from './attributes';
|
||||
|
||||
// TODO(freben): move all of this out of index
|
||||
import DOMPurify from 'dompurify';
|
||||
import type { Transformer } from './transformer';
|
||||
|
||||
export const sanitizeDOM = (): Transformer => {
|
||||
return dom => {
|
||||
const sanitizedHtml = sanitizeHtml(dom.innerHTML, {
|
||||
allowedTags: TECHDOCS_ALLOWED_TAGS,
|
||||
allowedAttributes: TECHDOCS_ALLOWED_ATTRIBUTES,
|
||||
allowedSchemes: ['http', 'https', 'ftp', 'mailto', 'data', 'blob'],
|
||||
return DOMPurify.sanitize(dom.innerHTML, {
|
||||
ADD_TAGS: ['link'],
|
||||
FORBID_TAGS: ['style'],
|
||||
WHOLE_DOCUMENT: true,
|
||||
RETURN_DOM: true,
|
||||
});
|
||||
|
||||
return new DOMParser().parseFromString(sanitizedHtml, 'text/html')
|
||||
.documentElement;
|
||||
};
|
||||
};
|
||||
@@ -1,386 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This is the source of truth of what attributes we support in TechDocs.
|
||||
*/
|
||||
|
||||
export const html = [
|
||||
'accept',
|
||||
'action',
|
||||
'align',
|
||||
'alt',
|
||||
'autocapitalize',
|
||||
'autocomplete',
|
||||
'autopictureinpicture',
|
||||
'autoplay',
|
||||
'background',
|
||||
'bgcolor',
|
||||
'border',
|
||||
'capture',
|
||||
'cellpadding',
|
||||
'cellspacing',
|
||||
'checked',
|
||||
'cite',
|
||||
'class',
|
||||
'clear',
|
||||
'color',
|
||||
'cols',
|
||||
'colspan',
|
||||
'controls',
|
||||
'controlslist',
|
||||
'coords',
|
||||
'crossorigin',
|
||||
'datetime',
|
||||
'decoding',
|
||||
'default',
|
||||
'dir',
|
||||
'disabled',
|
||||
'disablepictureinpicture',
|
||||
'disableremoteplayback',
|
||||
'download',
|
||||
'draggable',
|
||||
'enctype',
|
||||
'enterkeyhint',
|
||||
'face',
|
||||
'for',
|
||||
'headers',
|
||||
'height',
|
||||
'hidden',
|
||||
'high',
|
||||
'href',
|
||||
'hreflang',
|
||||
'id',
|
||||
'inputmode',
|
||||
'integrity',
|
||||
'ismap',
|
||||
'kind',
|
||||
'label',
|
||||
'lang',
|
||||
'list',
|
||||
'loading',
|
||||
'loop',
|
||||
'low',
|
||||
'max',
|
||||
'maxlength',
|
||||
'media',
|
||||
'method',
|
||||
'min',
|
||||
'minlength',
|
||||
'multiple',
|
||||
'muted',
|
||||
'name',
|
||||
'noshade',
|
||||
'novalidate',
|
||||
'nowrap',
|
||||
'open',
|
||||
'optimum',
|
||||
'pattern',
|
||||
'placeholder',
|
||||
'playsinline',
|
||||
'poster',
|
||||
'preload',
|
||||
'pubdate',
|
||||
'radiogroup',
|
||||
'readonly',
|
||||
'rel',
|
||||
'required',
|
||||
'rev',
|
||||
'reversed',
|
||||
'role',
|
||||
'rows',
|
||||
'rowspan',
|
||||
'spellcheck',
|
||||
'scope',
|
||||
'selected',
|
||||
'shape',
|
||||
'size',
|
||||
'sizes',
|
||||
'span',
|
||||
'srclang',
|
||||
'start',
|
||||
'src',
|
||||
'srcset',
|
||||
'step',
|
||||
'style',
|
||||
'summary',
|
||||
'tabindex',
|
||||
'title',
|
||||
'translate',
|
||||
'type',
|
||||
'usemap',
|
||||
'valign',
|
||||
'value',
|
||||
'width',
|
||||
'xmlns',
|
||||
];
|
||||
|
||||
export const svg = [
|
||||
'accent-height',
|
||||
'accumulate',
|
||||
'additive',
|
||||
'alignment-baseline',
|
||||
'ascent',
|
||||
'attributename',
|
||||
'attributetype',
|
||||
'azimuth',
|
||||
'basefrequency',
|
||||
'baseline-shift',
|
||||
'begin',
|
||||
'bias',
|
||||
'by',
|
||||
'class',
|
||||
'clip',
|
||||
'clip-path',
|
||||
'clip-rule',
|
||||
'color',
|
||||
'color-interpolation',
|
||||
'color-interpolation-filters',
|
||||
'color-profile',
|
||||
'color-rendering',
|
||||
'cx',
|
||||
'cy',
|
||||
'd',
|
||||
'dx',
|
||||
'dy',
|
||||
'data',
|
||||
'diffuseconstant',
|
||||
'direction',
|
||||
'display',
|
||||
'divisor',
|
||||
'dur',
|
||||
'edgemode',
|
||||
'elevation',
|
||||
'end',
|
||||
'fill',
|
||||
'fill-opacity',
|
||||
'fill-rule',
|
||||
'filter',
|
||||
'filterunits',
|
||||
'flood-color',
|
||||
'flood-opacity',
|
||||
'font-family',
|
||||
'font-size',
|
||||
'font-size-adjust',
|
||||
'font-stretch',
|
||||
'font-style',
|
||||
'font-variant',
|
||||
'font-weight',
|
||||
'fx',
|
||||
'fy',
|
||||
'g1',
|
||||
'g2',
|
||||
'glyph-name',
|
||||
'glyphref',
|
||||
'gradientunits',
|
||||
'gradienttransform',
|
||||
'height',
|
||||
'href',
|
||||
'id',
|
||||
'image-rendering',
|
||||
'in',
|
||||
'in2',
|
||||
'k',
|
||||
'k1',
|
||||
'k2',
|
||||
'k3',
|
||||
'k4',
|
||||
'kerning',
|
||||
'keypoints',
|
||||
'keysplines',
|
||||
'keytimes',
|
||||
'lang',
|
||||
'lengthadjust',
|
||||
'letter-spacing',
|
||||
'kernelmatrix',
|
||||
'kernelunitlength',
|
||||
'lighting-color',
|
||||
'local',
|
||||
'marker-end',
|
||||
'marker-mid',
|
||||
'marker-start',
|
||||
'markerheight',
|
||||
'markerunits',
|
||||
'markerwidth',
|
||||
'maskcontentunits',
|
||||
'maskunits',
|
||||
'max',
|
||||
'mask',
|
||||
'media',
|
||||
'method',
|
||||
'mode',
|
||||
'min',
|
||||
'name',
|
||||
'numoctaves',
|
||||
'offset',
|
||||
'operator',
|
||||
'opacity',
|
||||
'order',
|
||||
'orient',
|
||||
'orientation',
|
||||
'origin',
|
||||
'overflow',
|
||||
'paint-order',
|
||||
'path',
|
||||
'pathlength',
|
||||
'patterncontentunits',
|
||||
'patterntransform',
|
||||
'patternunits',
|
||||
'points',
|
||||
'preservealpha',
|
||||
'preserveaspectratio',
|
||||
'primitiveunits',
|
||||
'r',
|
||||
'rx',
|
||||
'ry',
|
||||
'radius',
|
||||
'refx',
|
||||
'refy',
|
||||
'repeatcount',
|
||||
'repeatdur',
|
||||
'restart',
|
||||
'result',
|
||||
'rotate',
|
||||
'scale',
|
||||
'seed',
|
||||
'shape-rendering',
|
||||
'specularconstant',
|
||||
'specularexponent',
|
||||
'spreadmethod',
|
||||
'startoffset',
|
||||
'stddeviation',
|
||||
'stitchtiles',
|
||||
'stop-color',
|
||||
'stop-opacity',
|
||||
'stroke-dasharray',
|
||||
'stroke-dashoffset',
|
||||
'stroke-linecap',
|
||||
'stroke-linejoin',
|
||||
'stroke-miterlimit',
|
||||
'stroke-opacity',
|
||||
'stroke',
|
||||
'stroke-width',
|
||||
'style',
|
||||
'surfacescale',
|
||||
'tabindex',
|
||||
'targetx',
|
||||
'targety',
|
||||
'transform',
|
||||
'text-anchor',
|
||||
'text-decoration',
|
||||
'text-rendering',
|
||||
'textlength',
|
||||
'type',
|
||||
'u1',
|
||||
'u2',
|
||||
'unicode',
|
||||
'values',
|
||||
'viewbox',
|
||||
'visibility',
|
||||
'version',
|
||||
'vert-adv-y',
|
||||
'vert-origin-x',
|
||||
'vert-origin-y',
|
||||
'width',
|
||||
'word-spacing',
|
||||
'wrap',
|
||||
'writing-mode',
|
||||
'xchannelselector',
|
||||
'ychannelselector',
|
||||
'x',
|
||||
'x1',
|
||||
'x2',
|
||||
'xmlns',
|
||||
'y',
|
||||
'y1',
|
||||
'y2',
|
||||
'z',
|
||||
'zoomandpan',
|
||||
];
|
||||
|
||||
export const mathMl = [
|
||||
'accent',
|
||||
'accentunder',
|
||||
'align',
|
||||
'bevelled',
|
||||
'close',
|
||||
'columnsalign',
|
||||
'columnlines',
|
||||
'columnspan',
|
||||
'denomalign',
|
||||
'depth',
|
||||
'dir',
|
||||
'display',
|
||||
'displaystyle',
|
||||
'encoding',
|
||||
'fence',
|
||||
'frame',
|
||||
'height',
|
||||
'href',
|
||||
'id',
|
||||
'largeop',
|
||||
'length',
|
||||
'linethickness',
|
||||
'lspace',
|
||||
'lquote',
|
||||
'mathbackground',
|
||||
'mathcolor',
|
||||
'mathsize',
|
||||
'mathvariant',
|
||||
'maxsize',
|
||||
'minsize',
|
||||
'movablelimits',
|
||||
'notation',
|
||||
'numalign',
|
||||
'open',
|
||||
'rowalign',
|
||||
'rowlines',
|
||||
'rowspacing',
|
||||
'rowspan',
|
||||
'rspace',
|
||||
'rquote',
|
||||
'scriptlevel',
|
||||
'scriptminsize',
|
||||
'scriptsizemultiplier',
|
||||
'selection',
|
||||
'separator',
|
||||
'separators',
|
||||
'stretchy',
|
||||
'subscriptshift',
|
||||
'supscriptshift',
|
||||
'symmetric',
|
||||
'voffset',
|
||||
'width',
|
||||
'xmlns',
|
||||
];
|
||||
|
||||
export const xml = [
|
||||
'xlink:href',
|
||||
'xml:id',
|
||||
'xlink:title',
|
||||
'xml:space',
|
||||
'xmlns:xlink',
|
||||
];
|
||||
|
||||
/**
|
||||
* Anything in here will be supported as HTML attributes from TechDocs.
|
||||
*
|
||||
* @note Be conscious about what you add here. It can affect the TechDocs experience. For
|
||||
* some review before making a PR, reach out in the #docs-like-code channel in Discord first.
|
||||
*/
|
||||
export const TECHDOCS_ALLOWED_ATTRIBUTES = {
|
||||
'*': [...html, ...svg, ...mathMl, ...xml, 'data-*'],
|
||||
};
|
||||
@@ -1,248 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This is the source of truth for what HTML tags we support in TechDocs.
|
||||
*/
|
||||
|
||||
// prettier-ignore
|
||||
export const html = [
|
||||
'a', 'abbr', 'acronym',
|
||||
'address', 'area', 'article',
|
||||
'aside', 'audio', 'b', 'bdi',
|
||||
'bdo', 'big',
|
||||
'blink', 'blockquote',
|
||||
'body',
|
||||
'br', 'button',
|
||||
'canvas', 'caption', 'center', 'cite', 'code',
|
||||
'col', 'colgroup',
|
||||
'content',
|
||||
'data',
|
||||
'datalist',
|
||||
'dd',
|
||||
'decorator',
|
||||
'del',
|
||||
'details',
|
||||
'dfn',
|
||||
'dir',
|
||||
'div',
|
||||
'dl',
|
||||
'dt',
|
||||
'element',
|
||||
'em',
|
||||
'fieldset',
|
||||
'figcaption',
|
||||
'figure',
|
||||
'font',
|
||||
'footer',
|
||||
'form',
|
||||
'h1',
|
||||
'h2',
|
||||
'h3',
|
||||
'h4',
|
||||
'h5',
|
||||
'h6',
|
||||
'head',
|
||||
'header',
|
||||
'hgroup',
|
||||
'hr',
|
||||
'html',
|
||||
'i',
|
||||
'img',
|
||||
'input',
|
||||
'ins',
|
||||
'kbd',
|
||||
'label',
|
||||
'legend',
|
||||
'li',
|
||||
'link',
|
||||
'main',
|
||||
'map',
|
||||
'mark',
|
||||
'marquee',
|
||||
'menu',
|
||||
'menuitem',
|
||||
'meter',
|
||||
'nav',
|
||||
'nobr',
|
||||
'ol',
|
||||
'optgroup',
|
||||
'option',
|
||||
'output',
|
||||
'p',
|
||||
'picture',
|
||||
'pre',
|
||||
'progress',
|
||||
'q',
|
||||
'rp',
|
||||
'rt',
|
||||
'ruby',
|
||||
's',
|
||||
'samp',
|
||||
'section',
|
||||
'select',
|
||||
'shadow',
|
||||
'small',
|
||||
'source',
|
||||
'spacer',
|
||||
'span',
|
||||
'strike',
|
||||
'strong',
|
||||
'sub',
|
||||
'summary',
|
||||
'sup',
|
||||
'table',
|
||||
'tbody',
|
||||
'td',
|
||||
'template',
|
||||
'textarea',
|
||||
'tfoot',
|
||||
'th',
|
||||
'thead',
|
||||
'time',
|
||||
'tr',
|
||||
'track',
|
||||
'tt',
|
||||
'u',
|
||||
'ul',
|
||||
'var',
|
||||
'video',
|
||||
'wbr',
|
||||
];
|
||||
|
||||
// SVG
|
||||
export const svg = [
|
||||
'svg',
|
||||
'a',
|
||||
'altglyph',
|
||||
'altglyphdef',
|
||||
'altglyphitem',
|
||||
'animatecolor',
|
||||
'animatemotion',
|
||||
'animatetransform',
|
||||
'audio',
|
||||
'canvas',
|
||||
'circle',
|
||||
'clippath',
|
||||
'defs',
|
||||
'desc',
|
||||
'ellipse',
|
||||
'filter',
|
||||
'font',
|
||||
'g',
|
||||
'glyph',
|
||||
'glyphref',
|
||||
'hkern',
|
||||
'image',
|
||||
'line',
|
||||
'lineargradient',
|
||||
'marker',
|
||||
'mask',
|
||||
'metadata',
|
||||
'mpath',
|
||||
'path',
|
||||
'pattern',
|
||||
'polygon',
|
||||
'polyline',
|
||||
'radialgradient',
|
||||
'rect',
|
||||
'stop',
|
||||
'switch',
|
||||
'symbol',
|
||||
'text',
|
||||
'textpath',
|
||||
'title',
|
||||
'tref',
|
||||
'tspan',
|
||||
'video',
|
||||
'view',
|
||||
'vkern',
|
||||
];
|
||||
|
||||
export const svgFilters = [
|
||||
'feBlend',
|
||||
'feColorMatrix',
|
||||
'feComponentTransfer',
|
||||
'feComposite',
|
||||
'feConvolveMatrix',
|
||||
'feDiffuseLighting',
|
||||
'feDisplacementMap',
|
||||
'feDistantLight',
|
||||
'feFlood',
|
||||
'feFuncA',
|
||||
'feFuncB',
|
||||
'feFuncG',
|
||||
'feFuncR',
|
||||
'feGaussianBlur',
|
||||
'feMerge',
|
||||
'feMergeNode',
|
||||
'feMorphology',
|
||||
'feOffset',
|
||||
'fePointLight',
|
||||
'feSpecularLighting',
|
||||
'feSpotLight',
|
||||
'feTile',
|
||||
'feTurbulence',
|
||||
];
|
||||
|
||||
export const mathMl = [
|
||||
'math',
|
||||
'menclose',
|
||||
'merror',
|
||||
'mfenced',
|
||||
'mfrac',
|
||||
'mglyph',
|
||||
'mi',
|
||||
'mlabeledtr',
|
||||
'mmultiscripts',
|
||||
'mn',
|
||||
'mo',
|
||||
'mover',
|
||||
'mpadded',
|
||||
'mphantom',
|
||||
'mroot',
|
||||
'mrow',
|
||||
'ms',
|
||||
'mspace',
|
||||
'msqrt',
|
||||
'mstyle',
|
||||
'msub',
|
||||
'msup',
|
||||
'msubsup',
|
||||
'mtable',
|
||||
'mtd',
|
||||
'mtext',
|
||||
'mtr',
|
||||
'munder',
|
||||
'munderover',
|
||||
];
|
||||
|
||||
export const text = ['#text'];
|
||||
|
||||
/**
|
||||
* Anything in here will be executed as HTML tags from TechDocs.
|
||||
*
|
||||
* @note Be conscious about what you add here. It can affect the TechDocs experience. For
|
||||
* some review before making a PR, reach out in the #docs-like-code channel in Discord first.
|
||||
*/
|
||||
export const TECHDOCS_ALLOWED_TAGS = [
|
||||
...html,
|
||||
...svg,
|
||||
...svgFilters,
|
||||
...mathMl,
|
||||
...text,
|
||||
'iframe',
|
||||
];
|
||||
Reference in New Issue
Block a user