contrib.nodejsscan.xxe_node.node_xxe

profile photo of returntocorpreturntocorp
Author
99
Download Count*
License

User controlled data in XML parsers can result in XML External or Internal Entity (XXE) Processing vulnerabilities

Run Locally

Run in CI

Defintion

rules:
  - id: node_xxe
    patterns:
      - pattern-either:
          - pattern-inside: function ($REQ, $RES, ...) {...}
          - pattern-inside: function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: $X = function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: var $X = function $FUNC($REQ, $RES, ...) {...};
          - pattern-inside: $APP.$METHOD(..., function $FUNC($REQ, $RES, ...) {...})
      - pattern-either:
          - pattern: |
              $LIBXML.parseXmlString(..., <... $REQ.$QUERY.$VAR.$FILE ...>, ...)
          - pattern: |
              $LIBXML.parseXmlString(..., <... $REQ.$QUERY.$VAR ...>, ...)
          - pattern: |
              $LIBXML.parseXmlString(..., <... $REQ.$QUERY ...>, ...)
          - pattern: >
              $FOO = <... $REQ.$QUERY.$VAR.$FILE ...>; ...
              $LIBXML.parseXmlString(..., <... $FOO ...>, ...);
          - pattern: >
              $FOO = <... $REQ.$QUERY.$VAR ...>; ... $LIBXML.parseXmlString(...,
              <... $FOO ...>, ...);
          - pattern: >
              $FOO = <... $REQ.$QUERY ...>; ... $LIBXML.parseXmlString(..., <...
              $FOO ...>, ...);
          - pattern: |
              $LIBXML.parseXml(..., <... $REQ.$QUERY.$VAR.$FILE ...>, ...)
          - pattern: |
              $LIBXML.parseXml(..., <... $REQ.$QUERY.$VAR ...>, ...)
          - pattern: |
              $LIBXML.parseXml(..., <... $REQ.$QUERY ...>, ...)
          - pattern: >
              $FOO = <... $REQ.$QUERY.$VAR.$FILE ...>; ... $LIBXML.parseXml(...,
              <... $FOO ...>, ...);
          - pattern: >
              $FOO = <... $REQ.$QUERY.$VAR ...>; ... $LIBXML.parseXml(..., <...
              $FOO ...>, ...);
          - pattern: |
              $FOO = <... $REQ.$QUERY ...>;
              ...
              $LIBXML.parseXml(..., <... $FOO ...>, ...);
          - pattern: |
              $PARSER = new libxmljs.SaxParser();
              ...
              $PARSER.parseString(..., <... $REQ.$QUERY ...>, ...);
          - pattern: |
              $PARSER = new libxmljs.SaxParser();
              ...
              $PARSER.parseString(..., <... $REQ.$QUERY.$BAR ...>, ...);
          - pattern: |
              $PARSER = new libxmljs.SaxParser();
              ...
              $PARSER.parseString(..., <... $REQ.$QUERY.$BAR.$FILE ...>, ...);
          - pattern: |
              $PARSER = new libxmljs.SaxPushParser();
              ...
              $PARSER.push(..., <... $REQ.$QUERY ...>, ...);
          - pattern: |
              $PARSER = new libxmljs.SaxPushParser();
              ...
              $PARSER.push(..., <... $REQ.$QUERY.$FOO ...> , ...);
          - pattern: |
              $PARSER = new libxmljs.SaxPushParser();
              ...
              $PARSER.push(..., <... $REQ.$QUERY.$FOO.$FILE ...> , ...);
          - pattern: |
              $PARSER = new libxmljs.SaxParser();
              ...
              $FOO = <... $REQ.$QUERY ...>;
              ...
              $PARSER.parseString(..., <... $FOO ...>, ...);
          - pattern: |
              $PARSER = new libxmljs.SaxParser();
              ...
              $FOO = <... $REQ.$QUERY.$BAR ...>;
              ...
              $PARSER.parseString(..., <... $FOO ...>, ...);
          - pattern: |
              $PARSER = new libxmljs.SaxParser();
              ...
              $FOO = <... $REQ.$QUERY.$BAR.$FILE ...>;
              ...
              $PARSER.parseString(..., <... $FOO ...>, ...);
          - pattern: |
              $PARSER = new libxmljs.SaxPushParser();
              ...
              $FOO = <... $REQ.$QUERY ...>;
              ...
              $PARSER.push(..., <... $FOO ...>, ...);
          - pattern: |
              $PARSER = new libxmljs.SaxPushParser();
              ...
              $FOO = <... $REQ.$QUERY.$BAR ...>;
              ...
              $PARSER.push(..., <... $FOO ...> , ...);
          - pattern: |
              $PARSER = new libxmljs.SaxPushParser();
              ...
              $FOO = <... $REQ.$QUERY.$BAR.$FILE ...>;
              ...
              $PARSER.push(..., <... $FOO ...> , ...);
    message: User controlled data in XML parsers can result in XML External or
      Internal Entity (XXE) Processing vulnerabilities
    languages:
      - javascript
    severity: ERROR
    metadata:
      owasp: A04:2017 - XML External Entities (XXE)
      cwe: "CWE-611: Improper Restriction of XML External Entity Reference"
      category: security
      technology:
        - node.js
        - express
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]

Examples

xxe_node.js


const libxmljs = require('libxmljs');

app.get('/noent', function (req, res) {
    // entity expansion
    // ruleid:node_xxe
    libxmljs.parseXml(req.param("xml"), { noent: true });
});


app.get('/sax', function (req, res) {
    // SAX parser expands external entities
    // ruleid:node_xxe
    const parser = new libxmljs.SaxParser();
    const x = 1
    parser.parseString(req.param("xml"));
});


app.get('/saxpush/parser', function (req, res) {
    // SAX parser expands external entities
    // ruleid:node_xxe
    const parser = new libxmljs.SaxPushParser();
    const x = 1
    parser.push(req.param("some-xml"));
});


app.get('/sax', function (req, res) {
    // SAX parser expands external entities
    const parser = new libxmljs.SaxParser();
    const x = 1
    // ruleid:node_xxe
    var products = parser.parseXmlString(req.files.products.data, { noent: true, noblanks: true })
})

const express = require('express')
const libxmljs = require('libxml')
const db = require('db');
const router = express.Router()

router.post('/upload-products', (req, res) => {
    // ruleid:node_xxe
    const XMLfile = req.files.products.data;
    const products = libxmljs.parseXmlString(XMLfile, { noent: true, noblanks: true })

    products.root().childNodes().forEach(product => {
        let newProduct = new db.Product()
        newProduct.name = product.childNodes()[0].text()
        newProduct.description = product.childNodes()[3].text()
        newProduct.save()
    });

    res.send('Thanks')
})

module.exports = router