+
+
+
+Parsing untrusted XML files with a weakly configured XML parser may lead to an
+XML External Entity (XXE) attack. This type of attack uses external entity references
+to access arbitrary files on a system, carry out denial-of-service (DoS) attacks, or server-side
+request forgery. Even when the result of parsing is not returned to the user, DoS attacks are still possible
+and out-of-band data retrieval techniques may allow attackers to steal sensitive data.
+
+
+
+
+
+The easiest way to prevent XXE attacks is to disable external entity handling when
+parsing untrusted data. How this is done depends on the library being used. Note that some
+libraries, such as recent versions of libxml, disable entity expansion by default,
+so unless you have explicitly enabled entity expansion, no further action needs to be taken.
+
+
+
+
+
+The following example uses the libxml XML parser to parse a string xmlSrc.
+If that string is from an untrusted source, this code may be vulnerable to an XXE attack, since
+the parser is invoked with the noent option set to true:
+
+
+
+
+To guard against XXE attacks, the noent option should be omitted or set to
+false. This means that no entity expansion is undertaken at all, not even for standard
+internal entities such as & or >. If desired, these
+entities can be expanded in a separate step using utility functions provided by libraries such
+as underscore,
+lodash or
+he.
+
+
+
+
+
+
+OWASP:
+XML External Entity (XXE) Processing.
+
+
+Timothy Morgen:
+XML Schema, DTD, and Entity Attacks.
+
+
+Timur Yunusov, Alexey Osipov:
+XML Out-Of-Band Data Retrieval.
+
+
+
diff --git a/python/ql/src/experimental/Security/NEW/CWE-611/Xxe.ql b/python/ql/src/experimental/Security/NEW/CWE-611/Xxe.ql
new file mode 100644
index 00000000000..01e518b6df7
--- /dev/null
+++ b/python/ql/src/experimental/Security/NEW/CWE-611/Xxe.ql
@@ -0,0 +1,23 @@
+/**
+ * @name XML external entity expansion
+ * @description Parsing user input as an XML document with external
+ * entity expansion is vulnerable to XXE attacks.
+ * @kind path-problem
+ * @problem.severity error
+ * @security-severity 9.1
+ * @precision high
+ * @id js/xxe
+ * @tags security
+ * external/cwe/cwe-611
+ * external/cwe/cwe-827
+ */
+
+import javascript
+import semmle.javascript.security.dataflow.XxeQuery
+import DataFlow::PathGraph
+
+from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
+where cfg.hasFlowPath(source, sink)
+select sink.getNode(), source, sink,
+ "A $@ is parsed as XML without guarding against external entity expansion.", source.getNode(),
+ "user-provided value"
diff --git a/python/ql/src/experimental/Security/NEW/CWE-611/examples/Xxe.js b/python/ql/src/experimental/Security/NEW/CWE-611/examples/Xxe.js
new file mode 100644
index 00000000000..99fa02cc42f
--- /dev/null
+++ b/python/ql/src/experimental/Security/NEW/CWE-611/examples/Xxe.js
@@ -0,0 +1,7 @@
+const app = require("express")(),
+ libxml = require("libxmljs");
+
+app.post("upload", (req, res) => {
+ let xmlSrc = req.body,
+ doc = libxml.parseXml(xmlSrc, { noent: true });
+});
diff --git a/python/ql/src/experimental/Security/NEW/CWE-611/examples/XxeGood.js b/python/ql/src/experimental/Security/NEW/CWE-611/examples/XxeGood.js
new file mode 100644
index 00000000000..8317dcac98f
--- /dev/null
+++ b/python/ql/src/experimental/Security/NEW/CWE-611/examples/XxeGood.js
@@ -0,0 +1,7 @@
+const app = require("express")(),
+ libxml = require("libxmljs");
+
+app.post("upload", (req, res) => {
+ let xmlSrc = req.body,
+ doc = libxml.parseXml(xmlSrc);
+});
diff --git a/python/ql/src/experimental/Security/NEW/CWE-776/XmlBomb.qhelp b/python/ql/src/experimental/Security/NEW/CWE-776/XmlBomb.qhelp
new file mode 100644
index 00000000000..c0714b3f96f
--- /dev/null
+++ b/python/ql/src/experimental/Security/NEW/CWE-776/XmlBomb.qhelp
@@ -0,0 +1,60 @@
+
+
+
+
+
+Parsing untrusted XML files with a weakly configured XML parser may be vulnerable to
+denial-of-service (DoS) attacks exploiting uncontrolled internal entity expansion.
+
+
+In XML, so-called internal entities are a mechanism for introducing an abbreviation
+for a piece of text or part of a document. When a parser that has been configured
+to expand entities encounters a reference to an internal entity, it replaces the entity
+by the data it represents. The replacement text may itself contain other entity references,
+which are expanded recursively. This means that entity expansion can increase document size
+dramatically.
+
+
+If untrusted XML is parsed with entity expansion enabled, a malicious attacker could
+submit a document that contains very deeply nested entity definitions, causing the parser
+to take a very long time or use large amounts of memory. This is sometimes called an
+XML bomb attack.
+
+
+
+
+
+The safest way to prevent XML bomb attacks is to disable entity expansion when parsing untrusted
+data. How this is done depends on the library being used. Note that some libraries, such as
+recent versions of libxmljs (though not its SAX parser API), disable entity expansion
+by default, so unless you have explicitly enabled entity expansion, no further action is needed.
+
+
+
+
+
+The following example uses the XML parser provided by the node-expat package to
+parse a string xmlSrc. If that string is from an untrusted source, this code may be
+vulnerable to a DoS attack, since node-expat expands internal entities by default:
+
+
+
+
+At the time of writing, node-expat does not provide a way of controlling entity
+expansion, but the example could be rewritten to use the sax package instead,
+which only expands standard entities such as &:
+
+
+
+
+
+
+Wikipedia:
+Billion Laughs.
+
+
+Bryan Sullivan:
+Security Briefs - XML Denial of Service Attacks and Defenses.
+
+
+
diff --git a/python/ql/src/experimental/Security/NEW/CWE-776/XmlBomb.ql b/python/ql/src/experimental/Security/NEW/CWE-776/XmlBomb.ql
new file mode 100644
index 00000000000..c340eee68cc
--- /dev/null
+++ b/python/ql/src/experimental/Security/NEW/CWE-776/XmlBomb.ql
@@ -0,0 +1,23 @@
+/**
+ * @name XML internal entity expansion
+ * @description Parsing user input as an XML document with arbitrary internal
+ * entity expansion is vulnerable to denial-of-service attacks.
+ * @kind path-problem
+ * @problem.severity warning
+ * @security-severity 7.5
+ * @precision high
+ * @id js/xml-bomb
+ * @tags security
+ * external/cwe/cwe-776
+ * external/cwe/cwe-400
+ */
+
+import javascript
+import semmle.javascript.security.dataflow.XmlBombQuery
+import DataFlow::PathGraph
+
+from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
+where cfg.hasFlowPath(source, sink)
+select sink.getNode(), source, sink,
+ "A $@ is parsed as XML without guarding against uncontrolled entity expansion.", source.getNode(),
+ "user-provided value"
diff --git a/python/ql/src/experimental/Security/NEW/CWE-776/examples/XmlBomb.js b/python/ql/src/experimental/Security/NEW/CWE-776/examples/XmlBomb.js
new file mode 100644
index 00000000000..f72902a5304
--- /dev/null
+++ b/python/ql/src/experimental/Security/NEW/CWE-776/examples/XmlBomb.js
@@ -0,0 +1,10 @@
+const app = require("express")(),
+ expat = require("node-expat");
+
+app.post("upload", (req, res) => {
+ let xmlSrc = req.body,
+ parser = new expat.Parser();
+ parser.on("startElement", handleStart);
+ parser.on("text", handleText);
+ parser.write(xmlSrc);
+});
diff --git a/python/ql/src/experimental/Security/NEW/CWE-776/examples/XmlBombGood.js b/python/ql/src/experimental/Security/NEW/CWE-776/examples/XmlBombGood.js
new file mode 100644
index 00000000000..a8c5bc97e63
--- /dev/null
+++ b/python/ql/src/experimental/Security/NEW/CWE-776/examples/XmlBombGood.js
@@ -0,0 +1,10 @@
+const app = require("express")(),
+ sax = require("sax");
+
+app.post("upload", (req, res) => {
+ let xmlSrc = req.body,
+ parser = sax.parser(true);
+ parser.onopentag = handleStart;
+ parser.ontext = handleText;
+ parser.write(xmlSrc);
+});
diff --git a/python/ql/src/experimental/semmle/python/security/dataflow/XmlBombCustomizations.qll b/python/ql/src/experimental/semmle/python/security/dataflow/XmlBombCustomizations.qll
new file mode 100644
index 00000000000..1d159b057ad
--- /dev/null
+++ b/python/ql/src/experimental/semmle/python/security/dataflow/XmlBombCustomizations.qll
@@ -0,0 +1,49 @@
+/**
+ * Provides default sources, sinks and sanitizers for reasoning about
+ * XML-bomb vulnerabilities, as well as extension points for adding
+ * your own.
+ */
+
+import javascript
+import semmle.javascript.security.dataflow.DOM
+
+module XmlBomb {
+ /**
+ * A data flow source for XML-bomb vulnerabilities.
+ */
+ abstract class Source extends DataFlow::Node { }
+
+ /**
+ * A data flow sink for XML-bomb vulnerabilities.
+ */
+ abstract class Sink extends DataFlow::Node { }
+
+ /**
+ * A sanitizer for XML-bomb vulnerabilities.
+ */
+ abstract class Sanitizer extends DataFlow::Node { }
+
+ /** A source of remote user input, considered as a flow source for XML bomb vulnerabilities. */
+ class RemoteFlowSourceAsSource extends Source {
+ RemoteFlowSourceAsSource() { this instanceof RemoteFlowSource }
+ }
+
+ /**
+ * An access to `document.location`, considered as a flow source for XML bomb vulnerabilities.
+ */
+ class LocationAsSource extends Source, DataFlow::ValueNode {
+ LocationAsSource() { isLocation(astNode) }
+ }
+
+ /**
+ * A call to an XML parser that performs internal entity expansion, viewed
+ * as a data flow sink for XML-bomb vulnerabilities.
+ */
+ class XmlParsingWithEntityResolution extends Sink, DataFlow::ValueNode {
+ XmlParsingWithEntityResolution() {
+ exists(XML::ParserInvocation parse | astNode = parse.getSourceArgument() |
+ parse.resolvesEntities(XML::InternalEntity())
+ )
+ }
+ }
+}
diff --git a/python/ql/src/experimental/semmle/python/security/dataflow/XmlBombQuery.qll b/python/ql/src/experimental/semmle/python/security/dataflow/XmlBombQuery.qll
new file mode 100644
index 00000000000..951b927f86e
--- /dev/null
+++ b/python/ql/src/experimental/semmle/python/security/dataflow/XmlBombQuery.qll
@@ -0,0 +1,27 @@
+/**
+ * Provides a taint tracking configuration for reasoning about
+ * XML-bomb vulnerabilities.
+ *
+ * Note, for performance reasons: only import this file if
+ * `XmlBomb::Configuration` is needed, otherwise
+ * `XmlBombCustomizations` should be imported instead.
+ */
+
+import javascript
+import XmlBombCustomizations::XmlBomb
+
+/**
+ * A taint-tracking configuration for reasoning about XML-bomb vulnerabilities.
+ */
+class Configuration extends TaintTracking::Configuration {
+ Configuration() { this = "XmlBomb" }
+
+ override predicate isSource(DataFlow::Node source) { source instanceof Source }
+
+ override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
+
+ override predicate isSanitizer(DataFlow::Node node) {
+ super.isSanitizer(node) or
+ node instanceof Sanitizer
+ }
+}
diff --git a/python/ql/src/experimental/semmle/python/security/dataflow/XxeCustomizations.qll b/python/ql/src/experimental/semmle/python/security/dataflow/XxeCustomizations.qll
new file mode 100644
index 00000000000..4e7bb5e730c
--- /dev/null
+++ b/python/ql/src/experimental/semmle/python/security/dataflow/XxeCustomizations.qll
@@ -0,0 +1,52 @@
+/**
+ * Provides default sources, sinks and sanitizers for reasoning about
+ * XML External Entity (XXE) vulnerabilities, as well as extension
+ * points for adding your own.
+ */
+
+import javascript
+import semmle.javascript.security.dataflow.DOM
+
+module Xxe {
+ /**
+ * A data flow source for XXE vulnerabilities.
+ */
+ abstract class Source extends DataFlow::Node { }
+
+ /**
+ * A data flow sink for XXE vulnerabilities.
+ */
+ abstract class Sink extends DataFlow::Node { }
+
+ /**
+ * A sanitizer for XXE vulnerabilities.
+ */
+ abstract class Sanitizer extends DataFlow::Node { }
+
+ /** A source of remote user input, considered as a flow source for XXE vulnerabilities. */
+ class RemoteFlowSourceAsSource extends Source {
+ RemoteFlowSourceAsSource() { this instanceof RemoteFlowSource }
+ }
+
+ /**
+ * An access to `document.location`, considered as a flow source for XXE vulnerabilities.
+ */
+ class LocationAsSource extends Source, DataFlow::ValueNode {
+ LocationAsSource() { isLocation(astNode) }
+ }
+
+ /**
+ * A call to an XML parser that performs external entity expansion, viewed
+ * as a data flow sink for XXE vulnerabilities.
+ */
+ class XmlParsingWithExternalEntityResolution extends Sink, DataFlow::ValueNode {
+ XmlParsingWithExternalEntityResolution() {
+ exists(XML::ParserInvocation parse | astNode = parse.getSourceArgument() |
+ parse.resolvesEntities(XML::ExternalEntity(_))
+ or
+ parse.resolvesEntities(XML::ParameterEntity(true)) and
+ parse.resolvesEntities(XML::InternalEntity())
+ )
+ }
+ }
+}
diff --git a/python/ql/src/experimental/semmle/python/security/dataflow/XxeQuery.qll b/python/ql/src/experimental/semmle/python/security/dataflow/XxeQuery.qll
new file mode 100644
index 00000000000..82d3fb4f6cc
--- /dev/null
+++ b/python/ql/src/experimental/semmle/python/security/dataflow/XxeQuery.qll
@@ -0,0 +1,27 @@
+/**
+ * Provides a taint tracking configuration for reasoning about XML
+ * External Entity (XXE) vulnerabilities.
+ *
+ * Note, for performance reasons: only import this file if
+ * `Xxe::Configuration` is needed, otherwise `XxeCustomizations`
+ * should be imported instead.
+ */
+
+import javascript
+import XxeCustomizations::Xxe
+
+/**
+ * A taint-tracking configuration for reasoning about XXE vulnerabilities.
+ */
+class Configuration extends TaintTracking::Configuration {
+ Configuration() { this = "Xxe" }
+
+ override predicate isSource(DataFlow::Node source) { source instanceof Source }
+
+ override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
+
+ override predicate isSanitizer(DataFlow::Node node) {
+ super.isSanitizer(node) or
+ node instanceof Sanitizer
+ }
+}