diff --git a/lib/index.js b/lib/index.js index 3bbe47bd..2ebf91f7 100644 --- a/lib/index.js +++ b/lib/index.js @@ -81,6 +81,7 @@ import {unreachable} from 'devlop' import {toJsxRuntime} from 'hast-util-to-jsx-runtime' import {urlAttributes} from 'html-url-attributes' +import {sanitizeUri} from 'micromark-util-sanitize-uri' // @ts-expect-error: untyped. import {Fragment, jsx, jsxs} from 'react/jsx-runtime' import remarkParse from 'remark-parse' @@ -89,8 +90,6 @@ import {unified} from 'unified' import {visit} from 'unist-util-visit' import {VFile} from 'vfile' -const safeProtocols = ['http', 'https', 'mailto', 'tel'] - const own = {}.hasOwnProperty const changelog = 'https://github.com/remarkjs/react-markdown/blob/main/changelog.md' @@ -99,6 +98,7 @@ const changelog = const emptyPlugins = [] /** @type {Readonly} */ const emptyRemarkRehypeOptions = {allowDangerousHtml: true} +const safeProtocol = /^(https?|ircs?|mailto|xmpp)$/i // Mutable because we `delete` any time it’s used and a message is sent. /** @type {ReadonlyArray>} */ @@ -293,38 +293,5 @@ export function Markdown(options) { * Safe URL. */ export function defaultUrlTransform(value) { - const url = value.trim() - const first = url.charAt(0) - - if (first === '#' || first === '/') { - return url - } - - const colon = url.indexOf(':') - if (colon === -1) { - return url - } - - for (const protocol of safeProtocols) { - if ( - colon === protocol.length && - url.slice(0, protocol.length).toLowerCase() === protocol - ) { - return url - } - } - - let index = url.indexOf('?') - if (index !== -1 && colon > index) { - return url - } - - index = url.indexOf('#') - if (index !== -1 && colon > index) { - return url - } - - // To do: is there an alternative? - // eslint-disable-next-line no-script-url - return 'javascript:void(0)' + return sanitizeUri(value, safeProtocol) } diff --git a/test.jsx b/test.jsx index 120bf26a..1a56847c 100644 --- a/test.jsx +++ b/test.jsx @@ -288,43 +288,31 @@ test('react-markdown', async function (t) { }) await t.test('should make a `javascript:` URL safe', function () { - const consoleError = console.error - console.error = noop assert.equal( asHtml(), - '

' + '

' ) - console.error = consoleError }) await t.test('should make a `vbscript:` URL safe', function () { - const consoleError = console.error - console.error = noop assert.equal( asHtml(), - '

' + '

' ) - console.error = consoleError }) await t.test('should make a `VBSCRIPT:` URL safe', function () { - const consoleError = console.error - console.error = noop assert.equal( asHtml(), - '

' + '

' ) - console.error = consoleError }) await t.test('should make a `file:` URL safe', function () { - const consoleError = console.error - console.error = noop assert.equal( asHtml(), - '

' + '

' ) - console.error = consoleError }) await t.test('should allow an empty URL', function () { @@ -1054,8 +1042,3 @@ test('react-markdown', async function (t) { function asHtml(input) { return renderToStaticMarkup(input) } - -/** - * @returns {undefined} - */ -function noop() {}