enhancement(renderer): custom uri protocols in links except scripts

Signed-off-by: Erik Michelson <github@erik.michelson.eu>
This commit is contained in:
Erik Michelson 2024-03-24 22:04:08 +01:00
parent 9d55c5ee53
commit 4b8f6da78a
3 changed files with 63 additions and 2 deletions

View file

@ -27,4 +27,40 @@ exports[`HTML to React will forward the parser options 1`] = `
</div> </div>
`; `;
exports[`HTML to React will render links with non-http protocols 1`] = `
<div>
<a
href="tel:+1234567890"
>
tel
</a>
<a
href="mailto:test@example.com"
>
mailto
</a>
<a
href="geo:25.197,55.274"
>
geo
</a>
<a
href="xmpp:test"
>
xmpp
</a>
</div>
`;
exports[`HTML to React won't render script links 1`] = `
<div>
<a>
js
</a>
<a>
vbs
</a>
</div>
`;
exports[`HTML to React won't render script tags 1`] = `<div />`; exports[`HTML to React won't render script tags 1`] = `<div />`;

View file

@ -17,6 +17,24 @@ describe('HTML to React', () => {
expect(view.container).toMatchSnapshot() expect(view.container).toMatchSnapshot()
}) })
it("won't render script links", () => {
const view = render(
<HtmlToReact htmlCode={'<a href="javascript:alert(true)">js</a><a href="vbscript:WScript.Evil">vbs</a>'} />
)
expect(view.container).toMatchSnapshot()
})
it('will render links with non-http protocols', () => {
const view = render(
<HtmlToReact
htmlCode={
'<a href="tel:+1234567890">tel</a><a href="mailto:test@example.com">mailto</a><a href="geo:25.197,55.274">geo</a><a href="xmpp:test">xmpp</a>'
}
/>
)
expect(view.container).toMatchSnapshot()
})
it('will forward the DomPurify settings', () => { it('will forward the DomPurify settings', () => {
const view = render( const view = render(
<HtmlToReact domPurifyConfig={{ ADD_TAGS: ['test-tag'] }} htmlCode={'<test-tag>Test!</test-tag>'} /> <HtmlToReact domPurifyConfig={{ ADD_TAGS: ['test-tag'] }} htmlCode={'<test-tag>Test!</test-tag>'} />

View file

@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file) * SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
@ -16,6 +16,8 @@ export interface HtmlToReactProps {
parserOptions?: ParserOptions parserOptions?: ParserOptions
} }
const REGEX_URI_SCHEME_NO_SCRIPTS = /^(?!.*script:).+:?/i
/** /**
* Renders * Renders
* @param htmlCode * @param htmlCode
@ -26,7 +28,12 @@ export interface HtmlToReactProps {
export const HtmlToReact: React.FC<HtmlToReactProps> = ({ htmlCode, domPurifyConfig, parserOptions }) => { export const HtmlToReact: React.FC<HtmlToReactProps> = ({ htmlCode, domPurifyConfig, parserOptions }) => {
const elements = useMemo(() => { const elements = useMemo(() => {
const sanitizedHtmlCode = measurePerformance('html-to-react: sanitize', () => { const sanitizedHtmlCode = measurePerformance('html-to-react: sanitize', () => {
return sanitize(htmlCode, { ...domPurifyConfig, RETURN_DOM_FRAGMENT: false, RETURN_DOM: false }) return sanitize(htmlCode, {
...domPurifyConfig,
RETURN_DOM_FRAGMENT: false,
RETURN_DOM: false,
ALLOWED_URI_REGEXP: REGEX_URI_SCHEME_NO_SCRIPTS
})
}) })
return measurePerformance('html-to-react: convertHtmlToReact', () => { return measurePerformance('html-to-react: convertHtmlToReact', () => {
return convertHtmlToReact(sanitizedHtmlCode, parserOptions) return convertHtmlToReact(sanitizedHtmlCode, parserOptions)