typescript.react.security.audit.react-unsanitized-method.react-unsanitized-method

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-method
    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://developer.mozilla.org/en-US/docs/Web/API/Document/writeln
        - https://developer.mozilla.org/en-US/docs/Web/API/Document/write
        - https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML
      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: HIGH
      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: |
                  this.window.document. ... .$HTML('...',$SINK) 
              - pattern: |
                  window.document. ... .$HTML('...',$SINK) 
              - pattern: |
                  document.$HTML($SINK)  
          - metavariable-regex:
              metavariable: $HTML
              regex: (writeln|write)
          - focus-metavariable: $SINK
      - patterns:
          - pattern-either:
              - pattern: |
                  $PROP. ... .$HTML('...',$SINK) 
          - metavariable-regex:
              metavariable: $HTML
              regex: (insertAdjacentHTML)
          - 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-method.jsx

function Test1({input}) {
  // ruleid: react-unsanitized-method
    this.ref.insertAdjacentHTML('afterend', input.foo);
  }
  
  function Test2({input}) {
  // ruleid: react-unsanitized-method
    document.write(input.foo);
  }
  
  function Test3 () {
  // ok: react-unsanitized-method
    document.writeln(input);
  }
  
  function OkTest1 () {
  // ok: react-unsanitized-method
    this.ref.insertAdjacentHTML('afterend', '<div id="two">two</div>');
  }
  
  function OkTest2 () {
  // ok: react-unsanitized-method
    document.write("<h1>foobar</h1>");
  }
  
  function OkTest3 () {
  // ok: react-unsanitized-method
    document.writeln("<p>foobar</p>");
  }
  

react-unsanitized-method.tsx

function Test1({input}) {
  // ruleid: react-unsanitized-method
    this.ref.insertAdjacentHTML('afterend', input.foo);
  }
  
  function Test2({input}) {
  // ruleid: react-unsanitized-method
    document.write(input.foo);
  }
  
  function Test3 () {
  // ok: react-unsanitized-method
    document.writeln(input);
  }
  
  function OkTest1 () {
  // ok: react-unsanitized-method
    this.ref.insertAdjacentHTML('afterend', '<div id="two">two</div>');
  }
  
  function OkTest2 () {
  // ok: react-unsanitized-method
    document.write("<h1>foobar</h1>");
  }
  
  function OkTest3 () {
  // ok: react-unsanitized-method
    document.writeln("<p>foobar</p>");
  }