|
1 | 1 | import { visit } from 'unist-util-visit'
|
| 2 | +import DOMPurify from 'dompurify' |
| 3 | +import { IS_ABSOLUTE_PATH } from 'uiSrc/constants/regex' |
| 4 | + |
| 5 | +const isOpeningTag = (value: string) => |
| 6 | + value.startsWith('<') && !value.startsWith('</') && value.endsWith('>') |
| 7 | +const removeClosingTag = (value: string) => value.replace(/<\/[a-zA-Z][^>]*>/g, '') |
| 8 | +const isContainsClosingTag = (value: string) => value.indexOf('</') > -1 |
| 9 | + |
| 10 | +DOMPurify.addHook('afterSanitizeAttributes', (node: Element) => { |
| 11 | + if (node.tagName === 'A' && node.hasAttribute('href')) { |
| 12 | + if (!IS_ABSOLUTE_PATH.test(node.getAttribute('href') || '')) { |
| 13 | + node.removeAttribute('href') |
| 14 | + return |
| 15 | + } |
2 | 16 |
|
3 |
| -const permittedAttibutes = [ |
4 |
| - 'dangerouslySetInnerHTML' |
5 |
| -] |
6 |
| - |
7 |
| -const dangerousAttributes = [ |
8 |
| - 'onabort', 'onafterprint', 'onanimationend', 'onanimationiteration', 'onanimationstart', |
9 |
| - 'onbeforeprint', 'onbeforeunload', 'onblur', 'oncancel', 'oncanplay', 'oncanplaythrough', |
10 |
| - 'onchange', 'onclick', 'onclose', 'oncontextmenu', 'oncopy', 'oncuechange', 'oncut', 'ondblclick', |
11 |
| - 'ondrag', 'ondragend', 'ondragenter', 'ondragexit', 'ondragleave', 'ondragover', 'ondragstart', |
12 |
| - 'ondrop', 'ondurationchange', 'onemptied', 'onended', 'onerror', 'onfocus', 'onfocusin', 'onfocusout', |
13 |
| - 'onformdata', 'onhashchange', 'oninput', 'oninvalid', 'onkeydown', 'onkeypress', 'onkeyup', |
14 |
| - 'onlanguagechange', 'onload', 'onloadeddata', 'onloadedmetadata', 'onloadstart', 'onmessage', |
15 |
| - 'onmessageerror', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', |
16 |
| - 'onmouseover', 'onmouseup', 'onoffline', 'ononline', 'onpagehide', 'onpageshow', 'onpaste', |
17 |
| - 'onpause', 'onplay', 'onplaying', 'onpopstate', 'onprogress', 'onratechange', 'onrejectionhandled', |
18 |
| - 'onreset', 'onresize', 'onscroll', 'onsearch', 'onseeked', 'onseeking', 'onselect', 'onstalled', |
19 |
| - 'onstorage', 'onsubmit', 'onsuspend', 'ontimeupdate', 'ontoggle', 'ontransitionend', 'onunhandledrejection', |
20 |
| - 'onunload', 'onvolumechange', 'onwaiting', 'onwheel', 'href', 'src', 'action', 'formaction', 'manifest', |
21 |
| - 'background', 'poster', 'cite', 'data', 'ping', 'xlink:href', 'style', 'srcdoc', 'sandbox' |
22 |
| -].join('|') |
23 |
| - |
24 |
| -// Define an array of potentially dangerous tags |
25 |
| -const dangerousTags = ['script', 'iframe', 'object', 'embed', 'link', 'style', 'meta'] |
| 17 | + node.setAttribute('target', '_blank') |
| 18 | + } |
| 19 | +}) |
26 | 20 |
|
27 | 21 | export const remarkSanitize = (): (tree: Node) => void => (tree: any) => {
|
28 | 22 | visit(tree, 'html', (node) => {
|
29 | 23 | const inputTag = node.value.toLowerCase()
|
30 | 24 |
|
31 |
| - // remove dangerous tags |
32 |
| - if (dangerousTags.some((tag) => inputTag.startsWith(`<${tag}`))) { |
33 |
| - node.value = '' |
34 |
| - return |
35 |
| - } |
36 |
| - |
37 |
| - // remove permitted attributes |
38 |
| - if (permittedAttibutes.some((attr) => node.value.includes(`${attr}=`))) { |
| 25 | + // JUST BANNED |
| 26 | + if (inputTag.indexOf('dangerouslysetinnerhtml') > -1) { |
39 | 27 | node.value = ''
|
40 |
| - return |
41 | 28 | }
|
42 | 29 |
|
43 |
| - // sanitize dangerous attributes |
44 |
| - const dangerousAttrRegex = new RegExp(`\\s*(${dangerousAttributes})="[^"]*"`, 'gi') |
45 |
| - if (node.value.match(dangerousAttrRegex)) { |
46 |
| - node.value = node.value.replace(dangerousAttrRegex, (match: string) => { |
47 |
| - const attr = match.toLowerCase().trim() |
48 |
| - if (attr.startsWith('href') || attr.startsWith('src') || attr.startsWith('xlink:href')) { |
49 |
| - if (attr.indexOf('"javascript:') > -1) return '' |
50 |
| - return match |
51 |
| - } |
52 |
| - |
53 |
| - return '' |
54 |
| - }) |
| 30 | + if (isOpeningTag(inputTag)) { |
| 31 | + const isTagContainsClosing = isContainsClosingTag(inputTag) |
| 32 | + const sanitized = DOMPurify.sanitize(node.value) |
| 33 | + node.value = isTagContainsClosing ? sanitized : removeClosingTag(sanitized) |
55 | 34 | }
|
56 | 35 | })
|
57 | 36 | }
|
0 commit comments