typescript.react.security.audit.react-unsanitized-property.react-unsanitized-property

profile photo of semgrepsemgrep
Author
7,258
Download Count*

Detection of $HTML from non-constant definition. This can inadvertently expose users to cross-site scripting (XSS) attacks if this comes from user-provided input. If you have to use $HTML, consider using a sanitization library such as DOMPurify to sanitize your HTML.

Run Locally

Run in CI

Defintion

rules:
  - id: react-unsanitized-property
    message: Detection of $HTML from non-constant definition. This can inadvertently
      expose users to cross-site scripting (XSS) attacks if this comes from
      user-provided input. If you have to use $HTML, consider using a
      sanitization library such as DOMPurify to sanitize your HTML.
    metadata:
      cwe:
        - "CWE-79: Improper Neutralization of Input During Web Page Generation
          ('Cross-site Scripting')"
      owasp:
        - A07:2017 - Cross-Site Scripting (XSS)
        - A03:2021 - Injection
      references:
        - https://react.dev/reference/react-dom/components/common#dangerously-setting-the-inner-html
      category: security
      confidence: MEDIUM
      technology:
        - react
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]
      cwe2022-top25: true
      cwe2021-top25: true
      subcategory:
        - vuln
      likelihood: MEDIUM
      impact: MEDIUM
      vulnerability_class:
        - Cross-Site-Scripting (XSS)
    languages:
      - typescript
      - javascript
    severity: WARNING
    mode: taint
    pattern-sources:
      - patterns:
          - pattern-either:
              - pattern-inside: |
                  function ...({..., $X, ...}) { ... }
              - pattern-inside: |
                  function ...(..., $X, ...) { ... }
          - focus-metavariable: $X
          - pattern-either:
              - pattern: $X.$Y
              - pattern: $X[...]
    pattern-sinks:
      - patterns:
          - pattern-either:
              - pattern-inside: |
                  $BODY = $REACT.useRef(...)
                  ...
              - pattern-inside: |
                  $BODY = useRef(...)
                  ...
              - pattern-inside: |
                  $BODY = findDOMNode(...)
                  ...
              - pattern-inside: |
                  $BODY = createRef(...)
                  ...
              - pattern-inside: |
                  $BODY = $REACT.findDOMNode(...)
                  ...
              - pattern-inside: |
                  $BODY = $REACT.createRef(...)
                  ...
          - pattern-either:
              - pattern: |
                  $BODY. ... .$HTML = $SINK 
              - pattern: |
                  $BODY.$HTML = $SINK  
          - metavariable-regex:
              metavariable: $HTML
              regex: (innerHTML|outerHTML)
          - focus-metavariable: $SINK
      - patterns:
          - pattern-either:
              - pattern: ReactDOM.findDOMNode(...).$HTML = $SINK
          - metavariable-regex:
              metavariable: $HTML
              regex: (innerHTML|outerHTML)
          - focus-metavariable: $SINK
    pattern-sanitizers:
      - patterns:
          - pattern-either:
              - pattern-inside: |
                  import $S from "underscore.string"
                  ...
              - pattern-inside: |
                  import * as $S from "underscore.string"
                  ...
              - pattern-inside: |
                  import $S from "underscore.string"
                  ...
              - pattern-inside: |
                  $S = require("underscore.string")
                  ...
          - pattern-either:
              - pattern: $S.escapeHTML(...)
      - patterns:
          - pattern-either:
              - pattern-inside: |
                  import $S from "dompurify"
                  ...
              - pattern-inside: |
                  import { ..., $S,... } from "dompurify"
                  ...
              - pattern-inside: |
                  import * as $S from "dompurify"
                  ...
              - pattern-inside: |
                  $S = require("dompurify")
                  ...
              - pattern-inside: |
                  import $S from "isomorphic-dompurify"
                  ...
              - pattern-inside: |
                  import * as $S from "isomorphic-dompurify"
                  ...
              - pattern-inside: |
                  $S = require("isomorphic-dompurify")
                  ...
          - pattern-either:
              - patterns:
                  - pattern-inside: |
                      $VALUE = $S(...)
                      ...
                  - pattern: $VALUE.sanitize(...)
              - patterns:
                  - pattern-inside: |
                      $VALUE = $S.sanitize
                      ...
                  - pattern: $S(...)
              - pattern: $S.sanitize(...)
              - pattern: $S(...)
      - patterns:
          - pattern-either:
              - pattern-inside: |
                  import $S from 'xss';
                  ...
              - pattern-inside: |
                  import * as $S from 'xss';
                  ...
              - pattern-inside: |
                  $S = require("xss")
                  ...
          - pattern: $S(...)
      - patterns:
          - pattern-either:
              - pattern-inside: |
                  import $S from 'sanitize-html';
                  ...
              - pattern-inside: |
                  import * as $S from "sanitize-html";
                  ...
              - pattern-inside: |
                  $S = require("sanitize-html")
                  ...
          - pattern: $S(...)
      - patterns:
          - pattern-either:
              - pattern-inside: |
                  $S = new Remarkable()
                  ...
          - pattern: $S.render(...)

Examples

react-unsanitized-property.jsx

function Test2(input) {
  // ruleid: react-unsanitized-property
    ReactDOM.findDOMNode(this.someRef).outerHTML = input.value;
  }
  
  function OkTest1() {
  // ok: react-unsanitized-property
    this.element.innerHTML = "<a href='/about.html'>About</a>";
  }
  
  function OkTest2() {
  // ok: react-unsanitized-property
    ReactDOM.findDOMNode(this.someRef).outerHTML = "<a href='/about.html'>About</a>";
  }
  

react-unsanitized-property.tsx

function Test2(input) {
  // ruleid: react-unsanitized-property
    ReactDOM.findDOMNode(this.someRef).outerHTML = input.value;
  }
  
  function OkTest1() {
  // ok: react-unsanitized-property
    this.element.innerHTML = "<a href='/about.html'>About</a>";
  }
  
  function OkTest2() {
  // ok: react-unsanitized-property
    ReactDOM.findDOMNode(this.someRef).outerHTML = "<a href='/about.html'>About</a>";
  }