javascript.express.security.express-xml2json-xxe.express-xml2json-xxe

profile photo of semgrepsemgrep
Author
3,077
Download Count*

Make sure that unverified user data can not reach the XML Parser, as it can result in XML External or Internal Entity (XXE) Processing vulnerabilities

Run Locally

Run in CI

Defintion

rules:
  - id: express-xml2json-xxe
    message: Make sure that unverified user data can not reach the XML Parser, as it
      can result in XML External or Internal Entity (XXE) Processing
      vulnerabilities
    metadata:
      owasp:
        - A04:2017 - XML External Entities (XXE)
        - A05:2021 - Security Misconfiguration
      cwe:
        - "CWE-611: Improper Restriction of XML External Entity Reference"
      asvs:
        section: V5 Validation, Sanitization and Encoding
        control_id: 5.5.2 Insecue XML Deserialization
        control_url: https://github.com/OWASP/ASVS/blob/master/4.0/en/0x13-V5-Validation-Sanitization-Encoding.md#v55-deserialization-prevention
        version: "4"
      category: security
      technology:
        - express
      references:
        - https://www.npmjs.com/package/xml2json
      cwe2022-top25: true
      cwe2021-top25: true
      subcategory:
        - vuln
      likelihood: MEDIUM
      impact: MEDIUM
      confidence: MEDIUM
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]
      vulnerability_class:
        - XML Injection
    languages:
      - javascript
      - typescript
    severity: ERROR
    mode: taint
    pattern-sources:
      - patterns:
          - pattern-either:
              - pattern-inside: function ... ($REQ, $RES) {...}
              - pattern-inside: function ... ($REQ, $RES, $NEXT) {...}
              - patterns:
                  - pattern-either:
                      - pattern-inside: $APP.$METHOD(..., function $FUNC($REQ, $RES) {...})
                      - pattern-inside: $APP.$METHOD(..., function $FUNC($REQ, $RES, $NEXT) {...})
                  - metavariable-regex:
                      metavariable: $METHOD
                      regex: ^(get|post|put|head|delete|options)$
          - pattern-either:
              - pattern: $REQ.query
              - pattern: $REQ.body
              - pattern: $REQ.params
              - pattern: $REQ.cookies
              - pattern: $REQ.headers
              - pattern: $REQ.files.$ANYTHING.data.toString('utf8')
              - pattern: $REQ.files.$ANYTHING['data'].toString('utf8')
      - patterns:
          - pattern-either:
              - pattern-inside: |
                  ({ $REQ }: Request,$RES: Response, $NEXT: NextFunction) =>
                  {...}
              - pattern-inside: |
                  ({ $REQ }: Request,$RES: Response) => {...}
          - focus-metavariable: $REQ
          - pattern-either:
              - pattern: params
              - pattern: query
              - pattern: cookies
              - pattern: headers
              - pattern: body
              - pattern: files.$ANYTHING.data.toString('utf8')
              - pattern: files.$ANYTHING['data'].toString('utf8')
    pattern-sinks:
      - patterns:
          - pattern-either:
              - pattern-inside: |
                  require('xml2json');
                  ...
              - pattern-inside: |
                  import 'xml2json';
                  ...
          - pattern: $EXPAT.toJson($SINK,...)
          - focus-metavariable: $SINK

Examples

express-xml2json-xxe.js

function test1() {
    const express = require('express')
    const xml2json = require('xml2json')
    const app = express()
    const port = 3000

    app.get('/', (req, res) => {
        const xml = req.query.xml
        // ruleid: express-xml2json-xxe
        const content = xml2json.toJson(xml, {coerce: true, object: true});
        res.send(content)
    })

    app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`))
}

function test2() {
    const express = require('express')
    const xml2json = require('xml2json')
    const app = express()
    const port = 3000

    app.get('/', (req, res) => {
        // ruleid: express-xml2json-xxe
        const content = xml2json.toJson(req.body, {coerce: true, object: true});
        res.send(content)
    })

    app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`))
}

function okTest() {
    const express = require('express')
    const xml2json = require('xml2json')
    const app = express()
    const port = 3000

    app.get('/', (req, res) => {
        // ok: express-xml2json-xxe
        const content = expat.toJson(someVerifiedData(), {coerce: true, object: true});
        res.send(content)
    })

    app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`))
}