From c792567904e7e833d62a0be28a025d05f77cf1db Mon Sep 17 00:00:00 2001 From: Tony Torralba Date: Mon, 14 Jun 2021 15:11:23 +0200 Subject: [PATCH 01/10] Move from experimental --- .../{experimental => }/Security/CWE/CWE-074/XsltInjection.java | 0 .../{experimental => }/Security/CWE/CWE-074/XsltInjection.qhelp | 0 .../{experimental => }/Security/CWE/CWE-074/XsltInjection.ql | 0 .../Security/CWE/CWE-074/XsltInjectionLib.qll | 0 .../query-tests/security/CWE-074/XsltInjection.qlref | 1 - java/ql/test/experimental/query-tests/security/CWE-074/options | 1 - .../query-tests/security/CWE-074/XsltInjection.expected | 0 .../query-tests/security/CWE-074/XsltInjection.java | 0 java/ql/test/query-tests/security/CWE-074/XsltInjection.qlref | 1 + java/ql/test/query-tests/security/CWE-074/options | 2 +- .../stubs/Saxon-HE-9.9.1-7/net/sf/saxon/Configuration.java | 0 .../stubs/Saxon-HE-9.9.1-7/net/sf/saxon/lib/SourceResolver.java | 0 .../stubs/Saxon-HE-9.9.1-7/net/sf/saxon/om/NotationSet.java | 0 .../net/sf/saxon/s9api/AbstractXsltTransformer.java | 0 .../stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/Destination.java | 0 .../stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/Processor.java | 0 .../stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/QName.java | 0 .../Saxon-HE-9.9.1-7/net/sf/saxon/s9api/SaxonApiException.java | 0 .../net/sf/saxon/s9api/SaxonApiUncheckedException.java | 0 .../stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XdmItem.java | 0 .../stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XdmValue.java | 0 .../Saxon-HE-9.9.1-7/net/sf/saxon/s9api/Xslt30Transformer.java | 0 .../stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XsltCompiler.java | 0 .../Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XsltExecutable.java | 0 .../stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XsltPackage.java | 0 .../Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XsltTransformer.java | 0 26 files changed, 2 insertions(+), 3 deletions(-) rename java/ql/src/{experimental => }/Security/CWE/CWE-074/XsltInjection.java (100%) rename java/ql/src/{experimental => }/Security/CWE/CWE-074/XsltInjection.qhelp (100%) rename java/ql/src/{experimental => }/Security/CWE/CWE-074/XsltInjection.ql (100%) rename java/ql/src/{experimental => }/Security/CWE/CWE-074/XsltInjectionLib.qll (100%) delete mode 100644 java/ql/test/experimental/query-tests/security/CWE-074/XsltInjection.qlref delete mode 100644 java/ql/test/experimental/query-tests/security/CWE-074/options rename java/ql/test/{experimental => }/query-tests/security/CWE-074/XsltInjection.expected (100%) rename java/ql/test/{experimental => }/query-tests/security/CWE-074/XsltInjection.java (100%) create mode 100644 java/ql/test/query-tests/security/CWE-074/XsltInjection.qlref rename java/ql/test/{experimental => }/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/Configuration.java (100%) rename java/ql/test/{experimental => }/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/lib/SourceResolver.java (100%) rename java/ql/test/{experimental => }/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/om/NotationSet.java (100%) rename java/ql/test/{experimental => }/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/AbstractXsltTransformer.java (100%) rename java/ql/test/{experimental => }/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/Destination.java (100%) rename java/ql/test/{experimental => }/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/Processor.java (100%) rename java/ql/test/{experimental => }/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/QName.java (100%) rename java/ql/test/{experimental => }/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/SaxonApiException.java (100%) rename java/ql/test/{experimental => }/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/SaxonApiUncheckedException.java (100%) rename java/ql/test/{experimental => }/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XdmItem.java (100%) rename java/ql/test/{experimental => }/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XdmValue.java (100%) rename java/ql/test/{experimental => }/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/Xslt30Transformer.java (100%) rename java/ql/test/{experimental => }/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XsltCompiler.java (100%) rename java/ql/test/{experimental => }/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XsltExecutable.java (100%) rename java/ql/test/{experimental => }/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XsltPackage.java (100%) rename java/ql/test/{experimental => }/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XsltTransformer.java (100%) diff --git a/java/ql/src/experimental/Security/CWE/CWE-074/XsltInjection.java b/java/ql/src/Security/CWE/CWE-074/XsltInjection.java similarity index 100% rename from java/ql/src/experimental/Security/CWE/CWE-074/XsltInjection.java rename to java/ql/src/Security/CWE/CWE-074/XsltInjection.java diff --git a/java/ql/src/experimental/Security/CWE/CWE-074/XsltInjection.qhelp b/java/ql/src/Security/CWE/CWE-074/XsltInjection.qhelp similarity index 100% rename from java/ql/src/experimental/Security/CWE/CWE-074/XsltInjection.qhelp rename to java/ql/src/Security/CWE/CWE-074/XsltInjection.qhelp diff --git a/java/ql/src/experimental/Security/CWE/CWE-074/XsltInjection.ql b/java/ql/src/Security/CWE/CWE-074/XsltInjection.ql similarity index 100% rename from java/ql/src/experimental/Security/CWE/CWE-074/XsltInjection.ql rename to java/ql/src/Security/CWE/CWE-074/XsltInjection.ql diff --git a/java/ql/src/experimental/Security/CWE/CWE-074/XsltInjectionLib.qll b/java/ql/src/Security/CWE/CWE-074/XsltInjectionLib.qll similarity index 100% rename from java/ql/src/experimental/Security/CWE/CWE-074/XsltInjectionLib.qll rename to java/ql/src/Security/CWE/CWE-074/XsltInjectionLib.qll diff --git a/java/ql/test/experimental/query-tests/security/CWE-074/XsltInjection.qlref b/java/ql/test/experimental/query-tests/security/CWE-074/XsltInjection.qlref deleted file mode 100644 index eb4c280c5df4..000000000000 --- a/java/ql/test/experimental/query-tests/security/CWE-074/XsltInjection.qlref +++ /dev/null @@ -1 +0,0 @@ -experimental/Security/CWE/CWE-074/XsltInjection.ql diff --git a/java/ql/test/experimental/query-tests/security/CWE-074/options b/java/ql/test/experimental/query-tests/security/CWE-074/options deleted file mode 100644 index 599f2f5f14aa..000000000000 --- a/java/ql/test/experimental/query-tests/security/CWE-074/options +++ /dev/null @@ -1 +0,0 @@ -//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.3.8:${testdir}/../../../stubs/Saxon-HE-9.9.1-7 diff --git a/java/ql/test/experimental/query-tests/security/CWE-074/XsltInjection.expected b/java/ql/test/query-tests/security/CWE-074/XsltInjection.expected similarity index 100% rename from java/ql/test/experimental/query-tests/security/CWE-074/XsltInjection.expected rename to java/ql/test/query-tests/security/CWE-074/XsltInjection.expected diff --git a/java/ql/test/experimental/query-tests/security/CWE-074/XsltInjection.java b/java/ql/test/query-tests/security/CWE-074/XsltInjection.java similarity index 100% rename from java/ql/test/experimental/query-tests/security/CWE-074/XsltInjection.java rename to java/ql/test/query-tests/security/CWE-074/XsltInjection.java diff --git a/java/ql/test/query-tests/security/CWE-074/XsltInjection.qlref b/java/ql/test/query-tests/security/CWE-074/XsltInjection.qlref new file mode 100644 index 000000000000..9d7fb59ac2c2 --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-074/XsltInjection.qlref @@ -0,0 +1 @@ +Security/CWE/CWE-074/XsltInjection.ql diff --git a/java/ql/test/query-tests/security/CWE-074/options b/java/ql/test/query-tests/security/CWE-074/options index efb24fabff15..a015486520ef 100644 --- a/java/ql/test/query-tests/security/CWE-074/options +++ b/java/ql/test/query-tests/security/CWE-074/options @@ -1 +1 @@ -//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/springframework-5.3.8:${testdir}/../../../stubs/shiro-core-1.5.2:${testdir}/../../../stubs/spring-ldap-2.3.2 +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/springframework-5.3.8:${testdir}/../../../stubs/shiro-core-1.5.2:${testdir}/../../../stubs/spring-ldap-2.3.2:${testdir}/../../../stubs/Saxon-HE-9.9.1-7 diff --git a/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/Configuration.java b/java/ql/test/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/Configuration.java similarity index 100% rename from java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/Configuration.java rename to java/ql/test/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/Configuration.java diff --git a/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/lib/SourceResolver.java b/java/ql/test/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/lib/SourceResolver.java similarity index 100% rename from java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/lib/SourceResolver.java rename to java/ql/test/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/lib/SourceResolver.java diff --git a/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/om/NotationSet.java b/java/ql/test/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/om/NotationSet.java similarity index 100% rename from java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/om/NotationSet.java rename to java/ql/test/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/om/NotationSet.java diff --git a/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/AbstractXsltTransformer.java b/java/ql/test/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/AbstractXsltTransformer.java similarity index 100% rename from java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/AbstractXsltTransformer.java rename to java/ql/test/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/AbstractXsltTransformer.java diff --git a/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/Destination.java b/java/ql/test/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/Destination.java similarity index 100% rename from java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/Destination.java rename to java/ql/test/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/Destination.java diff --git a/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/Processor.java b/java/ql/test/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/Processor.java similarity index 100% rename from java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/Processor.java rename to java/ql/test/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/Processor.java diff --git a/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/QName.java b/java/ql/test/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/QName.java similarity index 100% rename from java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/QName.java rename to java/ql/test/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/QName.java diff --git a/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/SaxonApiException.java b/java/ql/test/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/SaxonApiException.java similarity index 100% rename from java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/SaxonApiException.java rename to java/ql/test/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/SaxonApiException.java diff --git a/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/SaxonApiUncheckedException.java b/java/ql/test/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/SaxonApiUncheckedException.java similarity index 100% rename from java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/SaxonApiUncheckedException.java rename to java/ql/test/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/SaxonApiUncheckedException.java diff --git a/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XdmItem.java b/java/ql/test/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XdmItem.java similarity index 100% rename from java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XdmItem.java rename to java/ql/test/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XdmItem.java diff --git a/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XdmValue.java b/java/ql/test/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XdmValue.java similarity index 100% rename from java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XdmValue.java rename to java/ql/test/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XdmValue.java diff --git a/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/Xslt30Transformer.java b/java/ql/test/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/Xslt30Transformer.java similarity index 100% rename from java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/Xslt30Transformer.java rename to java/ql/test/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/Xslt30Transformer.java diff --git a/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XsltCompiler.java b/java/ql/test/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XsltCompiler.java similarity index 100% rename from java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XsltCompiler.java rename to java/ql/test/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XsltCompiler.java diff --git a/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XsltExecutable.java b/java/ql/test/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XsltExecutable.java similarity index 100% rename from java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XsltExecutable.java rename to java/ql/test/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XsltExecutable.java diff --git a/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XsltPackage.java b/java/ql/test/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XsltPackage.java similarity index 100% rename from java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XsltPackage.java rename to java/ql/test/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XsltPackage.java diff --git a/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XsltTransformer.java b/java/ql/test/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XsltTransformer.java similarity index 100% rename from java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XsltTransformer.java rename to java/ql/test/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XsltTransformer.java From d8bb5273e71e60790bbf24e345a01058c0313c76 Mon Sep 17 00:00:00 2001 From: Tony Torralba Date: Mon, 14 Jun 2021 15:37:38 +0200 Subject: [PATCH 02/10] Refactor to use CSV sink models --- .../src/Security/CWE/CWE-074/XsltInjection.ql | 21 +- .../code/java/security/XsltInjection.qll} | 230 ++++++++---------- 2 files changed, 118 insertions(+), 133 deletions(-) rename java/ql/src/{Security/CWE/CWE-074/XsltInjectionLib.qll => semmle/code/java/security/XsltInjection.qll} (60%) diff --git a/java/ql/src/Security/CWE/CWE-074/XsltInjection.ql b/java/ql/src/Security/CWE/CWE-074/XsltInjection.ql index 1403573e4b19..cd47d69470fe 100644 --- a/java/ql/src/Security/CWE/CWE-074/XsltInjection.ql +++ b/java/ql/src/Security/CWE/CWE-074/XsltInjection.ql @@ -12,9 +12,28 @@ import java import semmle.code.java.dataflow.FlowSources -import XsltInjectionLib +import semmle.code.java.security.XsltInjection import DataFlow::PathGraph +/** + * A taint-tracking configuration for unvalidated user input that is used in XSLT transformation. + */ +class XsltInjectionFlowConfig extends TaintTracking::Configuration { + XsltInjectionFlowConfig() { this = "XsltInjectionFlowConfig" } + + override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node sink) { sink instanceof XsltInjectionSink } + + override predicate isSanitizer(DataFlow::Node node) { + node.getType() instanceof PrimitiveType or node.getType() instanceof BoxedType + } + + override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { + any(XsltInjectionAdditionalTaintStep c).step(node1, node2) + } +} + from DataFlow::PathNode source, DataFlow::PathNode sink, XsltInjectionFlowConfig conf where conf.hasFlowPath(source, sink) select sink.getNode(), source, sink, "XSLT transformation might include stylesheet from $@.", diff --git a/java/ql/src/Security/CWE/CWE-074/XsltInjectionLib.qll b/java/ql/src/semmle/code/java/security/XsltInjection.qll similarity index 60% rename from java/ql/src/Security/CWE/CWE-074/XsltInjectionLib.qll rename to java/ql/src/semmle/code/java/security/XsltInjection.qll index 4ba0eb6d0b11..4db9f7afd566 100644 --- a/java/ql/src/Security/CWE/CWE-074/XsltInjectionLib.qll +++ b/java/ql/src/semmle/code/java/security/XsltInjection.qll @@ -1,23 +1,51 @@ +/** Provides classes to reason about XSLT injection vulnerabilities. */ + import java -import semmle.code.java.dataflow.FlowSources +import semmle.code.java.dataflow.ExternalFlow import semmle.code.java.security.XmlParsers -import DataFlow +import semmle.code.java.dataflow.DataFlow /** - * A taint-tracking configuration for unvalidated user input that is used in XSLT transformation. + * A data flow sink for unvalidated user input that is used in XSLT transformation. + * Extend this class to add your own XSLT Injection sinks. */ -class XsltInjectionFlowConfig extends TaintTracking::Configuration { - XsltInjectionFlowConfig() { this = "XsltInjectionFlowConfig" } - - override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } +abstract class XsltInjectionSink extends DataFlow::Node { } + +private class DefaultXsltInjectionSinkModel extends SinkModelCsv { + override predicate row(string row) { + row = + [ + "javax.xml.transform;Transformer;false;transform;;;Argument[-1];xslt", + "net.sf.saxon.s9api;XsltTransformer;false;transform;;;Argument[-1];xslt", + "net.sf.saxon.s9api;Xslt30Transformer;false;transform;;;Argument[-1];xslt", + "net.sf.saxon.s9api;Xslt30Transformer;false;applyTemplates;;;Argument[-1];xslt", + "net.sf.saxon.s9api;Xslt30Transformer;false;callFunction;;;Argument[-1];xslt", + "net.sf.saxon.s9api;Xslt30Transformer;false;callTemplate;;;Argument[-1];xslt" + ] + } +} - override predicate isSink(DataFlow::Node sink) { sink instanceof XsltInjectionSink } +/** A default sink representing methods susceptible to XSLT Injection attacks. */ +private class DefaultXsltInjectionSink extends XsltInjectionSink { + DefaultXsltInjectionSink() { sinkNode(this, "xslt") } +} - override predicate isSanitizer(DataFlow::Node node) { - node.getType() instanceof PrimitiveType or node.getType() instanceof BoxedType - } +/** + * A unit class for adding additional taint steps. + * + * Extend this class to add additional taint steps that should apply to the `XsltInjectionFlowConfig`. + */ +class XsltInjectionAdditionalTaintStep extends Unit { + /** + * Holds if the step from `node1` to `node2` should be considered a taint + * step for the `XsltInjectionFlowConfig` configuration. + */ + abstract predicate step(DataFlow::Node node1, DataFlow::Node node2); +} - override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { +/** A set of additional taint steps to consider when taint tracking XSLT related data flows. */ +private class DefaultXsltInjectionAdditionalTaintStep extends XsltInjectionAdditionalTaintStep { + override predicate step(DataFlow::Node node1, DataFlow::Node node2) { xmlStreamReaderStep(node1, node2) or xmlEventReaderStep(node1, node2) or staxSourceStep(node1, node2) or @@ -31,95 +59,11 @@ class XsltInjectionFlowConfig extends TaintTracking::Configuration { } } -/** The class `javax.xml.transform.stax.StAXSource`. */ -class TypeStAXSource extends Class { - TypeStAXSource() { this.hasQualifiedName("javax.xml.transform.stax", "StAXSource") } -} - -/** The class `javax.xml.transform.dom.DOMSource`. */ -class TypeDOMSource extends Class { - TypeDOMSource() { this.hasQualifiedName("javax.xml.transform.dom", "DOMSource") } -} - -/** The interface `javax.xml.transform.Templates`. */ -class TypeTemplates extends Interface { - TypeTemplates() { this.hasQualifiedName("javax.xml.transform", "Templates") } -} - -/** The method `net.sf.saxon.s9api.XsltTransformer.transform`. */ -class XsltTransformerTransformMethod extends Method { - XsltTransformerTransformMethod() { - this.getDeclaringType().hasQualifiedName("net.sf.saxon.s9api", "XsltTransformer") and - this.hasName("transform") - } -} - -/** The method `net.sf.saxon.s9api.Xslt30Transformer.transform`. */ -class Xslt30TransformerTransformMethod extends Method { - Xslt30TransformerTransformMethod() { - this.getDeclaringType().hasQualifiedName("net.sf.saxon.s9api", "Xslt30Transformer") and - this.hasName("transform") - } -} - -/** The method `net.sf.saxon.s9api.Xslt30Transformer.applyTemplates`. */ -class Xslt30TransformerApplyTemplatesMethod extends Method { - Xslt30TransformerApplyTemplatesMethod() { - this.getDeclaringType().hasQualifiedName("net.sf.saxon.s9api", "Xslt30Transformer") and - this.hasName("applyTemplates") - } -} - -/** The method `net.sf.saxon.s9api.Xslt30Transformer.callFunction`. */ -class Xslt30TransformerCallFunctionMethod extends Method { - Xslt30TransformerCallFunctionMethod() { - this.getDeclaringType().hasQualifiedName("net.sf.saxon.s9api", "Xslt30Transformer") and - this.hasName("callFunction") - } -} - -/** The method `net.sf.saxon.s9api.Xslt30Transformer.callTemplate`. */ -class Xslt30TransformerCallTemplateMethod extends Method { - Xslt30TransformerCallTemplateMethod() { - this.getDeclaringType().hasQualifiedName("net.sf.saxon.s9api", "Xslt30Transformer") and - this.hasName("callTemplate") - } -} - -/** The class `net.sf.saxon.s9api.XsltCompiler`. */ -class TypeXsltCompiler extends Class { - TypeXsltCompiler() { this.hasQualifiedName("net.sf.saxon.s9api", "XsltCompiler") } -} - -/** The class `net.sf.saxon.s9api.XsltExecutable`. */ -class TypeXsltExecutable extends Class { - TypeXsltExecutable() { this.hasQualifiedName("net.sf.saxon.s9api", "XsltExecutable") } -} - -/** The class `net.sf.saxon.s9api.XsltPackage`. */ -class TypeXsltPackage extends Class { - TypeXsltPackage() { this.hasQualifiedName("net.sf.saxon.s9api", "XsltPackage") } -} - -/** A data flow sink for unvalidated user input that is used in XSLT transformation. */ -class XsltInjectionSink extends DataFlow::ExprNode { - XsltInjectionSink() { - exists(MethodAccess ma, Method m | m = ma.getMethod() and ma.getQualifier() = this.getExpr() | - ma instanceof TransformerTransform or - m instanceof XsltTransformerTransformMethod or - m instanceof Xslt30TransformerTransformMethod or - m instanceof Xslt30TransformerApplyTemplatesMethod or - m instanceof Xslt30TransformerCallFunctionMethod or - m instanceof Xslt30TransformerCallTemplateMethod - ) - } -} - /** * Holds if `n1` to `n2` is a dataflow step that converts between `InputStream` or `Reader` and * `XMLStreamReader`, i.e. `XMLInputFactory.createXMLStreamReader(tainted)`. */ -predicate xmlStreamReaderStep(ExprNode n1, ExprNode n2) { +private predicate xmlStreamReaderStep(DataFlow::Node n1, DataFlow::Node n2) { exists(XmlInputFactoryStreamReader xmlStreamReader | n1.asExpr() = xmlStreamReader.getSink() and n2.asExpr() = xmlStreamReader @@ -130,7 +74,7 @@ predicate xmlStreamReaderStep(ExprNode n1, ExprNode n2) { * Holds if `n1` to `n2` is a dataflow step that converts between `InputStream` or `Reader` and * `XMLEventReader`, i.e. `XMLInputFactory.createXMLEventReader(tainted)`. */ -predicate xmlEventReaderStep(ExprNode n1, ExprNode n2) { +private predicate xmlEventReaderStep(DataFlow::Node n1, DataFlow::Node n2) { exists(XmlInputFactoryEventReader xmlEventReader | n1.asExpr() = xmlEventReader.getSink() and n2.asExpr() = xmlEventReader @@ -141,7 +85,7 @@ predicate xmlEventReaderStep(ExprNode n1, ExprNode n2) { * Holds if `n1` to `n2` is a dataflow step that converts between `XMLStreamReader` or * `XMLEventReader` and `StAXSource`, i.e. `new StAXSource(tainted)`. */ -predicate staxSourceStep(ExprNode n1, ExprNode n2) { +private predicate staxSourceStep(DataFlow::Node n1, DataFlow::Node n2) { exists(ConstructorCall cc | cc.getConstructedType() instanceof TypeStAXSource | n1.asExpr() = cc.getAnArgument() and n2.asExpr() = cc @@ -152,7 +96,7 @@ predicate staxSourceStep(ExprNode n1, ExprNode n2) { * Holds if `n1` to `n2` is a dataflow step that converts between `InputStream` and `Document`, * i.e. `DocumentBuilder.parse(tainted)`. */ -predicate documentBuilderStep(ExprNode n1, ExprNode n2) { +private predicate documentBuilderStep(DataFlow::Node n1, DataFlow::Node n2) { exists(DocumentBuilderParse documentBuilder | n1.asExpr() = documentBuilder.getSink() and n2.asExpr() = documentBuilder @@ -163,13 +107,30 @@ predicate documentBuilderStep(ExprNode n1, ExprNode n2) { * Holds if `n1` to `n2` is a dataflow step that converts between `Document` and `DOMSource`, i.e. * `new DOMSource(tainted)`. */ -predicate domSourceStep(ExprNode n1, ExprNode n2) { +private predicate domSourceStep(DataFlow::Node n1, DataFlow::Node n2) { exists(ConstructorCall cc | cc.getConstructedType() instanceof TypeDOMSource | n1.asExpr() = cc.getAnArgument() and n2.asExpr() = cc ) } +/** + * Holds if `n1` to `n2` is a dataflow step that converts between `Source` and `Transformer` or + * `Templates`, i.e. `TransformerFactory.newTransformer(tainted)` or + * `TransformerFactory.newTemplates(tainted)`. + */ +private predicate newTransformerOrTemplatesStep(DataFlow::Node n1, DataFlow::Node n2) { + exists(MethodAccess ma, Method m | ma.getMethod() = m | + n1.asExpr() = ma.getAnArgument() and + n2.asExpr() = ma and + m.getDeclaringType() instanceof TransformerFactory and + m.hasName(["newTransformer", "newTemplates"]) and + not exists(TransformerFactoryWithSecureProcessingFeatureFlowConfig conf | + conf.hasFlowToExpr(ma.getQualifier()) + ) + ) +} + /** * A data flow configuration for secure processing feature that is enabled on `TransformerFactory`. */ @@ -207,31 +168,11 @@ private class TransformerFactoryFeatureConfig extends ParserConfig { } } -/** - * Holds if `n1` to `n2` is a dataflow step that converts between `Source` and `Transformer` or - * `Templates`, i.e. `TransformerFactory.newTransformer(tainted)` or - * `TransformerFactory.newTemplates(tainted)`. - */ -predicate newTransformerOrTemplatesStep(ExprNode n1, ExprNode n2) { - exists(MethodAccess ma, Method m | ma.getMethod() = m | - n1.asExpr() = ma.getAnArgument() and - n2.asExpr() = ma and - ( - m.getDeclaringType() instanceof TransformerFactory and m.hasName("newTransformer") - or - m.getDeclaringType() instanceof TransformerFactory and m.hasName("newTemplates") - ) and - not exists(TransformerFactoryWithSecureProcessingFeatureFlowConfig conf | - conf.hasFlowToExpr(ma.getQualifier()) - ) - ) -} - /** * Holds if `n1` to `n2` is a dataflow step that converts between `Templates` and `Transformer`, * i.e. `tainted.newTransformer()`. */ -predicate newTransformerFromTemplatesStep(ExprNode n1, ExprNode n2) { +private predicate newTransformerFromTemplatesStep(DataFlow::Node n1, DataFlow::Node n2) { exists(MethodAccess ma, Method m | ma.getMethod() = m | n1.asExpr() = ma.getQualifier() and n2.asExpr() = ma and @@ -246,17 +187,12 @@ predicate newTransformerFromTemplatesStep(ExprNode n1, ExprNode n2) { * `XsltCompiler.loadExecutablePackage(tainted)` or `XsltCompiler.compilePackage(tainted)` or * `XsltCompiler.loadLibraryPackage(tainted)`. */ -predicate xsltCompilerStep(ExprNode n1, ExprNode n2) { +private predicate xsltCompilerStep(DataFlow::Node n1, DataFlow::Node n2) { exists(MethodAccess ma, Method m | ma.getMethod() = m | n1.asExpr() = ma.getArgument(0) and n2.asExpr() = ma and m.getDeclaringType() instanceof TypeXsltCompiler and - ( - m.hasName("compile") or - m.hasName("loadExecutablePackage") or - m.hasName("compilePackage") or - m.hasName("loadLibraryPackage") - ) + m.hasName(["compile", "loadExecutablePackage", "compilePackage", "loadLibraryPackage"]) ) } @@ -265,12 +201,12 @@ predicate xsltCompilerStep(ExprNode n1, ExprNode n2) { * `XsltTransformer` or `Xslt30Transformer`, i.e. `XsltExecutable.load()` or * `XsltExecutable.load30()`. */ -predicate xsltExecutableStep(ExprNode n1, ExprNode n2) { +private predicate xsltExecutableStep(DataFlow::Node n1, DataFlow::Node n2) { exists(MethodAccess ma, Method m | ma.getMethod() = m | n1.asExpr() = ma.getQualifier() and n2.asExpr() = ma and m.getDeclaringType() instanceof TypeXsltExecutable and - (m.hasName("load") or m.hasName("load30")) + m.hasName(["load", "load30"]) ) } @@ -278,7 +214,7 @@ predicate xsltExecutableStep(ExprNode n1, ExprNode n2) { * Holds if `n1` to `n2` is a dataflow step that converts between `XsltPackage` and * `XsltExecutable`, i.e. `XsltPackage.link()`. */ -predicate xsltPackageStep(ExprNode n1, ExprNode n2) { +private predicate xsltPackageStep(DataFlow::Node n1, DataFlow::Node n2) { exists(MethodAccess ma, Method m | ma.getMethod() = m | n1.asExpr() = ma.getQualifier() and n2.asExpr() = ma and @@ -286,3 +222,33 @@ predicate xsltPackageStep(ExprNode n1, ExprNode n2) { m.hasName("link") ) } + +/** The class `javax.xml.transform.stax.StAXSource`. */ +private class TypeStAXSource extends Class { + TypeStAXSource() { this.hasQualifiedName("javax.xml.transform.stax", "StAXSource") } +} + +/** The class `javax.xml.transform.dom.DOMSource`. */ +private class TypeDOMSource extends Class { + TypeDOMSource() { this.hasQualifiedName("javax.xml.transform.dom", "DOMSource") } +} + +/** The interface `javax.xml.transform.Templates`. */ +private class TypeTemplates extends Interface { + TypeTemplates() { this.hasQualifiedName("javax.xml.transform", "Templates") } +} + +/** The class `net.sf.saxon.s9api.XsltCompiler`. */ +private class TypeXsltCompiler extends Class { + TypeXsltCompiler() { this.hasQualifiedName("net.sf.saxon.s9api", "XsltCompiler") } +} + +/** The class `net.sf.saxon.s9api.XsltExecutable`. */ +private class TypeXsltExecutable extends Class { + TypeXsltExecutable() { this.hasQualifiedName("net.sf.saxon.s9api", "XsltExecutable") } +} + +/** The class `net.sf.saxon.s9api.XsltPackage`. */ +private class TypeXsltPackage extends Class { + TypeXsltPackage() { this.hasQualifiedName("net.sf.saxon.s9api", "XsltPackage") } +} From 108118afa36c40603b61c996591977fd9c299de7 Mon Sep 17 00:00:00 2001 From: Tony Torralba Date: Mon, 14 Jun 2021 16:12:47 +0200 Subject: [PATCH 03/10] Use InlineExpectationsTest --- .../security/CWE-074/XsltInjection.expected | 122 ------------------ .../security/CWE-074/XsltInjection.qlref | 1 - .../CWE-074/XsltInjectionTest.expected | 0 ...tInjection.java => XsltInjectionTest.java} | 74 ++++++----- .../security/CWE-074/XsltInjectionTest.ql | 36 ++++++ 5 files changed, 75 insertions(+), 158 deletions(-) delete mode 100644 java/ql/test/query-tests/security/CWE-074/XsltInjection.expected delete mode 100644 java/ql/test/query-tests/security/CWE-074/XsltInjection.qlref create mode 100644 java/ql/test/query-tests/security/CWE-074/XsltInjectionTest.expected rename java/ql/test/query-tests/security/CWE-074/{XsltInjection.java => XsltInjectionTest.java} (68%) create mode 100644 java/ql/test/query-tests/security/CWE-074/XsltInjectionTest.ql diff --git a/java/ql/test/query-tests/security/CWE-074/XsltInjection.expected b/java/ql/test/query-tests/security/CWE-074/XsltInjection.expected deleted file mode 100644 index 99cde4c3c10e..000000000000 --- a/java/ql/test/query-tests/security/CWE-074/XsltInjection.expected +++ /dev/null @@ -1,122 +0,0 @@ -edges -| XsltInjection.java:30:27:30:67 | new StreamSource(...) : StreamSource | XsltInjection.java:31:5:31:59 | newTransformer(...) | -| XsltInjection.java:30:44:30:66 | getInputStream(...) : InputStream | XsltInjection.java:30:27:30:67 | new StreamSource(...) : StreamSource | -| XsltInjection.java:35:27:35:90 | new StreamSource(...) : StreamSource | XsltInjection.java:36:5:36:74 | newTransformer(...) | -| XsltInjection.java:35:44:35:89 | new InputStreamReader(...) : InputStreamReader | XsltInjection.java:35:27:35:90 | new StreamSource(...) : StreamSource | -| XsltInjection.java:35:66:35:88 | getInputStream(...) : InputStream | XsltInjection.java:35:44:35:89 | new InputStreamReader(...) : InputStreamReader | -| XsltInjection.java:40:45:40:70 | param : String | XsltInjection.java:42:61:42:64 | xslt : String | -| XsltInjection.java:42:27:42:66 | new StreamSource(...) : StreamSource | XsltInjection.java:43:5:43:59 | newTransformer(...) | -| XsltInjection.java:42:44:42:65 | new StringReader(...) : StringReader | XsltInjection.java:42:27:42:66 | new StreamSource(...) : StreamSource | -| XsltInjection.java:42:61:42:64 | xslt : String | XsltInjection.java:42:44:42:65 | new StringReader(...) : StringReader | -| XsltInjection.java:47:24:47:78 | new SAXSource(...) : SAXSource | XsltInjection.java:48:5:48:74 | newTransformer(...) | -| XsltInjection.java:47:38:47:77 | new InputSource(...) : InputSource | XsltInjection.java:47:24:47:78 | new SAXSource(...) : SAXSource | -| XsltInjection.java:47:54:47:76 | getInputStream(...) : InputStream | XsltInjection.java:47:38:47:77 | new InputSource(...) : InputSource | -| XsltInjection.java:52:24:52:107 | new SAXSource(...) : SAXSource | XsltInjection.java:53:5:53:59 | newTransformer(...) | -| XsltInjection.java:52:44:52:106 | new InputSource(...) : InputSource | XsltInjection.java:52:24:52:107 | new SAXSource(...) : SAXSource | -| XsltInjection.java:52:60:52:105 | new InputStreamReader(...) : InputStreamReader | XsltInjection.java:52:44:52:106 | new InputSource(...) : InputSource | -| XsltInjection.java:52:82:52:104 | getInputStream(...) : InputStream | XsltInjection.java:52:60:52:105 | new InputStreamReader(...) : InputStreamReader | -| XsltInjection.java:57:91:57:113 | getInputStream(...) : InputStream | XsltInjection.java:58:5:58:59 | newTransformer(...) | -| XsltInjection.java:62:98:62:143 | new InputStreamReader(...) : InputStreamReader | XsltInjection.java:63:5:63:74 | newTransformer(...) | -| XsltInjection.java:62:120:62:142 | getInputStream(...) : InputStream | XsltInjection.java:62:98:62:143 | new InputStreamReader(...) : InputStreamReader | -| XsltInjection.java:67:102:67:124 | getInputStream(...) : InputStream | XsltInjection.java:68:5:68:59 | newTransformer(...) | -| XsltInjection.java:72:27:72:67 | new StreamSource(...) : StreamSource | XsltInjection.java:76:5:76:34 | newTransformer(...) | -| XsltInjection.java:72:44:72:66 | getInputStream(...) : InputStream | XsltInjection.java:72:27:72:67 | new StreamSource(...) : StreamSource | -| XsltInjection.java:80:27:80:67 | new StreamSource(...) : StreamSource | XsltInjection.java:83:5:83:34 | newTransformer(...) | -| XsltInjection.java:80:44:80:66 | getInputStream(...) : InputStream | XsltInjection.java:80:27:80:67 | new StreamSource(...) : StreamSource | -| XsltInjection.java:87:27:87:67 | new StreamSource(...) : StreamSource | XsltInjection.java:90:5:90:35 | load(...) | -| XsltInjection.java:87:27:87:67 | new StreamSource(...) : StreamSource | XsltInjection.java:91:5:91:37 | load30(...) | -| XsltInjection.java:87:27:87:67 | new StreamSource(...) : StreamSource | XsltInjection.java:92:5:92:37 | load30(...) | -| XsltInjection.java:87:27:87:67 | new StreamSource(...) : StreamSource | XsltInjection.java:93:5:93:37 | load30(...) | -| XsltInjection.java:87:27:87:67 | new StreamSource(...) : StreamSource | XsltInjection.java:94:5:94:37 | load30(...) | -| XsltInjection.java:87:27:87:67 | new StreamSource(...) : StreamSource | XsltInjection.java:95:5:95:37 | load30(...) | -| XsltInjection.java:87:27:87:67 | new StreamSource(...) : StreamSource | XsltInjection.java:96:5:96:37 | load30(...) | -| XsltInjection.java:87:27:87:67 | new StreamSource(...) : StreamSource | XsltInjection.java:97:5:97:37 | load30(...) | -| XsltInjection.java:87:27:87:67 | new StreamSource(...) : StreamSource | XsltInjection.java:98:5:98:37 | load30(...) | -| XsltInjection.java:87:27:87:67 | new StreamSource(...) : StreamSource | XsltInjection.java:99:5:99:37 | load30(...) | -| XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:87:27:87:67 | new StreamSource(...) : StreamSource | -| XsltInjection.java:103:36:103:61 | param : String | XsltInjection.java:104:23:104:27 | param : String | -| XsltInjection.java:104:15:104:28 | new URI(...) : URI | XsltInjection.java:108:5:108:46 | load(...) | -| XsltInjection.java:104:15:104:28 | new URI(...) : URI | XsltInjection.java:110:5:110:50 | load(...) | -| XsltInjection.java:104:23:104:27 | param : String | XsltInjection.java:104:15:104:28 | new URI(...) : URI | -| XsltInjection.java:105:27:105:67 | new StreamSource(...) : StreamSource | XsltInjection.java:109:5:109:49 | load(...) | -| XsltInjection.java:105:44:105:66 | getInputStream(...) : InputStream | XsltInjection.java:105:27:105:67 | new StreamSource(...) : StreamSource | -nodes -| XsltInjection.java:30:27:30:67 | new StreamSource(...) : StreamSource | semmle.label | new StreamSource(...) : StreamSource | -| XsltInjection.java:30:44:30:66 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream | -| XsltInjection.java:31:5:31:59 | newTransformer(...) | semmle.label | newTransformer(...) | -| XsltInjection.java:35:27:35:90 | new StreamSource(...) : StreamSource | semmle.label | new StreamSource(...) : StreamSource | -| XsltInjection.java:35:44:35:89 | new InputStreamReader(...) : InputStreamReader | semmle.label | new InputStreamReader(...) : InputStreamReader | -| XsltInjection.java:35:66:35:88 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream | -| XsltInjection.java:36:5:36:74 | newTransformer(...) | semmle.label | newTransformer(...) | -| XsltInjection.java:40:45:40:70 | param : String | semmle.label | param : String | -| XsltInjection.java:42:27:42:66 | new StreamSource(...) : StreamSource | semmle.label | new StreamSource(...) : StreamSource | -| XsltInjection.java:42:44:42:65 | new StringReader(...) : StringReader | semmle.label | new StringReader(...) : StringReader | -| XsltInjection.java:42:61:42:64 | xslt : String | semmle.label | xslt : String | -| XsltInjection.java:43:5:43:59 | newTransformer(...) | semmle.label | newTransformer(...) | -| XsltInjection.java:47:24:47:78 | new SAXSource(...) : SAXSource | semmle.label | new SAXSource(...) : SAXSource | -| XsltInjection.java:47:38:47:77 | new InputSource(...) : InputSource | semmle.label | new InputSource(...) : InputSource | -| XsltInjection.java:47:54:47:76 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream | -| XsltInjection.java:48:5:48:74 | newTransformer(...) | semmle.label | newTransformer(...) | -| XsltInjection.java:52:24:52:107 | new SAXSource(...) : SAXSource | semmle.label | new SAXSource(...) : SAXSource | -| XsltInjection.java:52:44:52:106 | new InputSource(...) : InputSource | semmle.label | new InputSource(...) : InputSource | -| XsltInjection.java:52:60:52:105 | new InputStreamReader(...) : InputStreamReader | semmle.label | new InputStreamReader(...) : InputStreamReader | -| XsltInjection.java:52:82:52:104 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream | -| XsltInjection.java:53:5:53:59 | newTransformer(...) | semmle.label | newTransformer(...) | -| XsltInjection.java:57:91:57:113 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream | -| XsltInjection.java:58:5:58:59 | newTransformer(...) | semmle.label | newTransformer(...) | -| XsltInjection.java:62:98:62:143 | new InputStreamReader(...) : InputStreamReader | semmle.label | new InputStreamReader(...) : InputStreamReader | -| XsltInjection.java:62:120:62:142 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream | -| XsltInjection.java:63:5:63:74 | newTransformer(...) | semmle.label | newTransformer(...) | -| XsltInjection.java:67:102:67:124 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream | -| XsltInjection.java:68:5:68:59 | newTransformer(...) | semmle.label | newTransformer(...) | -| XsltInjection.java:72:27:72:67 | new StreamSource(...) : StreamSource | semmle.label | new StreamSource(...) : StreamSource | -| XsltInjection.java:72:44:72:66 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream | -| XsltInjection.java:76:5:76:34 | newTransformer(...) | semmle.label | newTransformer(...) | -| XsltInjection.java:80:27:80:67 | new StreamSource(...) : StreamSource | semmle.label | new StreamSource(...) : StreamSource | -| XsltInjection.java:80:44:80:66 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream | -| XsltInjection.java:83:5:83:34 | newTransformer(...) | semmle.label | newTransformer(...) | -| XsltInjection.java:87:27:87:67 | new StreamSource(...) : StreamSource | semmle.label | new StreamSource(...) : StreamSource | -| XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream | -| XsltInjection.java:90:5:90:35 | load(...) | semmle.label | load(...) | -| XsltInjection.java:91:5:91:37 | load30(...) | semmle.label | load30(...) | -| XsltInjection.java:92:5:92:37 | load30(...) | semmle.label | load30(...) | -| XsltInjection.java:93:5:93:37 | load30(...) | semmle.label | load30(...) | -| XsltInjection.java:94:5:94:37 | load30(...) | semmle.label | load30(...) | -| XsltInjection.java:95:5:95:37 | load30(...) | semmle.label | load30(...) | -| XsltInjection.java:96:5:96:37 | load30(...) | semmle.label | load30(...) | -| XsltInjection.java:97:5:97:37 | load30(...) | semmle.label | load30(...) | -| XsltInjection.java:98:5:98:37 | load30(...) | semmle.label | load30(...) | -| XsltInjection.java:99:5:99:37 | load30(...) | semmle.label | load30(...) | -| XsltInjection.java:103:36:103:61 | param : String | semmle.label | param : String | -| XsltInjection.java:104:15:104:28 | new URI(...) : URI | semmle.label | new URI(...) : URI | -| XsltInjection.java:104:23:104:27 | param : String | semmle.label | param : String | -| XsltInjection.java:105:27:105:67 | new StreamSource(...) : StreamSource | semmle.label | new StreamSource(...) : StreamSource | -| XsltInjection.java:105:44:105:66 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream | -| XsltInjection.java:108:5:108:46 | load(...) | semmle.label | load(...) | -| XsltInjection.java:109:5:109:49 | load(...) | semmle.label | load(...) | -| XsltInjection.java:110:5:110:50 | load(...) | semmle.label | load(...) | -subpaths -#select -| XsltInjection.java:31:5:31:59 | newTransformer(...) | XsltInjection.java:30:44:30:66 | getInputStream(...) : InputStream | XsltInjection.java:31:5:31:59 | newTransformer(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:30:44:30:66 | getInputStream(...) | this user input | -| XsltInjection.java:36:5:36:74 | newTransformer(...) | XsltInjection.java:35:66:35:88 | getInputStream(...) : InputStream | XsltInjection.java:36:5:36:74 | newTransformer(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:35:66:35:88 | getInputStream(...) | this user input | -| XsltInjection.java:43:5:43:59 | newTransformer(...) | XsltInjection.java:40:45:40:70 | param : String | XsltInjection.java:43:5:43:59 | newTransformer(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:40:45:40:70 | param | this user input | -| XsltInjection.java:48:5:48:74 | newTransformer(...) | XsltInjection.java:47:54:47:76 | getInputStream(...) : InputStream | XsltInjection.java:48:5:48:74 | newTransformer(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:47:54:47:76 | getInputStream(...) | this user input | -| XsltInjection.java:53:5:53:59 | newTransformer(...) | XsltInjection.java:52:82:52:104 | getInputStream(...) : InputStream | XsltInjection.java:53:5:53:59 | newTransformer(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:52:82:52:104 | getInputStream(...) | this user input | -| XsltInjection.java:58:5:58:59 | newTransformer(...) | XsltInjection.java:57:91:57:113 | getInputStream(...) : InputStream | XsltInjection.java:58:5:58:59 | newTransformer(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:57:91:57:113 | getInputStream(...) | this user input | -| XsltInjection.java:63:5:63:74 | newTransformer(...) | XsltInjection.java:62:120:62:142 | getInputStream(...) : InputStream | XsltInjection.java:63:5:63:74 | newTransformer(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:62:120:62:142 | getInputStream(...) | this user input | -| XsltInjection.java:68:5:68:59 | newTransformer(...) | XsltInjection.java:67:102:67:124 | getInputStream(...) : InputStream | XsltInjection.java:68:5:68:59 | newTransformer(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:67:102:67:124 | getInputStream(...) | this user input | -| XsltInjection.java:76:5:76:34 | newTransformer(...) | XsltInjection.java:72:44:72:66 | getInputStream(...) : InputStream | XsltInjection.java:76:5:76:34 | newTransformer(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:72:44:72:66 | getInputStream(...) | this user input | -| XsltInjection.java:83:5:83:34 | newTransformer(...) | XsltInjection.java:80:44:80:66 | getInputStream(...) : InputStream | XsltInjection.java:83:5:83:34 | newTransformer(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:80:44:80:66 | getInputStream(...) | this user input | -| XsltInjection.java:90:5:90:35 | load(...) | XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:90:5:90:35 | load(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:87:44:87:66 | getInputStream(...) | this user input | -| XsltInjection.java:91:5:91:37 | load30(...) | XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:91:5:91:37 | load30(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:87:44:87:66 | getInputStream(...) | this user input | -| XsltInjection.java:92:5:92:37 | load30(...) | XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:92:5:92:37 | load30(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:87:44:87:66 | getInputStream(...) | this user input | -| XsltInjection.java:93:5:93:37 | load30(...) | XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:93:5:93:37 | load30(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:87:44:87:66 | getInputStream(...) | this user input | -| XsltInjection.java:94:5:94:37 | load30(...) | XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:94:5:94:37 | load30(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:87:44:87:66 | getInputStream(...) | this user input | -| XsltInjection.java:95:5:95:37 | load30(...) | XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:95:5:95:37 | load30(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:87:44:87:66 | getInputStream(...) | this user input | -| XsltInjection.java:96:5:96:37 | load30(...) | XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:96:5:96:37 | load30(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:87:44:87:66 | getInputStream(...) | this user input | -| XsltInjection.java:97:5:97:37 | load30(...) | XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:97:5:97:37 | load30(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:87:44:87:66 | getInputStream(...) | this user input | -| XsltInjection.java:98:5:98:37 | load30(...) | XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:98:5:98:37 | load30(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:87:44:87:66 | getInputStream(...) | this user input | -| XsltInjection.java:99:5:99:37 | load30(...) | XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:99:5:99:37 | load30(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:87:44:87:66 | getInputStream(...) | this user input | -| XsltInjection.java:108:5:108:46 | load(...) | XsltInjection.java:103:36:103:61 | param : String | XsltInjection.java:108:5:108:46 | load(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:103:36:103:61 | param | this user input | -| XsltInjection.java:109:5:109:49 | load(...) | XsltInjection.java:105:44:105:66 | getInputStream(...) : InputStream | XsltInjection.java:109:5:109:49 | load(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:105:44:105:66 | getInputStream(...) | this user input | -| XsltInjection.java:110:5:110:50 | load(...) | XsltInjection.java:103:36:103:61 | param : String | XsltInjection.java:110:5:110:50 | load(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:103:36:103:61 | param | this user input | diff --git a/java/ql/test/query-tests/security/CWE-074/XsltInjection.qlref b/java/ql/test/query-tests/security/CWE-074/XsltInjection.qlref deleted file mode 100644 index 9d7fb59ac2c2..000000000000 --- a/java/ql/test/query-tests/security/CWE-074/XsltInjection.qlref +++ /dev/null @@ -1 +0,0 @@ -Security/CWE/CWE-074/XsltInjection.ql diff --git a/java/ql/test/query-tests/security/CWE-074/XsltInjectionTest.expected b/java/ql/test/query-tests/security/CWE-074/XsltInjectionTest.expected new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/java/ql/test/query-tests/security/CWE-074/XsltInjection.java b/java/ql/test/query-tests/security/CWE-074/XsltInjectionTest.java similarity index 68% rename from java/ql/test/query-tests/security/CWE-074/XsltInjection.java rename to java/ql/test/query-tests/security/CWE-074/XsltInjectionTest.java index d1a19217f02e..2bfd02a865c6 100644 --- a/java/ql/test/query-tests/security/CWE-074/XsltInjection.java +++ b/java/ql/test/query-tests/security/CWE-074/XsltInjectionTest.java @@ -25,47 +25,51 @@ import net.sf.saxon.s9api.XsltCompiler; @Controller -public class XsltInjection { +public class XsltInjectionTest { public void testStreamSourceInputStream(Socket socket) throws Exception { StreamSource source = new StreamSource(socket.getInputStream()); - TransformerFactory.newInstance().newTransformer(source).transform(null, null); + TransformerFactory.newInstance().newTransformer(source).transform(null, null); // $hasXsltInjection } public void testStreamSourceReader(Socket socket) throws Exception { StreamSource source = new StreamSource(new InputStreamReader(socket.getInputStream())); - TransformerFactory.newInstance().newTemplates(source).newTransformer().transform(null, null); + TransformerFactory.newInstance().newTemplates(source).newTransformer().transform(null, null); // $hasXsltInjection } @RequestMapping public void testStreamSourceInjectedParam(@RequestParam String param) throws Exception { String xslt = ""; StreamSource source = new StreamSource(new StringReader(xslt)); - TransformerFactory.newInstance().newTransformer(source).transform(null, null); + TransformerFactory.newInstance().newTransformer(source).transform(null, null); // $hasXsltInjection } public void testSAXSourceInputStream(Socket socket) throws Exception { SAXSource source = new SAXSource(new InputSource(socket.getInputStream())); - TransformerFactory.newInstance().newTemplates(source).newTransformer().transform(null, null); + TransformerFactory.newInstance().newTemplates(source).newTransformer().transform(null, null); // $hasXsltInjection } public void testSAXSourceReader(Socket socket) throws Exception { - SAXSource source = new SAXSource(null, new InputSource(new InputStreamReader(socket.getInputStream()))); - TransformerFactory.newInstance().newTransformer(source).transform(null, null); + SAXSource source = + new SAXSource(null, new InputSource(new InputStreamReader(socket.getInputStream()))); + TransformerFactory.newInstance().newTransformer(source).transform(null, null); // $hasXsltInjection } public void testStAXSourceEventReader(Socket socket) throws Exception { - StAXSource source = new StAXSource(XMLInputFactory.newInstance().createXMLEventReader(socket.getInputStream())); - TransformerFactory.newInstance().newTransformer(source).transform(null, null); + StAXSource source = + new StAXSource(XMLInputFactory.newInstance().createXMLEventReader(socket.getInputStream())); + TransformerFactory.newInstance().newTransformer(source).transform(null, null); // $hasXsltInjection } public void testStAXSourceEventStream(Socket socket) throws Exception { - StAXSource source = new StAXSource(XMLInputFactory.newInstance().createXMLStreamReader(null, new InputStreamReader(socket.getInputStream()))); - TransformerFactory.newInstance().newTemplates(source).newTransformer().transform(null, null); + StAXSource source = new StAXSource(XMLInputFactory.newInstance().createXMLStreamReader(null, + new InputStreamReader(socket.getInputStream()))); + TransformerFactory.newInstance().newTemplates(source).newTransformer().transform(null, null); // $hasXsltInjection } public void testDOMSource(Socket socket) throws Exception { - DOMSource source = new DOMSource(DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(socket.getInputStream())); - TransformerFactory.newInstance().newTransformer(source).transform(null, null); + DOMSource source = new DOMSource( + DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(socket.getInputStream())); + TransformerFactory.newInstance().newTransformer(source).transform(null, null); // $hasXsltInjection } public void testDisabledXXE(Socket socket) throws Exception { @@ -73,30 +77,30 @@ public void testDisabledXXE(Socket socket) throws Exception { TransformerFactory factory = TransformerFactory.newInstance(); factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, ""); - factory.newTransformer(source).transform(null, null); + factory.newTransformer(source).transform(null, null); // $hasXsltInjection } public void testFeatureSecureProcessingDisabled(Socket socket) throws Exception { StreamSource source = new StreamSource(socket.getInputStream()); TransformerFactory factory = TransformerFactory.newInstance(); factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, false); - factory.newTransformer(source).transform(null, null); + factory.newTransformer(source).transform(null, null); // $hasXsltInjection } public void testSaxon(Socket socket) throws Exception { StreamSource source = new StreamSource(socket.getInputStream()); XsltCompiler compiler = new Processor(true).newXsltCompiler(); - - compiler.compile(source).load().transform(); - compiler.compile(source).load30().transform(null, null); - compiler.compile(source).load30().applyTemplates((Source) null); - compiler.compile(source).load30().applyTemplates((Source) null, null); - compiler.compile(source).load30().applyTemplates((XdmValue) null); - compiler.compile(source).load30().applyTemplates((XdmValue) null, null); - compiler.compile(source).load30().callFunction(null, null); - compiler.compile(source).load30().callFunction(null, null, null); - compiler.compile(source).load30().callTemplate(null); - compiler.compile(source).load30().callTemplate(null, null); + + compiler.compile(source).load().transform(); // $hasXsltInjection + compiler.compile(source).load30().transform(null, null); // $hasXsltInjection + compiler.compile(source).load30().applyTemplates((Source) null); // $hasXsltInjection + compiler.compile(source).load30().applyTemplates((Source) null, null); // $hasXsltInjection + compiler.compile(source).load30().applyTemplates((XdmValue) null); // $hasXsltInjection + compiler.compile(source).load30().applyTemplates((XdmValue) null, null); // $hasXsltInjection + compiler.compile(source).load30().callFunction(null, null); // $hasXsltInjection + compiler.compile(source).load30().callFunction(null, null, null); // $hasXsltInjection + compiler.compile(source).load30().callTemplate(null); // $hasXsltInjection + compiler.compile(source).load30().callTemplate(null, null); // $hasXsltInjection } @RequestMapping @@ -104,24 +108,24 @@ public void testSaxonXsltPackage(@RequestParam String param, Socket socket) thro URI uri = new URI(param); StreamSource source = new StreamSource(socket.getInputStream()); XsltCompiler compiler = new Processor(true).newXsltCompiler(); - - compiler.loadExecutablePackage(uri).load().transform(); - compiler.compilePackage(source).link().load().transform(); - compiler.loadLibraryPackage(uri).link().load().transform(); + + compiler.loadExecutablePackage(uri).load().transform(); // $hasXsltInjection + compiler.compilePackage(source).link().load().transform(); // $hasXsltInjection + compiler.loadLibraryPackage(uri).link().load().transform(); // $hasXsltInjection } public void testOkFeatureSecureProcessing(Socket socket) throws Exception { StreamSource source = new StreamSource(socket.getInputStream()); TransformerFactory factory = TransformerFactory.newInstance(); factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); - factory.newTransformer(source).transform(null, null); + factory.newTransformer(source).transform(null, null); // Safe } public void testOkSaxon(Socket socket) throws Exception { StreamSource source = new StreamSource(socket.getInputStream()); XsltCompiler compiler = new Processor(true).newXsltCompiler(); - - compiler.compile(source).load().close(); - compiler.compile((Source) new Object()).load().transform(); + + compiler.compile(source).load().close(); // Safe + compiler.compile((Source) new Object()).load().transform(); // Safe } -} \ No newline at end of file +} diff --git a/java/ql/test/query-tests/security/CWE-074/XsltInjectionTest.ql b/java/ql/test/query-tests/security/CWE-074/XsltInjectionTest.ql new file mode 100644 index 000000000000..1ac2b87d5056 --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-074/XsltInjectionTest.ql @@ -0,0 +1,36 @@ +import java +import semmle.code.java.dataflow.TaintTracking +import semmle.code.java.dataflow.FlowSources +import semmle.code.java.security.XsltInjection +import TestUtilities.InlineExpectationsTest + +class Conf extends TaintTracking::Configuration { + Conf() { this = "test:cwe:xslt-injection" } + + override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node sink) { sink instanceof XsltInjectionSink } + + override predicate isSanitizer(DataFlow::Node node) { + node.getType() instanceof PrimitiveType or node.getType() instanceof BoxedType + } + + override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { + any(XsltInjectionAdditionalTaintStep c).step(node1, node2) + } +} + +class HasXsltInjectionTest extends InlineExpectationsTest { + HasXsltInjectionTest() { this = "HasXsltInjectionTest" } + + override string getARelevantTag() { result = "hasXsltInjection" } + + override predicate hasActualResult(Location location, string element, string tag, string value) { + tag = "hasXsltInjection" and + exists(DataFlow::Node src, DataFlow::Node sink, Conf conf | conf.hasFlow(src, sink) | + sink.getLocation() = location and + element = sink.toString() and + value = "" + ) + } +} From fc58ada92e15d899769757d96afce1c5fd1ae458 Mon Sep 17 00:00:00 2001 From: Tony Torralba Date: Thu, 17 Jun 2021 10:20:40 +0200 Subject: [PATCH 04/10] Add change note --- java/change-notes/2021-06-16-xslt-injection-query.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 java/change-notes/2021-06-16-xslt-injection-query.md diff --git a/java/change-notes/2021-06-16-xslt-injection-query.md b/java/change-notes/2021-06-16-xslt-injection-query.md new file mode 100644 index 000000000000..a6861f304f6a --- /dev/null +++ b/java/change-notes/2021-06-16-xslt-injection-query.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* The query "XSLT transformation with user-controlled stylesheet" (`java/xslt-injection`) has been promoted from experimental to the main query pack. Its results will now appear by default. This query was originally [submitted as an experimental query by @ggolawski](https://github.com/github/codeql/pull/3363) \ No newline at end of file From 6967b06dee236c7534500a8ae64c3c40df3855a7 Mon Sep 17 00:00:00 2001 From: Tony Torralba Date: Mon, 19 Jul 2021 13:16:11 +0200 Subject: [PATCH 05/10] Decouple XsltInjection.qll to reuse the taint tracking configuration --- .../code/java/dataflow/ExternalFlow.qll | 1 + .../src/Security/CWE/CWE-074/XsltInjection.ql | 22 +----------- ...ltInjection.qll => XsltInjectionQuery.qll} | 35 +++++++++++-------- .../java/security/XsltInjectionSinkModels.qll | 17 +++++++++ .../security/CWE-074/XsltInjectionTest.ql | 22 +++--------- 5 files changed, 43 insertions(+), 54 deletions(-) rename java/ql/src/semmle/code/java/security/{XsltInjection.qll => XsltInjectionQuery.qll} (91%) create mode 100644 java/ql/src/semmle/code/java/security/XsltInjectionSinkModels.qll diff --git a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll index bce80a3ee08a..f88cdcb15935 100644 --- a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll +++ b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll @@ -110,6 +110,7 @@ private module Frameworks { private import semmle.code.java.security.MvelInjection private import semmle.code.java.security.OgnlInjection private import semmle.code.java.security.XPath + private import semmle.code.java.security.XsltInjectionSinkModels private import semmle.code.java.frameworks.android.Android private import semmle.code.java.frameworks.android.SQLite private import semmle.code.java.frameworks.Jdbc diff --git a/java/ql/src/Security/CWE/CWE-074/XsltInjection.ql b/java/ql/src/Security/CWE/CWE-074/XsltInjection.ql index cd47d69470fe..3728ac9f3eeb 100644 --- a/java/ql/src/Security/CWE/CWE-074/XsltInjection.ql +++ b/java/ql/src/Security/CWE/CWE-074/XsltInjection.ql @@ -11,29 +11,9 @@ */ import java -import semmle.code.java.dataflow.FlowSources -import semmle.code.java.security.XsltInjection +import semmle.code.java.security.XsltInjectionQuery import DataFlow::PathGraph -/** - * A taint-tracking configuration for unvalidated user input that is used in XSLT transformation. - */ -class XsltInjectionFlowConfig extends TaintTracking::Configuration { - XsltInjectionFlowConfig() { this = "XsltInjectionFlowConfig" } - - override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } - - override predicate isSink(DataFlow::Node sink) { sink instanceof XsltInjectionSink } - - override predicate isSanitizer(DataFlow::Node node) { - node.getType() instanceof PrimitiveType or node.getType() instanceof BoxedType - } - - override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { - any(XsltInjectionAdditionalTaintStep c).step(node1, node2) - } -} - from DataFlow::PathNode source, DataFlow::PathNode sink, XsltInjectionFlowConfig conf where conf.hasFlowPath(source, sink) select sink.getNode(), source, sink, "XSLT transformation might include stylesheet from $@.", diff --git a/java/ql/src/semmle/code/java/security/XsltInjection.qll b/java/ql/src/semmle/code/java/security/XsltInjectionQuery.qll similarity index 91% rename from java/ql/src/semmle/code/java/security/XsltInjection.qll rename to java/ql/src/semmle/code/java/security/XsltInjectionQuery.qll index 4db9f7afd566..9a23223a997c 100644 --- a/java/ql/src/semmle/code/java/security/XsltInjection.qll +++ b/java/ql/src/semmle/code/java/security/XsltInjectionQuery.qll @@ -1,7 +1,7 @@ /** Provides classes to reason about XSLT injection vulnerabilities. */ import java -import semmle.code.java.dataflow.ExternalFlow +import semmle.code.java.dataflow.FlowSources import semmle.code.java.security.XmlParsers import semmle.code.java.dataflow.DataFlow @@ -11,20 +11,6 @@ import semmle.code.java.dataflow.DataFlow */ abstract class XsltInjectionSink extends DataFlow::Node { } -private class DefaultXsltInjectionSinkModel extends SinkModelCsv { - override predicate row(string row) { - row = - [ - "javax.xml.transform;Transformer;false;transform;;;Argument[-1];xslt", - "net.sf.saxon.s9api;XsltTransformer;false;transform;;;Argument[-1];xslt", - "net.sf.saxon.s9api;Xslt30Transformer;false;transform;;;Argument[-1];xslt", - "net.sf.saxon.s9api;Xslt30Transformer;false;applyTemplates;;;Argument[-1];xslt", - "net.sf.saxon.s9api;Xslt30Transformer;false;callFunction;;;Argument[-1];xslt", - "net.sf.saxon.s9api;Xslt30Transformer;false;callTemplate;;;Argument[-1];xslt" - ] - } -} - /** A default sink representing methods susceptible to XSLT Injection attacks. */ private class DefaultXsltInjectionSink extends XsltInjectionSink { DefaultXsltInjectionSink() { sinkNode(this, "xslt") } @@ -59,6 +45,25 @@ private class DefaultXsltInjectionAdditionalTaintStep extends XsltInjectionAddit } } +/** + * A taint-tracking configuration for unvalidated user input that is used in XSLT transformation. + */ +class XsltInjectionFlowConfig extends TaintTracking::Configuration { + XsltInjectionFlowConfig() { this = "XsltInjectionFlowConfig" } + + override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node sink) { sink instanceof XsltInjectionSink } + + override predicate isSanitizer(DataFlow::Node node) { + node.getType() instanceof PrimitiveType or node.getType() instanceof BoxedType + } + + override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { + any(XsltInjectionAdditionalTaintStep c).step(node1, node2) + } +} + /** * Holds if `n1` to `n2` is a dataflow step that converts between `InputStream` or `Reader` and * `XMLStreamReader`, i.e. `XMLInputFactory.createXMLStreamReader(tainted)`. diff --git a/java/ql/src/semmle/code/java/security/XsltInjectionSinkModels.qll b/java/ql/src/semmle/code/java/security/XsltInjectionSinkModels.qll new file mode 100644 index 000000000000..599e2e963d30 --- /dev/null +++ b/java/ql/src/semmle/code/java/security/XsltInjectionSinkModels.qll @@ -0,0 +1,17 @@ +/** Provides sink models relating to XSLT injection vulnerabilities. */ + +import semmle.code.java.dataflow.ExternalFlow + +private class DefaultXsltInjectionSinkModel extends SinkModelCsv { + override predicate row(string row) { + row = + [ + "javax.xml.transform;Transformer;false;transform;;;Argument[-1];xslt", + "net.sf.saxon.s9api;XsltTransformer;false;transform;;;Argument[-1];xslt", + "net.sf.saxon.s9api;Xslt30Transformer;false;transform;;;Argument[-1];xslt", + "net.sf.saxon.s9api;Xslt30Transformer;false;applyTemplates;;;Argument[-1];xslt", + "net.sf.saxon.s9api;Xslt30Transformer;false;callFunction;;;Argument[-1];xslt", + "net.sf.saxon.s9api;Xslt30Transformer;false;callTemplate;;;Argument[-1];xslt" + ] + } +} diff --git a/java/ql/test/query-tests/security/CWE-074/XsltInjectionTest.ql b/java/ql/test/query-tests/security/CWE-074/XsltInjectionTest.ql index 1ac2b87d5056..c2f02865b2e9 100644 --- a/java/ql/test/query-tests/security/CWE-074/XsltInjectionTest.ql +++ b/java/ql/test/query-tests/security/CWE-074/XsltInjectionTest.ql @@ -1,25 +1,9 @@ import java import semmle.code.java.dataflow.TaintTracking import semmle.code.java.dataflow.FlowSources -import semmle.code.java.security.XsltInjection +import semmle.code.java.security.XsltInjectionQuery import TestUtilities.InlineExpectationsTest -class Conf extends TaintTracking::Configuration { - Conf() { this = "test:cwe:xslt-injection" } - - override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } - - override predicate isSink(DataFlow::Node sink) { sink instanceof XsltInjectionSink } - - override predicate isSanitizer(DataFlow::Node node) { - node.getType() instanceof PrimitiveType or node.getType() instanceof BoxedType - } - - override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { - any(XsltInjectionAdditionalTaintStep c).step(node1, node2) - } -} - class HasXsltInjectionTest extends InlineExpectationsTest { HasXsltInjectionTest() { this = "HasXsltInjectionTest" } @@ -27,7 +11,9 @@ class HasXsltInjectionTest extends InlineExpectationsTest { override predicate hasActualResult(Location location, string element, string tag, string value) { tag = "hasXsltInjection" and - exists(DataFlow::Node src, DataFlow::Node sink, Conf conf | conf.hasFlow(src, sink) | + exists(DataFlow::Node src, DataFlow::Node sink, XsltInjectionFlowConfig conf | + conf.hasFlow(src, sink) + | sink.getLocation() = location and element = sink.toString() and value = "" From ff21662b23884deec9a842249916dbecc42ab4a0 Mon Sep 17 00:00:00 2001 From: Tony Torralba Date: Tue, 20 Jul 2021 17:55:45 +0200 Subject: [PATCH 06/10] Refactor XsltInjection.qll --- .../2021-06-16-xslt-injection-query.md | 2 +- .../code/java/security/XsltInjection.qll | 254 ++++++++++++++++++ .../code/java/security/XsltInjectionQuery.qll | 240 +---------------- .../java/security/XsltInjectionSinkModels.qll | 17 -- 4 files changed, 258 insertions(+), 255 deletions(-) create mode 100644 java/ql/src/semmle/code/java/security/XsltInjection.qll delete mode 100644 java/ql/src/semmle/code/java/security/XsltInjectionSinkModels.qll diff --git a/java/change-notes/2021-06-16-xslt-injection-query.md b/java/change-notes/2021-06-16-xslt-injection-query.md index a6861f304f6a..d30421607b3c 100644 --- a/java/change-notes/2021-06-16-xslt-injection-query.md +++ b/java/change-notes/2021-06-16-xslt-injection-query.md @@ -1,2 +1,2 @@ lgtm,codescanning -* The query "XSLT transformation with user-controlled stylesheet" (`java/xslt-injection`) has been promoted from experimental to the main query pack. Its results will now appear by default. This query was originally [submitted as an experimental query by @ggolawski](https://github.com/github/codeql/pull/3363) \ No newline at end of file +* The query "XSLT transformation with user-controlled stylesheet" (`java/xslt-injection`) has been promoted from experimental to the main query pack. Its results will now appear by default. This query was originally [submitted as an experimental query by @ggolawski](https://github.com/github/codeql/pull/3363). \ No newline at end of file diff --git a/java/ql/src/semmle/code/java/security/XsltInjection.qll b/java/ql/src/semmle/code/java/security/XsltInjection.qll new file mode 100644 index 000000000000..1ebf961e03c4 --- /dev/null +++ b/java/ql/src/semmle/code/java/security/XsltInjection.qll @@ -0,0 +1,254 @@ +/** Provides classes to reason about XSLT injection vulnerabilities. */ + +import java +import semmle.code.java.dataflow.DataFlow +import semmle.code.java.dataflow.ExternalFlow +import semmle.code.java.security.XmlParsers + +/** + * A data flow sink for unvalidated user input that is used in XSLT transformation. + * Extend this class to add your own XSLT Injection sinks. + */ +abstract class XsltInjectionSink extends DataFlow::Node { } + +/** A default sink representing methods susceptible to XSLT Injection attacks. */ +private class DefaultXsltInjectionSink extends XsltInjectionSink { + DefaultXsltInjectionSink() { sinkNode(this, "xslt") } +} + +private class DefaultXsltInjectionSinkModel extends SinkModelCsv { + override predicate row(string row) { + row = + [ + "javax.xml.transform;Transformer;false;transform;;;Argument[-1];xslt", + "net.sf.saxon.s9api;XsltTransformer;false;transform;;;Argument[-1];xslt", + "net.sf.saxon.s9api;Xslt30Transformer;false;transform;;;Argument[-1];xslt", + "net.sf.saxon.s9api;Xslt30Transformer;false;applyTemplates;;;Argument[-1];xslt", + "net.sf.saxon.s9api;Xslt30Transformer;false;callFunction;;;Argument[-1];xslt", + "net.sf.saxon.s9api;Xslt30Transformer;false;callTemplate;;;Argument[-1];xslt" + ] + } +} + +/** + * A unit class for adding additional taint steps. + * + * Extend this class to add additional taint steps that should apply to the `XsltInjectionFlowConfig`. + */ +class XsltInjectionAdditionalTaintStep extends Unit { + /** + * Holds if the step from `node1` to `node2` should be considered a taint + * step for the `XsltInjectionFlowConfig` configuration. + */ + abstract predicate step(DataFlow::Node node1, DataFlow::Node node2); +} + +/** A set of additional taint steps to consider when taint tracking XSLT related data flows. */ +private class DefaultXsltInjectionAdditionalTaintStep extends XsltInjectionAdditionalTaintStep { + override predicate step(DataFlow::Node node1, DataFlow::Node node2) { + xmlStreamReaderStep(node1, node2) or + xmlEventReaderStep(node1, node2) or + staxSourceStep(node1, node2) or + documentBuilderStep(node1, node2) or + domSourceStep(node1, node2) or + newTransformerOrTemplatesStep(node1, node2) or + newTransformerFromTemplatesStep(node1, node2) or + xsltCompilerStep(node1, node2) or + xsltExecutableStep(node1, node2) or + xsltPackageStep(node1, node2) + } +} + +/** + * Holds if `n1` to `n2` is a dataflow step that converts between `InputStream` or `Reader` and + * `XMLStreamReader`, i.e. `XMLInputFactory.createXMLStreamReader(tainted)`. + */ +private predicate xmlStreamReaderStep(DataFlow::Node n1, DataFlow::Node n2) { + exists(XmlInputFactoryStreamReader xmlStreamReader | + n1.asExpr() = xmlStreamReader.getSink() and + n2.asExpr() = xmlStreamReader + ) +} + +/** + * Holds if `n1` to `n2` is a dataflow step that converts between `InputStream` or `Reader` and + * `XMLEventReader`, i.e. `XMLInputFactory.createXMLEventReader(tainted)`. + */ +private predicate xmlEventReaderStep(DataFlow::Node n1, DataFlow::Node n2) { + exists(XmlInputFactoryEventReader xmlEventReader | + n1.asExpr() = xmlEventReader.getSink() and + n2.asExpr() = xmlEventReader + ) +} + +/** + * Holds if `n1` to `n2` is a dataflow step that converts between `XMLStreamReader` or + * `XMLEventReader` and `StAXSource`, i.e. `new StAXSource(tainted)`. + */ +private predicate staxSourceStep(DataFlow::Node n1, DataFlow::Node n2) { + exists(ConstructorCall cc | cc.getConstructedType() instanceof TypeStAXSource | + n1.asExpr() = cc.getAnArgument() and + n2.asExpr() = cc + ) +} + +/** + * Holds if `n1` to `n2` is a dataflow step that converts between `InputStream` and `Document`, + * i.e. `DocumentBuilder.parse(tainted)`. + */ +private predicate documentBuilderStep(DataFlow::Node n1, DataFlow::Node n2) { + exists(DocumentBuilderParse documentBuilder | + n1.asExpr() = documentBuilder.getSink() and + n2.asExpr() = documentBuilder + ) +} + +/** + * Holds if `n1` to `n2` is a dataflow step that converts between `Document` and `DOMSource`, i.e. + * `new DOMSource(tainted)`. + */ +private predicate domSourceStep(DataFlow::Node n1, DataFlow::Node n2) { + exists(ConstructorCall cc | cc.getConstructedType() instanceof TypeDOMSource | + n1.asExpr() = cc.getAnArgument() and + n2.asExpr() = cc + ) +} + +/** + * Holds if `n1` to `n2` is a dataflow step that converts between `Source` and `Transformer` or + * `Templates`, i.e. `TransformerFactory.newTransformer(tainted)` or + * `TransformerFactory.newTemplates(tainted)`. + */ +private predicate newTransformerOrTemplatesStep(DataFlow::Node n1, DataFlow::Node n2) { + exists(MethodAccess ma, Method m | ma.getMethod() = m | + n1.asExpr() = ma.getAnArgument() and + n2.asExpr() = ma and + m.getDeclaringType() instanceof TransformerFactory and + m.hasName(["newTransformer", "newTemplates"]) and + not exists(TransformerFactoryWithSecureProcessingFeatureFlowConfig conf | + conf.hasFlowToExpr(ma.getQualifier()) + ) + ) +} + +/** + * A data flow configuration for secure processing feature that is enabled on `TransformerFactory`. + */ +private class TransformerFactoryWithSecureProcessingFeatureFlowConfig extends DataFlow2::Configuration { + TransformerFactoryWithSecureProcessingFeatureFlowConfig() { + this = "TransformerFactoryWithSecureProcessingFeatureFlowConfig" + } + + override predicate isSource(DataFlow::Node src) { + exists(Variable v | v = src.asExpr().(VarAccess).getVariable() | + exists(TransformerFactoryFeatureConfig config | config.getQualifier() = v.getAnAccess() | + config.enables(configSecureProcessing()) + ) + ) + } + + override predicate isSink(DataFlow::Node sink) { + exists(MethodAccess ma | + sink.asExpr() = ma.getQualifier() and + ma.getMethod().getDeclaringType() instanceof TransformerFactory + ) + } + + override int fieldFlowBranchLimit() { result = 0 } +} + +/** A `ParserConfig` specific to `TransformerFactory`. */ +private class TransformerFactoryFeatureConfig extends ParserConfig { + TransformerFactoryFeatureConfig() { + exists(Method m | + m = this.getMethod() and + m.getDeclaringType() instanceof TransformerFactory and + m.hasName("setFeature") + ) + } +} + +/** + * Holds if `n1` to `n2` is a dataflow step that converts between `Templates` and `Transformer`, + * i.e. `tainted.newTransformer()`. + */ +private predicate newTransformerFromTemplatesStep(DataFlow::Node n1, DataFlow::Node n2) { + exists(MethodAccess ma, Method m | ma.getMethod() = m | + n1.asExpr() = ma.getQualifier() and + n2.asExpr() = ma and + m.getDeclaringType() instanceof TypeTemplates and + m.hasName("newTransformer") + ) +} + +/** + * Holds if `n1` to `n2` is a dataflow step that converts between `Source` or `URI` and + * `XsltExecutable` or `XsltPackage`, i.e. `XsltCompiler.compile(tainted)` or + * `XsltCompiler.loadExecutablePackage(tainted)` or `XsltCompiler.compilePackage(tainted)` or + * `XsltCompiler.loadLibraryPackage(tainted)`. + */ +private predicate xsltCompilerStep(DataFlow::Node n1, DataFlow::Node n2) { + exists(MethodAccess ma, Method m | ma.getMethod() = m | + n1.asExpr() = ma.getArgument(0) and + n2.asExpr() = ma and + m.getDeclaringType() instanceof TypeXsltCompiler and + m.hasName(["compile", "loadExecutablePackage", "compilePackage", "loadLibraryPackage"]) + ) +} + +/** + * Holds if `n1` to `n2` is a dataflow step that converts between `XsltExecutable` and + * `XsltTransformer` or `Xslt30Transformer`, i.e. `XsltExecutable.load()` or + * `XsltExecutable.load30()`. + */ +private predicate xsltExecutableStep(DataFlow::Node n1, DataFlow::Node n2) { + exists(MethodAccess ma, Method m | ma.getMethod() = m | + n1.asExpr() = ma.getQualifier() and + n2.asExpr() = ma and + m.getDeclaringType() instanceof TypeXsltExecutable and + m.hasName(["load", "load30"]) + ) +} + +/** + * Holds if `n1` to `n2` is a dataflow step that converts between `XsltPackage` and + * `XsltExecutable`, i.e. `XsltPackage.link()`. + */ +private predicate xsltPackageStep(DataFlow::Node n1, DataFlow::Node n2) { + exists(MethodAccess ma, Method m | ma.getMethod() = m | + n1.asExpr() = ma.getQualifier() and + n2.asExpr() = ma and + m.getDeclaringType() instanceof TypeXsltPackage and + m.hasName("link") + ) +} + +/** The class `javax.xml.transform.stax.StAXSource`. */ +private class TypeStAXSource extends Class { + TypeStAXSource() { this.hasQualifiedName("javax.xml.transform.stax", "StAXSource") } +} + +/** The class `javax.xml.transform.dom.DOMSource`. */ +private class TypeDOMSource extends Class { + TypeDOMSource() { this.hasQualifiedName("javax.xml.transform.dom", "DOMSource") } +} + +/** The interface `javax.xml.transform.Templates`. */ +private class TypeTemplates extends Interface { + TypeTemplates() { this.hasQualifiedName("javax.xml.transform", "Templates") } +} + +/** The class `net.sf.saxon.s9api.XsltCompiler`. */ +private class TypeXsltCompiler extends Class { + TypeXsltCompiler() { this.hasQualifiedName("net.sf.saxon.s9api", "XsltCompiler") } +} + +/** The class `net.sf.saxon.s9api.XsltExecutable`. */ +private class TypeXsltExecutable extends Class { + TypeXsltExecutable() { this.hasQualifiedName("net.sf.saxon.s9api", "XsltExecutable") } +} + +/** The class `net.sf.saxon.s9api.XsltPackage`. */ +private class TypeXsltPackage extends Class { + TypeXsltPackage() { this.hasQualifiedName("net.sf.saxon.s9api", "XsltPackage") } +} diff --git a/java/ql/src/semmle/code/java/security/XsltInjectionQuery.qll b/java/ql/src/semmle/code/java/security/XsltInjectionQuery.qll index 9a23223a997c..8beafb22e10b 100644 --- a/java/ql/src/semmle/code/java/security/XsltInjectionQuery.qll +++ b/java/ql/src/semmle/code/java/security/XsltInjectionQuery.qll @@ -1,49 +1,9 @@ -/** Provides classes to reason about XSLT injection vulnerabilities. */ +/** Provides taint tracking configurations to be used in XSLT injection queries. */ import java import semmle.code.java.dataflow.FlowSources -import semmle.code.java.security.XmlParsers -import semmle.code.java.dataflow.DataFlow - -/** - * A data flow sink for unvalidated user input that is used in XSLT transformation. - * Extend this class to add your own XSLT Injection sinks. - */ -abstract class XsltInjectionSink extends DataFlow::Node { } - -/** A default sink representing methods susceptible to XSLT Injection attacks. */ -private class DefaultXsltInjectionSink extends XsltInjectionSink { - DefaultXsltInjectionSink() { sinkNode(this, "xslt") } -} - -/** - * A unit class for adding additional taint steps. - * - * Extend this class to add additional taint steps that should apply to the `XsltInjectionFlowConfig`. - */ -class XsltInjectionAdditionalTaintStep extends Unit { - /** - * Holds if the step from `node1` to `node2` should be considered a taint - * step for the `XsltInjectionFlowConfig` configuration. - */ - abstract predicate step(DataFlow::Node node1, DataFlow::Node node2); -} - -/** A set of additional taint steps to consider when taint tracking XSLT related data flows. */ -private class DefaultXsltInjectionAdditionalTaintStep extends XsltInjectionAdditionalTaintStep { - override predicate step(DataFlow::Node node1, DataFlow::Node node2) { - xmlStreamReaderStep(node1, node2) or - xmlEventReaderStep(node1, node2) or - staxSourceStep(node1, node2) or - documentBuilderStep(node1, node2) or - domSourceStep(node1, node2) or - newTransformerOrTemplatesStep(node1, node2) or - newTransformerFromTemplatesStep(node1, node2) or - xsltCompilerStep(node1, node2) or - xsltExecutableStep(node1, node2) or - xsltPackageStep(node1, node2) - } -} +import semmle.code.java.dataflow.TaintTracking +import semmle.code.java.security.XsltInjection /** * A taint-tracking configuration for unvalidated user input that is used in XSLT transformation. @@ -63,197 +23,3 @@ class XsltInjectionFlowConfig extends TaintTracking::Configuration { any(XsltInjectionAdditionalTaintStep c).step(node1, node2) } } - -/** - * Holds if `n1` to `n2` is a dataflow step that converts between `InputStream` or `Reader` and - * `XMLStreamReader`, i.e. `XMLInputFactory.createXMLStreamReader(tainted)`. - */ -private predicate xmlStreamReaderStep(DataFlow::Node n1, DataFlow::Node n2) { - exists(XmlInputFactoryStreamReader xmlStreamReader | - n1.asExpr() = xmlStreamReader.getSink() and - n2.asExpr() = xmlStreamReader - ) -} - -/** - * Holds if `n1` to `n2` is a dataflow step that converts between `InputStream` or `Reader` and - * `XMLEventReader`, i.e. `XMLInputFactory.createXMLEventReader(tainted)`. - */ -private predicate xmlEventReaderStep(DataFlow::Node n1, DataFlow::Node n2) { - exists(XmlInputFactoryEventReader xmlEventReader | - n1.asExpr() = xmlEventReader.getSink() and - n2.asExpr() = xmlEventReader - ) -} - -/** - * Holds if `n1` to `n2` is a dataflow step that converts between `XMLStreamReader` or - * `XMLEventReader` and `StAXSource`, i.e. `new StAXSource(tainted)`. - */ -private predicate staxSourceStep(DataFlow::Node n1, DataFlow::Node n2) { - exists(ConstructorCall cc | cc.getConstructedType() instanceof TypeStAXSource | - n1.asExpr() = cc.getAnArgument() and - n2.asExpr() = cc - ) -} - -/** - * Holds if `n1` to `n2` is a dataflow step that converts between `InputStream` and `Document`, - * i.e. `DocumentBuilder.parse(tainted)`. - */ -private predicate documentBuilderStep(DataFlow::Node n1, DataFlow::Node n2) { - exists(DocumentBuilderParse documentBuilder | - n1.asExpr() = documentBuilder.getSink() and - n2.asExpr() = documentBuilder - ) -} - -/** - * Holds if `n1` to `n2` is a dataflow step that converts between `Document` and `DOMSource`, i.e. - * `new DOMSource(tainted)`. - */ -private predicate domSourceStep(DataFlow::Node n1, DataFlow::Node n2) { - exists(ConstructorCall cc | cc.getConstructedType() instanceof TypeDOMSource | - n1.asExpr() = cc.getAnArgument() and - n2.asExpr() = cc - ) -} - -/** - * Holds if `n1` to `n2` is a dataflow step that converts between `Source` and `Transformer` or - * `Templates`, i.e. `TransformerFactory.newTransformer(tainted)` or - * `TransformerFactory.newTemplates(tainted)`. - */ -private predicate newTransformerOrTemplatesStep(DataFlow::Node n1, DataFlow::Node n2) { - exists(MethodAccess ma, Method m | ma.getMethod() = m | - n1.asExpr() = ma.getAnArgument() and - n2.asExpr() = ma and - m.getDeclaringType() instanceof TransformerFactory and - m.hasName(["newTransformer", "newTemplates"]) and - not exists(TransformerFactoryWithSecureProcessingFeatureFlowConfig conf | - conf.hasFlowToExpr(ma.getQualifier()) - ) - ) -} - -/** - * A data flow configuration for secure processing feature that is enabled on `TransformerFactory`. - */ -private class TransformerFactoryWithSecureProcessingFeatureFlowConfig extends DataFlow2::Configuration { - TransformerFactoryWithSecureProcessingFeatureFlowConfig() { - this = "TransformerFactoryWithSecureProcessingFeatureFlowConfig" - } - - override predicate isSource(DataFlow::Node src) { - exists(Variable v | v = src.asExpr().(VarAccess).getVariable() | - exists(TransformerFactoryFeatureConfig config | config.getQualifier() = v.getAnAccess() | - config.enables(configSecureProcessing()) - ) - ) - } - - override predicate isSink(DataFlow::Node sink) { - exists(MethodAccess ma | - sink.asExpr() = ma.getQualifier() and - ma.getMethod().getDeclaringType() instanceof TransformerFactory - ) - } - - override int fieldFlowBranchLimit() { result = 0 } -} - -/** A `ParserConfig` specific to `TransformerFactory`. */ -private class TransformerFactoryFeatureConfig extends ParserConfig { - TransformerFactoryFeatureConfig() { - exists(Method m | - m = this.getMethod() and - m.getDeclaringType() instanceof TransformerFactory and - m.hasName("setFeature") - ) - } -} - -/** - * Holds if `n1` to `n2` is a dataflow step that converts between `Templates` and `Transformer`, - * i.e. `tainted.newTransformer()`. - */ -private predicate newTransformerFromTemplatesStep(DataFlow::Node n1, DataFlow::Node n2) { - exists(MethodAccess ma, Method m | ma.getMethod() = m | - n1.asExpr() = ma.getQualifier() and - n2.asExpr() = ma and - m.getDeclaringType() instanceof TypeTemplates and - m.hasName("newTransformer") - ) -} - -/** - * Holds if `n1` to `n2` is a dataflow step that converts between `Source` or `URI` and - * `XsltExecutable` or `XsltPackage`, i.e. `XsltCompiler.compile(tainted)` or - * `XsltCompiler.loadExecutablePackage(tainted)` or `XsltCompiler.compilePackage(tainted)` or - * `XsltCompiler.loadLibraryPackage(tainted)`. - */ -private predicate xsltCompilerStep(DataFlow::Node n1, DataFlow::Node n2) { - exists(MethodAccess ma, Method m | ma.getMethod() = m | - n1.asExpr() = ma.getArgument(0) and - n2.asExpr() = ma and - m.getDeclaringType() instanceof TypeXsltCompiler and - m.hasName(["compile", "loadExecutablePackage", "compilePackage", "loadLibraryPackage"]) - ) -} - -/** - * Holds if `n1` to `n2` is a dataflow step that converts between `XsltExecutable` and - * `XsltTransformer` or `Xslt30Transformer`, i.e. `XsltExecutable.load()` or - * `XsltExecutable.load30()`. - */ -private predicate xsltExecutableStep(DataFlow::Node n1, DataFlow::Node n2) { - exists(MethodAccess ma, Method m | ma.getMethod() = m | - n1.asExpr() = ma.getQualifier() and - n2.asExpr() = ma and - m.getDeclaringType() instanceof TypeXsltExecutable and - m.hasName(["load", "load30"]) - ) -} - -/** - * Holds if `n1` to `n2` is a dataflow step that converts between `XsltPackage` and - * `XsltExecutable`, i.e. `XsltPackage.link()`. - */ -private predicate xsltPackageStep(DataFlow::Node n1, DataFlow::Node n2) { - exists(MethodAccess ma, Method m | ma.getMethod() = m | - n1.asExpr() = ma.getQualifier() and - n2.asExpr() = ma and - m.getDeclaringType() instanceof TypeXsltPackage and - m.hasName("link") - ) -} - -/** The class `javax.xml.transform.stax.StAXSource`. */ -private class TypeStAXSource extends Class { - TypeStAXSource() { this.hasQualifiedName("javax.xml.transform.stax", "StAXSource") } -} - -/** The class `javax.xml.transform.dom.DOMSource`. */ -private class TypeDOMSource extends Class { - TypeDOMSource() { this.hasQualifiedName("javax.xml.transform.dom", "DOMSource") } -} - -/** The interface `javax.xml.transform.Templates`. */ -private class TypeTemplates extends Interface { - TypeTemplates() { this.hasQualifiedName("javax.xml.transform", "Templates") } -} - -/** The class `net.sf.saxon.s9api.XsltCompiler`. */ -private class TypeXsltCompiler extends Class { - TypeXsltCompiler() { this.hasQualifiedName("net.sf.saxon.s9api", "XsltCompiler") } -} - -/** The class `net.sf.saxon.s9api.XsltExecutable`. */ -private class TypeXsltExecutable extends Class { - TypeXsltExecutable() { this.hasQualifiedName("net.sf.saxon.s9api", "XsltExecutable") } -} - -/** The class `net.sf.saxon.s9api.XsltPackage`. */ -private class TypeXsltPackage extends Class { - TypeXsltPackage() { this.hasQualifiedName("net.sf.saxon.s9api", "XsltPackage") } -} diff --git a/java/ql/src/semmle/code/java/security/XsltInjectionSinkModels.qll b/java/ql/src/semmle/code/java/security/XsltInjectionSinkModels.qll deleted file mode 100644 index 599e2e963d30..000000000000 --- a/java/ql/src/semmle/code/java/security/XsltInjectionSinkModels.qll +++ /dev/null @@ -1,17 +0,0 @@ -/** Provides sink models relating to XSLT injection vulnerabilities. */ - -import semmle.code.java.dataflow.ExternalFlow - -private class DefaultXsltInjectionSinkModel extends SinkModelCsv { - override predicate row(string row) { - row = - [ - "javax.xml.transform;Transformer;false;transform;;;Argument[-1];xslt", - "net.sf.saxon.s9api;XsltTransformer;false;transform;;;Argument[-1];xslt", - "net.sf.saxon.s9api;Xslt30Transformer;false;transform;;;Argument[-1];xslt", - "net.sf.saxon.s9api;Xslt30Transformer;false;applyTemplates;;;Argument[-1];xslt", - "net.sf.saxon.s9api;Xslt30Transformer;false;callFunction;;;Argument[-1];xslt", - "net.sf.saxon.s9api;Xslt30Transformer;false;callTemplate;;;Argument[-1];xslt" - ] - } -} From 13417dbf14f73eb86443e6579363bf314890bb3a Mon Sep 17 00:00:00 2001 From: Tony Torralba Date: Wed, 21 Jul 2021 11:12:31 +0200 Subject: [PATCH 07/10] Remove DataFlow references from XsltInjection.qll --- .../code/java/security/XsltInjection.qll | 112 +++++++++--------- .../code/java/security/XsltInjectionQuery.qll | 65 ++++++++++ 2 files changed, 118 insertions(+), 59 deletions(-) diff --git a/java/ql/src/semmle/code/java/security/XsltInjection.qll b/java/ql/src/semmle/code/java/security/XsltInjection.qll index 1ebf961e03c4..752884920046 100644 --- a/java/ql/src/semmle/code/java/security/XsltInjection.qll +++ b/java/ql/src/semmle/code/java/security/XsltInjection.qll @@ -3,7 +3,6 @@ import java import semmle.code.java.dataflow.DataFlow import semmle.code.java.dataflow.ExternalFlow -import semmle.code.java.security.XmlParsers /** * A data flow sink for unvalidated user input that is used in XSLT transformation. @@ -51,7 +50,6 @@ private class DefaultXsltInjectionAdditionalTaintStep extends XsltInjectionAddit staxSourceStep(node1, node2) or documentBuilderStep(node1, node2) or domSourceStep(node1, node2) or - newTransformerOrTemplatesStep(node1, node2) or newTransformerFromTemplatesStep(node1, node2) or xsltCompilerStep(node1, node2) or xsltExecutableStep(node1, node2) or @@ -65,7 +63,10 @@ private class DefaultXsltInjectionAdditionalTaintStep extends XsltInjectionAddit */ private predicate xmlStreamReaderStep(DataFlow::Node n1, DataFlow::Node n2) { exists(XmlInputFactoryStreamReader xmlStreamReader | - n1.asExpr() = xmlStreamReader.getSink() and + if xmlStreamReader.getMethod().getParameterType(0) instanceof TypeString + then n1.asExpr() = xmlStreamReader.getArgument(1) + else n1.asExpr() = xmlStreamReader.getArgument(0) + | n2.asExpr() = xmlStreamReader ) } @@ -76,7 +77,10 @@ private predicate xmlStreamReaderStep(DataFlow::Node n1, DataFlow::Node n2) { */ private predicate xmlEventReaderStep(DataFlow::Node n1, DataFlow::Node n2) { exists(XmlInputFactoryEventReader xmlEventReader | - n1.asExpr() = xmlEventReader.getSink() and + if xmlEventReader.getMethod().getParameterType(0) instanceof TypeString + then n1.asExpr() = xmlEventReader.getArgument(1) + else n1.asExpr() = xmlEventReader.getArgument(0) + | n2.asExpr() = xmlEventReader ) } @@ -98,7 +102,7 @@ private predicate staxSourceStep(DataFlow::Node n1, DataFlow::Node n2) { */ private predicate documentBuilderStep(DataFlow::Node n1, DataFlow::Node n2) { exists(DocumentBuilderParse documentBuilder | - n1.asExpr() = documentBuilder.getSink() and + n1.asExpr() = documentBuilder.getArgument(0) and n2.asExpr() = documentBuilder ) } @@ -114,60 +118,6 @@ private predicate domSourceStep(DataFlow::Node n1, DataFlow::Node n2) { ) } -/** - * Holds if `n1` to `n2` is a dataflow step that converts between `Source` and `Transformer` or - * `Templates`, i.e. `TransformerFactory.newTransformer(tainted)` or - * `TransformerFactory.newTemplates(tainted)`. - */ -private predicate newTransformerOrTemplatesStep(DataFlow::Node n1, DataFlow::Node n2) { - exists(MethodAccess ma, Method m | ma.getMethod() = m | - n1.asExpr() = ma.getAnArgument() and - n2.asExpr() = ma and - m.getDeclaringType() instanceof TransformerFactory and - m.hasName(["newTransformer", "newTemplates"]) and - not exists(TransformerFactoryWithSecureProcessingFeatureFlowConfig conf | - conf.hasFlowToExpr(ma.getQualifier()) - ) - ) -} - -/** - * A data flow configuration for secure processing feature that is enabled on `TransformerFactory`. - */ -private class TransformerFactoryWithSecureProcessingFeatureFlowConfig extends DataFlow2::Configuration { - TransformerFactoryWithSecureProcessingFeatureFlowConfig() { - this = "TransformerFactoryWithSecureProcessingFeatureFlowConfig" - } - - override predicate isSource(DataFlow::Node src) { - exists(Variable v | v = src.asExpr().(VarAccess).getVariable() | - exists(TransformerFactoryFeatureConfig config | config.getQualifier() = v.getAnAccess() | - config.enables(configSecureProcessing()) - ) - ) - } - - override predicate isSink(DataFlow::Node sink) { - exists(MethodAccess ma | - sink.asExpr() = ma.getQualifier() and - ma.getMethod().getDeclaringType() instanceof TransformerFactory - ) - } - - override int fieldFlowBranchLimit() { result = 0 } -} - -/** A `ParserConfig` specific to `TransformerFactory`. */ -private class TransformerFactoryFeatureConfig extends ParserConfig { - TransformerFactoryFeatureConfig() { - exists(Method m | - m = this.getMethod() and - m.getDeclaringType() instanceof TransformerFactory and - m.hasName("setFeature") - ) - } -} - /** * Holds if `n1` to `n2` is a dataflow step that converts between `Templates` and `Transformer`, * i.e. `tainted.newTransformer()`. @@ -252,3 +202,47 @@ private class TypeXsltExecutable extends Class { private class TypeXsltPackage extends Class { TypeXsltPackage() { this.hasQualifiedName("net.sf.saxon.s9api", "XsltPackage") } } + +// XmlParsers classes +/** A call to `DocumentBuilder.parse`. */ +private class DocumentBuilderParse extends MethodAccess { + DocumentBuilderParse() { + exists(Method m | + this.getMethod() = m and + m.getDeclaringType() instanceof DocumentBuilder and + m.hasName("parse") + ) + } +} + +/** The class `javax.xml.parsers.DocumentBuilder`. */ +private class DocumentBuilder extends RefType { + DocumentBuilder() { this.hasQualifiedName("javax.xml.parsers", "DocumentBuilder") } +} + +/** A call to `XMLInputFactory.createXMLStreamReader`. */ +private class XmlInputFactoryStreamReader extends MethodAccess { + XmlInputFactoryStreamReader() { + exists(Method m | + this.getMethod() = m and + m.getDeclaringType() instanceof XmlInputFactory and + m.hasName("createXMLStreamReader") + ) + } +} + +/** A call to `XMLInputFactory.createEventReader`. */ +private class XmlInputFactoryEventReader extends MethodAccess { + XmlInputFactoryEventReader() { + exists(Method m | + this.getMethod() = m and + m.getDeclaringType() instanceof XmlInputFactory and + m.hasName("createXMLEventReader") + ) + } +} + +/** The class `javax.xml.stream.XMLInputFactory`. */ +private class XmlInputFactory extends RefType { + XmlInputFactory() { this.hasQualifiedName("javax.xml.stream", "XMLInputFactory") } +} diff --git a/java/ql/src/semmle/code/java/security/XsltInjectionQuery.qll b/java/ql/src/semmle/code/java/security/XsltInjectionQuery.qll index 8beafb22e10b..34e533c00405 100644 --- a/java/ql/src/semmle/code/java/security/XsltInjectionQuery.qll +++ b/java/ql/src/semmle/code/java/security/XsltInjectionQuery.qll @@ -3,6 +3,7 @@ import java import semmle.code.java.dataflow.FlowSources import semmle.code.java.dataflow.TaintTracking +import semmle.code.java.security.XmlParsers import semmle.code.java.security.XsltInjection /** @@ -23,3 +24,67 @@ class XsltInjectionFlowConfig extends TaintTracking::Configuration { any(XsltInjectionAdditionalTaintStep c).step(node1, node2) } } + +/** + * A set of additional taint steps to consider when taint tracking XSLT related data flows. + * These steps use data flow logic themselves. + */ +private class DataFlowXsltInjectionAdditionalTaintStep extends XsltInjectionAdditionalTaintStep { + override predicate step(DataFlow::Node node1, DataFlow::Node node2) { + newTransformerOrTemplatesStep(node1, node2) + } +} + +/** + * Holds if `n1` to `n2` is a dataflow step that converts between `Source` and `Transformer` or + * `Templates`, i.e. `TransformerFactory.newTransformer(tainted)` or + * `TransformerFactory.newTemplates(tainted)`. + */ +private predicate newTransformerOrTemplatesStep(DataFlow::Node n1, DataFlow::Node n2) { + exists(MethodAccess ma, Method m | ma.getMethod() = m | + n1.asExpr() = ma.getAnArgument() and + n2.asExpr() = ma and + m.getDeclaringType() instanceof TransformerFactory and + m.hasName(["newTransformer", "newTemplates"]) and + not exists(TransformerFactoryWithSecureProcessingFeatureFlowConfig conf | + conf.hasFlowToExpr(ma.getQualifier()) + ) + ) +} + +/** + * A data flow configuration for secure processing feature that is enabled on `TransformerFactory`. + */ +private class TransformerFactoryWithSecureProcessingFeatureFlowConfig extends DataFlow2::Configuration { + TransformerFactoryWithSecureProcessingFeatureFlowConfig() { + this = "TransformerFactoryWithSecureProcessingFeatureFlowConfig" + } + + override predicate isSource(DataFlow::Node src) { + exists(Variable v | v = src.asExpr().(VarAccess).getVariable() | + exists(TransformerFactoryFeatureConfig config | config.getQualifier() = v.getAnAccess() | + config.enables(configSecureProcessing()) + ) + ) + } + + override predicate isSink(DataFlow::Node sink) { + exists(MethodAccess ma | + sink.asExpr() = ma.getQualifier() and + ma.getMethod().getDeclaringType() instanceof TransformerFactory + ) + } + + override int fieldFlowBranchLimit() { result = 0 } +} + +/** A `ParserConfig` specific to `TransformerFactory`. */ +private class TransformerFactoryFeatureConfig extends ParserConfig { + TransformerFactoryFeatureConfig() { + exists(Method m | + m = this.getMethod() and + m.getDeclaringType() instanceof TransformerFactory and + m.hasName("setFeature") + ) + } +} From 95751fcc21cfe82e7aac3418c3046ec34496135c Mon Sep 17 00:00:00 2001 From: mc <42146119+mchammer01@users.noreply.github.com> Date: Thu, 29 Jul 2021 11:17:39 +0100 Subject: [PATCH 08/10] Update XsltInjection.qhelp Made a few minor tweaks during editorial review --- java/ql/src/Security/CWE/CWE-074/XsltInjection.qhelp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/java/ql/src/Security/CWE/CWE-074/XsltInjection.qhelp b/java/ql/src/Security/CWE/CWE-074/XsltInjection.qhelp index 4392550d33fb..402f4f7f5db1 100644 --- a/java/ql/src/Security/CWE/CWE-074/XsltInjection.qhelp +++ b/java/ql/src/Security/CWE/CWE-074/XsltInjection.qhelp @@ -4,12 +4,12 @@

