Skip to content
Logo Theodo

RisXSS, the missing ESLint rule for React and Vue

Clément Escolano3 min read

RisXSS

XSS attacks are expected to be ranked 3rd in the OWASP Top 10, compared to their 7th rank in 2021 (source). The pace of application creations is increasing and so is the surface of attacks, so making your applications secure is more and more challenging and needed. React and Vue are some of the most used frameworks to build applications and include built-in protection for some types of XSS by escaping user input by default. However, there may be some cases when they need to display user content as raw HTML (a blog post with style for instance).

export const BlogPost = ({ post }) => {
  return (
    <div dangerouslySetInnerHTML={post} />
  );
};

(All the examples in this article use React but are similar with Vue).

A first attempt to prevent XSS attacks

This opens a large hole in the XSS protection. A secure solution is to first sanitize the content with a library that will remove unsafe parts of the content while keeping useful tags and attributes (for styling purpose for instance). The most popular libraries for this are DOMPurify and js-xss.

This solution is not always known though. On a website I worked on, I saw the use of dangerouslySetInnerHTML that was not properly sanitized and introduced a real XSS vulnerability. My first reaction back then was: “How can I make sure this will never happen again?” So I searched the Internet and I happily found ESLint rules designed precisely to prevent the use of dangerouslySetInnerHTML and v-html. I added these rules on my projects and some time later, again, I found another XSS vulnerability.

How did it happen? Easy: some developer wanted to display sanitized content with style, so they used v-html while sanitizing the output first and added a comment to disable the ESLint rule for this line. This is correct in this case. Then, another developer copied-pasted (who does not?) this code in another page, but the content was not sanitized.

When you have no other means than to disable a rule to do something, disabling the rule becomes the norm, and the rule loses its purpose.

Custom ESLint rule to the rescue

This is when I talked with some colleagues and started a custom ESLint rule to warn about insecure HTML only if the HTML is not sanitized. Corentin Normand and Guillaume Klaus implemented the logic (and even wrote a tutorial about it) and it became RisXSS.

RisXSS warns you about insecure use of dangerouslySetInnerHTML and v-html but only if the content is not sanitized. The following snippet will not display a warning:

import { sanitize } from 'dompurify';

export const BlogPost = ({ post }) => {
  return (
    <div dangerouslySetInnerHTML={{ __html: sanitize(post) }} />
  );
};

By offering a smart ESLint rule, RisXSS achieves the best of both worlds:

Now that you understand the need of RisXSS, go check out how to install it on your project on the RisXSS homepage.

Liked this article?