Skip to content

Commit 1e99314

Browse files
Merge pull request #3941 from RedisInsight/fe/feature/RI-6132
#RI-6132 - update sanitizer
2 parents 6ee4412 + 5f8b935 commit 1e99314

File tree

5 files changed

+47
-47
lines changed

5 files changed

+47
-47
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@
9898
"@types/classnames": "^2.2.11",
9999
"@types/d3": "^7.4.0",
100100
"@types/detect-port": "^1.3.0",
101+
"@types/dompurify": "^3.0.5",
101102
"@types/electron-store": "^3.2.0",
102103
"@types/express": "^4.17.3",
103104
"@types/file-saver": "^2.0.5",
@@ -220,6 +221,7 @@
220221
"d3": "^7.6.1",
221222
"date-fns": "^3.6.0",
222223
"date-fns-tz": "^3.1.3",
224+
"dompurify": "^3.1.7",
223225
"electron-context-menu": "^3.1.0",
224226
"electron-log": "^4.2.4",
225227
"electron-store": "^8.0.0",

redisinsight/ui/src/components/side-panels/panels/enablement-area/EnablementArea/components/Navigation/Navigation.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { useState } from 'react'
22
import cx from 'classnames'
3-
import { EuiListGroup, EuiText } from '@elastic/eui'
3+
import { EuiListGroup } from '@elastic/eui'
44
import { isArray } from 'lodash'
55
import { useParams } from 'react-router-dom'
66
import { useDispatch } from 'react-redux'
@@ -147,7 +147,7 @@ const Navigation = (props: Props) => {
147147
testId={id || label}
148148
label={label}
149149
summary={summary}
150-
{...args}
150+
path={args?.path}
151151
>
152152
{args?.content || label}
153153
</InternalLink>
Lines changed: 23 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,36 @@
11
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+
}
216

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+
})
2620

2721
export const remarkSanitize = (): (tree: Node) => void => (tree: any) => {
2822
visit(tree, 'html', (node) => {
2923
const inputTag = node.value.toLowerCase()
3024

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) {
3927
node.value = ''
40-
return
4128
}
4229

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)
5534
}
5635
})
5736
}

redisinsight/ui/src/utils/tests/formatters/markdown/remarkSanitize.spec.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@ jest.mock('unist-util-visit')
55

66
const testCases = [
77
{ input: '', output: '' },
8-
{ input: '<a href="https://localhost">', output: '<a href="https://localhost">' },
8+
{ input: '<a href="https://localhost">', output: '<a href="https://localhost" target="_blank">' },
9+
{ input: '<a href="/settings">', output: '<a>' },
910
{ input: '<a href="javascript:alert(1)">', output: '<a>' },
1011
{ input: '<img onload="alert(1)">', output: '<img>' },
1112
{ input: '<img src="javascript:alert(1)">', output: '<img>' },
1213
{ input: '<img src="img.png">', output: '<img src="img.png">' },
1314
{ input: '<div dangerouslySetInnerHTML={{"__html": "<img src=x onerror=alert(\'this.still.works\')>"}} />', output: '' },
1415
{ input: '<script>', output: '' },
16+
{ input: '<script>alert(1)</script>', output: '' },
1517
]
1618

1719
describe('remarkSanitize', () => {

yarn.lock

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2604,6 +2604,13 @@
26042604
resolved "https://registry.yarnpkg.com/@types/detect-port/-/detect-port-1.3.2.tgz#8c06a975e472803b931ee73740aeebd0a2eb27ae"
26052605
integrity sha512-xxgAGA2SAU4111QefXPSp5eGbDm/hW6zhvYl9IeEPZEry9F4d66QAHm5qpUXjb6IsevZV/7emAEx5MhP6O192g==
26062606

2607+
"@types/dompurify@^3.0.5":
2608+
version "3.0.5"
2609+
resolved "https://registry.yarnpkg.com/@types/dompurify/-/dompurify-3.0.5.tgz#02069a2fcb89a163bacf1a788f73cb415dd75cb7"
2610+
integrity sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==
2611+
dependencies:
2612+
"@types/trusted-types" "*"
2613+
26072614
"@types/electron-store@^3.2.0":
26082615
version "3.2.0"
26092616
resolved "https://registry.yarnpkg.com/@types/electron-store/-/electron-store-3.2.0.tgz#7fb722425b8df3560ebab86709eb03864833e9e7"
@@ -3105,6 +3112,11 @@
31053112
resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.2.tgz#6286b4c7228d58ab7866d19716f3696e03a09397"
31063113
integrity sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==
31073114

3115+
"@types/trusted-types@*":
3116+
version "2.0.7"
3117+
resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11"
3118+
integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==
3119+
31083120
"@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.2", "@types/unist@^2.0.3":
31093121
version "2.0.6"
31103122
resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d"
@@ -5654,6 +5666,11 @@ domhandler@^5.0.2, domhandler@^5.0.3:
56545666
dependencies:
56555667
domelementtype "^2.3.0"
56565668

5669+
dompurify@^3.1.7:
5670+
version "3.1.7"
5671+
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.1.7.tgz#711a8c96479fb6ced93453732c160c3c72418a6a"
5672+
integrity sha512-VaTstWtsneJY8xzy7DekmYWEOZcmzIe3Qb3zPd4STve1OBTa+e+WmS1ITQec1fZYXI3HCsOZZiSMpG6oxoWMWQ==
5673+
56575674
domutils@^2.5.2, domutils@^2.8.0:
56585675
version "2.8.0"
56595676
resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135"

0 commit comments

Comments
 (0)