diff --git a/.changeset/tricky-poems-collect.md b/.changeset/tricky-poems-collect.md
new file mode 100644
index 00000000..0ee84dcf
--- /dev/null
+++ b/.changeset/tricky-poems-collect.md
@@ -0,0 +1,24 @@
+---
+'markdown-to-jsx': minor
+---
+
+Allow modifying HTML attribute sanitization when `options.sanitizer` is passed by the composer.
+
+By default a lightweight URL sanitizer function is provided to avoid common attack vectors that might be placed into the `href` of an anchor tag, for example. The sanitizer receives the input, the HTML tag being targeted, and the attribute name. The original function is available as a library export called `sanitizer`.
+
+This can be overridden and replaced with a custom sanitizer if desired via `options.sanitizer`:
+
+```jsx
+// sanitizer in this situation would receive:
+// ('javascript:alert("foo")', 'a', 'href')
+
+; value }}>
+ {`[foo](javascript:alert("foo"))`}
+
+
+// or
+
+compiler('[foo](javascript:alert("foo"))', {
+ sanitizer: (value, tag, attribute) => value,
+})
+```
diff --git a/README.md b/README.md
index 2146e272..8f32a817 100644
--- a/README.md
+++ b/README.md
@@ -19,6 +19,7 @@ The most lightweight, customizable React markdown component.
- [options.createElement - Custom React.createElement behavior](#optionscreateelement---custom-reactcreateelement-behavior)
- [options.enforceAtxHeadings](#optionsenforceatxheadings)
- [options.renderRule](#optionsrenderrule)
+ - [options.sanitizer](#optionssanitizer)
- [options.slugify](#optionsslugify)
- [options.namedCodesToUnicode](#optionsnamedcodestounicode)
- [options.disableParsingRawHTML](#optionsdisableparsingrawhtml)
@@ -435,21 +436,44 @@ function App() {
}
````
+#### options.sanitizer
+
+By default a lightweight URL sanitizer function is provided to avoid common attack vectors that might be placed into the `href` of an anchor tag, for example. The sanitizer receives the input, the HTML tag being targeted, and the attribute name. The original function is available as a library export called `sanitizer`.
+
+This can be overridden and replaced with a custom sanitizer if desired via `options.sanitizer`:
+
+```jsx
+// sanitizer in this situation would receive:
+// ('javascript:alert("foo")', 'a', 'href')
+
+; value }}>
+ {`[foo](javascript:alert("foo"))`}
+
+
+// or
+
+compiler('[foo](javascript:alert("foo"))', {
+ sanitizer: (value, tag, attribute) => value,
+})
+```
+
#### options.slugify
By default, a [lightweight deburring function](https://github.com/probablyup/markdown-to-jsx/blob/bc2f57412332dc670f066320c0f38d0252e0f057/index.js#L261-L275) is used to generate an HTML id from headings. You can override this by passing a function to `options.slugify`. This is helpful when you are using non-alphanumeric characters (e.g. Chinese or Japanese characters) in headings. For example:
```jsx
-; str }}># 中文
+ str }}># 中文
// or
compiler('# 中文', { slugify: str => str })
// renders:
-;
中文
+
中文
```
+The original function is available as a library export called `slugify`.
+
#### options.namedCodesToUnicode
By default only a couple of named html codes are converted to unicode characters:
diff --git a/index.compiler.spec.tsx b/index.compiler.spec.tsx
index 01379bee..08d3d39f 100644
--- a/index.compiler.spec.tsx
+++ b/index.compiler.spec.tsx
@@ -1,4 +1,4 @@
-import { compiler, RuleType } from './index'
+import { compiler, sanitizer, RuleType } from './index'
import * as React from 'react'
import * as ReactDOM from 'react-dom'
import * as fs from 'fs'
@@ -1180,6 +1180,46 @@ describe('links', () => {
`)
})
+ it('should not sanitize markdown when explicitly disabled', () => {
+ jest.spyOn(console, 'warn').mockImplementation(() => {})
+ jest.spyOn(console, 'error').mockImplementation(() => {})
+
+ render(compiler('[foo](javascript:doSomethingBad)', { sanitizer: x => x }))
+
+ expect(root.innerHTML).toMatchInlineSnapshot(`
+
+ foo
+
+ `)
+
+ expect(console.warn).not.toHaveBeenCalled()
+ })
+
+ it('tag and attribute are provided to allow for conditional override', () => {
+ jest.spyOn(console, 'warn').mockImplementation(() => {})
+ jest.spyOn(console, 'error').mockImplementation(() => {})
+
+ render(
+ compiler(
+ '[foo](javascript:doSomethingBad)\n![foo](javascript:doSomethingBad)',
+ {
+ sanitizer: (value, tag) => (tag === 'a' ? value : sanitizer(value)),
+ }
+ )
+ )
+
+ expect(root.innerHTML).toMatchInlineSnapshot(`
+