XSLT (Extensible Stylesheet Language Transformations) is a language for transforming XML -documents into other XML documents or other formats. Processing of unvalidated XSLT stylesheet can -let attacker to read arbitrary files from the filesystem or to execute arbitrary code.

+documents into other XML documents or other formats. Processing unvalidated XSLT stylesheets can +allow attackers to read arbitrary files from the filesystem or to execute arbitrary code.

-

The general recommendation is to not process untrusted XSLT stylesheets. If user provided +

The general recommendation is to not process untrusted XSLT stylesheets. If user-provided stylesheets must be processed, enable the secure processing mode.

@@ -17,7 +17,7 @@ stylesheets must be processed, enable the secure processing mode.

In the following examples, the code accepts an XSLT stylesheet from the user and processes it.

-

In the first example, the user provided XSLT stylesheet is parsed and processed.

+

In the first example, the user-provided XSLT stylesheet is parsed and processed.

In the second example, secure processing mode is enabled.

From ad08ccb50b40c6eef2f8b99d06ad317bd071fd03 Mon Sep 17 00:00:00 2001 From: Tony Torralba Date: Thu, 29 Jul 2021 16:57:04 +0200 Subject: [PATCH 09/10] Apply suggestion from code review --- java/ql/src/Security/CWE/CWE-074/XsltInjection.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/src/Security/CWE/CWE-074/XsltInjection.ql b/java/ql/src/Security/CWE/CWE-074/XsltInjection.ql index 3728ac9f3eeb..aeec7441cb4c 100644 --- a/java/ql/src/Security/CWE/CWE-074/XsltInjection.ql +++ b/java/ql/src/Security/CWE/CWE-074/XsltInjection.ql @@ -1,6 +1,6 @@ /** * @name XSLT transformation with user-controlled stylesheet - * @description Doing an XSLT transformation with user-controlled stylesheet can lead to + * @description Performing an XSLT transformation with user-controlled stylesheets can lead to * information disclosure or execution of arbitrary code. * @kind path-problem * @problem.severity error From 78c12dc505b6732498a7c4cca7d6af141ccd9773 Mon Sep 17 00:00:00 2001 From: Tony Torralba Date: Mon, 27 Sep 2021 12:04:14 +0200 Subject: [PATCH 10/10] Move to lib --- java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll | 2 +- .../ql/{src => lib}/semmle/code/java/security/XsltInjection.qll | 0 .../semmle/code/java/security/XsltInjectionQuery.qll | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename java/ql/{src => lib}/semmle/code/java/security/XsltInjection.qll (100%) rename java/ql/{src => lib}/semmle/code/java/security/XsltInjectionQuery.qll (100%) diff --git a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll index f88cdcb15935..7f46f2d79b60 100644 --- a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll +++ b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll @@ -110,7 +110,7 @@ private module Frameworks { private import semmle.code.java.security.MvelInjection private import semmle.code.java.security.OgnlInjection private import semmle.code.java.security.XPath - private import semmle.code.java.security.XsltInjectionSinkModels + private import semmle.code.java.security.XsltInjection private import semmle.code.java.frameworks.android.Android private import semmle.code.java.frameworks.android.SQLite private import semmle.code.java.frameworks.Jdbc diff --git a/java/ql/src/semmle/code/java/security/XsltInjection.qll b/java/ql/lib/semmle/code/java/security/XsltInjection.qll similarity index 100% rename from java/ql/src/semmle/code/java/security/XsltInjection.qll rename to java/ql/lib/semmle/code/java/security/XsltInjection.qll diff --git a/java/ql/src/semmle/code/java/security/XsltInjectionQuery.qll b/java/ql/lib/semmle/code/java/security/XsltInjectionQuery.qll similarity index 100% rename from java/ql/src/semmle/code/java/security/XsltInjectionQuery.qll rename to java/ql/lib/semmle/code/java/security/XsltInjectionQuery.qll