49

I've got some user generated html markup from a text area and I'd like to render it on another part of the screen. The markup is saved as a string in the props of the component.

I don't want to use dangerouslysethtml for obvious reasons. Is there a parser such as marked but for html so that it strips out script tags and other invalid html.

silverlight513
  • 5,268
  • 4
  • 26
  • 38

3 Answers3

65

Sanitize the html using the sanitize-html module, and render the sanitized string using dangerouslySetInnerHTML.

You can create a simple wrapper component:

const defaultOptions = {
  allowedTags: [ 'b', 'i', 'em', 'strong', 'a' ],
  allowedAttributes: {
    'a': [ 'href' ]
  },
  allowedIframeHostnames: ['www.youtube.com']
};

const sanitize = (dirty, options) => ({
  __html: sanitizeHtml(
    dirty, 
    options: { ...defaultOptions, ...options }
  )
});

const SanitizeHTML = ({ html, options }) => (
  <div dangerouslySetInnerHTML={sanitize(html, options)} />
);

Usage:

<SanitizeHTML html="<img src=x onerror=alert('img') />" />

You can also use react-sanitized-html's SanitizedHTML component, which is a react wrapper around sanitize-html:

<SanitizedHTML
  allowedAttributes={{ 'a': ['href'] }}
  allowedTags={['a']}
  html={ `<a href="http://bing.com/">Bing</a>` }
/>
Ori Drori
  • 183,571
  • 29
  • 224
  • 209
  • I don't understand how `react-sanitized-html@2.0.0` is just 2kb if is a wrapper of `sanitize-html`, when `sanitize-html@1.20.0` is 157.2kB :/ ! – Aral Roca Apr 03 '19 at 07:34
  • Ok, because is needed to install both packages for separate... react-sanitized-html and sanitize-html :/ – Aral Roca Apr 03 '19 at 07:37
  • 3
    Because sanitize-html is a peer dependency, and not actual part of the package. – Ori Drori Apr 03 '19 at 07:42
  • Hi there @ori , I'm following ur instruciton but i got error on my compiler `[11:40:00] Starting 'browserify_typescript'... [11:40:04] 'browserify_typescript' errored after 4.29 s [11:40:04] Error in plugin 'gulp-browserify' module "./tagtypes" not found from "/d/myProject/web/node_modules/sanitize-html/dist/sanitize-html.js"` it said ./tagtypes not found – dmh Mar 17 '20 at 05:09
  • This is a build related issue. You should open a new question. – Ori Drori Mar 17 '20 at 06:34
32

An example based on the accepted answer:

import sanitizeHtml from 'sanitize-html';

const MyComponent = () => {
  dirty = '<a href="my-slug" target="_blank" onClick="evil()">click</a>';
  const clean = sanitizeHtml(dirty, {
    allowedTags: ['b', 'i', 'em', 'strong', 'a'],
    allowedAttributes: {
      a: ['href', 'target']
    }
  });
  return (
    <div 
      dangerouslySetInnerHTML={{__html: clean}}
    />
  );
};

MyComponent renders div containing the link without onClick="evil()":

<a href="my-slug" target="_blank">click</a>
joshweir
  • 5,427
  • 3
  • 39
  • 59
15

For XSS filtering, there is a good alternative to sanitize-html written by security people, called dompurify.

Here is how the wrapper from https://stackoverflow.com/a/38663813/1762849 would look with DOMPurify:

const defaultOptions = {
  ALLOWED_TAGS: [ 'b', 'i', 'em', 'strong', 'a' ], 
  ALLOWED_ATTR: ['href']
};

const sanitize = (dirty, options) => ({
  __html: DOMPurify.sanitize(
    dirty, 
    { ...defaultOptions, ...options }
  )
});

const SanitizeHTML = ({ html, options }) => (
  <div dangerouslySetInnerHTML={sanitize(html, options)} />
);

Usage:

<SanitizeHTML html="<img src=x onerror=alert('img') />" />

Also, if you you need to sanitize HTML on client and server, consider using the isomophic-dompurify module which unifies use of DOMPurify on frontend and backend.

Phill Healey
  • 3,084
  • 2
  • 33
  • 67
Konstantin Komelin
  • 666
  • 1
  • 8
  • 12