Skip to content

Commit 9909773

Browse files
farnabazlirantal
andauthored
Merge commit from fork
Co-authored-by: Liran Tal <[email protected]>
1 parent 8a59ae3 commit 9909773

File tree

2 files changed

+47
-2
lines changed

2 files changed

+47
-2
lines changed

src/runtime/parser/utils/props.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,31 @@ export const unsafeLinkPrefix = [
99
'data:text/xml'
1010
]
1111

12+
function isAnchorLinkAllowed(value: string) {
13+
const decodedUrl = decodeURIComponent(value)
14+
const urlSanitized = decodedUrl.replace(/&#x([0-9a-f]+);?/gi, '')
15+
.replace(/&#(\d+);?/g, '')
16+
.replace(/&[a-z]+;?/gi, '')
17+
18+
try {
19+
const url = new URL(urlSanitized)
20+
if (unsafeLinkPrefix.some(prefix => url.protocol.toLowerCase().startsWith(prefix))) {
21+
return false
22+
}
23+
} catch {
24+
return false
25+
}
26+
27+
return true
28+
}
29+
1230
export const validateProp = (attribute: string, value: string) => {
1331
if (attribute.startsWith('on')) {
1432
return false
1533
}
1634

1735
if (attribute === 'href' || attribute === 'src') {
18-
return !unsafeLinkPrefix.some(prefix => value.toLowerCase().startsWith(prefix))
36+
return isAnchorLinkAllowed(value)
1937
}
2038

2139
return true

test/markdown/xss.test.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const md = `\
1010
[XSS](vbscript:alert(document.domain))
1111
<javascript:prompt(document.cookie)>
1212
[x](y '<style>')
13+
<a href="jav&#x09;ascript:alert('XSS');">Click Me</a>
1314
1415
<!-- image -->
1516
@@ -31,7 +32,7 @@ const md = `\
3132
3233
`.trim()
3334

34-
it('XSS', async () => {
35+
it('XSS generic payloads', async () => {
3536
const { data, body } = await parseMarkdown(md)
3637

3738
expect(Object.keys(data)).toHaveLength(2)
@@ -41,3 +42,29 @@ it('XSS', async () => {
4142
expect(Object.entries(props as Record<string, any>).every(([k, v]) => validateProp(k, v))).toBeTruthy()
4243
}
4344
})
45+
46+
it('XSS payloads with HTML entities should be caught', async () => {
47+
const md = `\
48+
## XSS payloads with HTML entities
49+
<a href="jav&#x09;ascript:alert('XSS');">Click Me 1</a>
50+
<a href="jav&#x0A;ascript:alert('XSS');">Click Me 2</a>
51+
<a href="jav&#10;ascript:alert('XSS');">Click Me 3</a>
52+
53+
54+
`.trim()
55+
56+
// set the number of assertions to expect
57+
expect.assertions(4)
58+
59+
const { data, body } = await parseMarkdown(md)
60+
61+
expect(Object.keys(data)).toHaveLength(2)
62+
63+
for (const node of (body.children[1] as MDCElement).children) {
64+
const props = (node as MDCElement).props || {}
65+
66+
if ((node as MDCElement).tag === 'a') {
67+
expect(Object.keys(props)).toHaveLength(0)
68+
}
69+
}
70+
})

0 commit comments

Comments
 (0)