diff --git a/java/ql/src/experimental/Security/CWE/CWE-074/XsltInjection.java b/java/ql/src/experimental/Security/CWE/CWE-074/XsltInjection.java new file mode 100644 index 000000000000..9c37908aeea5 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-074/XsltInjection.java @@ -0,0 +1,18 @@ +import javax.xml.XMLConstants; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +public void transform(Socket socket, String inputXml) throws Exception { + StreamSource xslt = new StreamSource(socket.getInputStream()); + StreamSource xml = new StreamSource(new StringReader(inputXml)); + StringWriter result = new StringWriter(); + TransformerFactory factory = TransformerFactory.newInstance(); + + // BAD: User provided XSLT stylesheet is processed + factory.newTransformer(xslt).transform(xml, new StreamResult(result)); + + // GOOD: The secure processing mode is enabled + factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + factory.newTransformer(xslt).transform(xml, new StreamResult(result)); +} \ No newline at end of file diff --git a/java/ql/src/experimental/Security/CWE/CWE-074/XsltInjection.qhelp b/java/ql/src/experimental/Security/CWE/CWE-074/XsltInjection.qhelp new file mode 100644 index 000000000000..20fa9dcaa0e5 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-074/XsltInjection.qhelp @@ -0,0 +1,32 @@ + + + +

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.

+
+ + +

The general recommendation is to not process untrusted XSLT stylesheets. If user provided +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 second example, secure processing mode is enabled.

+ + +
+ + +
  • Wikipedia: XSLT.
  • +
  • Java Tutorial: Transforming XML Data with XSLT.
  • +
  • XSLT Injection Basics.
  • +
    +
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-074/XsltInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-074/XsltInjection.ql new file mode 100644 index 000000000000..1403573e4b19 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-074/XsltInjection.ql @@ -0,0 +1,21 @@ +/** + * @name XSLT transformation with user-controlled stylesheet + * @description Doing an XSLT transformation with user-controlled stylesheet can lead to + * information disclosure or execution of arbitrary code. + * @kind path-problem + * @problem.severity error + * @precision high + * @id java/xslt-injection + * @tags security + * external/cwe/cwe-074 + */ + +import java +import semmle.code.java.dataflow.FlowSources +import XsltInjectionLib +import DataFlow::PathGraph + +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 $@.", + source.getNode(), "this user input" diff --git a/java/ql/src/experimental/Security/CWE/CWE-074/XsltInjectionLib.qll b/java/ql/src/experimental/Security/CWE/CWE-074/XsltInjectionLib.qll new file mode 100644 index 000000000000..4ba0eb6d0b11 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-074/XsltInjectionLib.qll @@ -0,0 +1,288 @@ +import java +import semmle.code.java.dataflow.FlowSources +import semmle.code.java.security.XmlParsers +import DataFlow + +/** + * 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) { + 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) + } +} + +/** 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) { + 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)`. + */ +predicate xmlEventReaderStep(ExprNode n1, ExprNode 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)`. + */ +predicate staxSourceStep(ExprNode n1, ExprNode 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)`. + */ +predicate documentBuilderStep(ExprNode n1, ExprNode 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)`. + */ +predicate domSourceStep(ExprNode n1, ExprNode n2) { + exists(ConstructorCall cc | cc.getConstructedType() instanceof TypeDOMSource | + n1.asExpr() = cc.getAnArgument() and + n2.asExpr() = cc + ) +} + +/** + * 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 `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) { + 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)`. + */ +predicate xsltCompilerStep(ExprNode n1, ExprNode 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") + ) + ) +} + +/** + * Holds if `n1` to `n2` is a dataflow step that converts between `XsltExecutable` and + * `XsltTransformer` or `Xslt30Transformer`, i.e. `XsltExecutable.load()` or + * `XsltExecutable.load30()`. + */ +predicate xsltExecutableStep(ExprNode n1, ExprNode 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")) + ) +} + +/** + * 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) { + 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") + ) +} diff --git a/java/ql/src/semmle/code/java/security/XmlParsers.qll b/java/ql/src/semmle/code/java/security/XmlParsers.qll index d04e228d2eb2..7701af08923b 100644 --- a/java/ql/src/semmle/code/java/security/XmlParsers.qll +++ b/java/ql/src/semmle/code/java/security/XmlParsers.qll @@ -1172,3 +1172,15 @@ class SimpleXMLFormatterCall extends XmlParserCall { override predicate isSafe() { none() } } + +/** A configuration for secure processing. */ +Expr configSecureProcessing() { + result.(ConstantStringExpr).getStringValue() = + "http://javax.xml.XMLConstants/feature/secure-processing" + or + exists(Field f | + result = f.getAnAccess() and + f.hasName("FEATURE_SECURE_PROCESSING") and + f.getDeclaringType() instanceof XmlConstants + ) +} diff --git a/java/ql/test/experimental/query-tests/security/CWE-074/XsltInjection.expected b/java/ql/test/experimental/query-tests/security/CWE-074/XsltInjection.expected new file mode 100644 index 000000000000..ece3ed7d17f5 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-074/XsltInjection.expected @@ -0,0 +1,85 @@ +edges +| XsltInjection.java:30:44:30:66 | getInputStream(...) : InputStream | XsltInjection.java:31:5:31:59 | newTransformer(...) | +| XsltInjection.java:35:66:35:88 | getInputStream(...) : InputStream | XsltInjection.java:36:5:36:74 | newTransformer(...) | +| XsltInjection.java:40:45:40:70 | param : String | XsltInjection.java:43:5:43:59 | newTransformer(...) | +| XsltInjection.java:47:54:47:76 | getInputStream(...) : InputStream | XsltInjection.java:48:5:48:74 | newTransformer(...) | +| XsltInjection.java:52:82:52:104 | getInputStream(...) : InputStream | XsltInjection.java:53:5:53:59 | newTransformer(...) | +| XsltInjection.java:57:91:57:113 | getInputStream(...) : InputStream | XsltInjection.java:58:5:58:59 | newTransformer(...) | +| XsltInjection.java:62:120:62:142 | getInputStream(...) : InputStream | XsltInjection.java:63:5:63:74 | newTransformer(...) | +| XsltInjection.java:67:102:67:124 | getInputStream(...) : InputStream | XsltInjection.java:68:5:68:59 | newTransformer(...) | +| XsltInjection.java:72:44:72:66 | getInputStream(...) : InputStream | XsltInjection.java:76:5:76:34 | newTransformer(...) | +| XsltInjection.java:80:44:80:66 | getInputStream(...) : InputStream | XsltInjection.java:83:5:83:34 | newTransformer(...) | +| XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:90:5:90:35 | load(...) | +| XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:91:5:91:37 | load30(...) | +| XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:92:5:92:37 | load30(...) | +| XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:93:5:93:37 | load30(...) | +| XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:94:5:94:37 | load30(...) | +| XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:95:5:95:37 | load30(...) | +| XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:96:5:96:37 | load30(...) | +| XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:97:5:97:37 | load30(...) | +| XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:98:5:98:37 | load30(...) | +| XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:99:5:99:37 | load30(...) | +| XsltInjection.java:103:36:103:61 | param : String | XsltInjection.java:108:5:108:46 | load(...) | +| XsltInjection.java:103:36:103:61 | param : String | XsltInjection.java:110:5:110:50 | load(...) | +| XsltInjection.java:105:44:105:66 | getInputStream(...) : InputStream | XsltInjection.java:109:5:109:49 | load(...) | +nodes +| 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: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:43:5:43:59 | newTransformer(...) | semmle.label | newTransformer(...) | +| 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: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: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:44:72:66 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream | +| XsltInjection.java:76:5:76:34 | newTransformer(...) | semmle.label | newTransformer(...) | +| 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: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: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(...) | +#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/experimental/query-tests/security/CWE-074/XsltInjection.java b/java/ql/test/experimental/query-tests/security/CWE-074/XsltInjection.java new file mode 100644 index 000000000000..d1a19217f02e --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-074/XsltInjection.java @@ -0,0 +1,127 @@ +import java.io.InputStreamReader; +import java.io.StringReader; +import java.io.StringWriter; +import java.net.Socket; +import java.net.URI; + +import javax.xml.XMLConstants; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.stream.XMLInputFactory; +import javax.xml.transform.Source; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.sax.SAXSource; +import javax.xml.transform.stax.StAXSource; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.xml.sax.InputSource; + +import net.sf.saxon.s9api.Processor; +import net.sf.saxon.s9api.XdmValue; +import net.sf.saxon.s9api.XsltCompiler; + +@Controller +public class XsltInjection { + public void testStreamSourceInputStream(Socket socket) throws Exception { + StreamSource source = new StreamSource(socket.getInputStream()); + TransformerFactory.newInstance().newTransformer(source).transform(null, null); + } + + public void testStreamSourceReader(Socket socket) throws Exception { + StreamSource source = new StreamSource(new InputStreamReader(socket.getInputStream())); + TransformerFactory.newInstance().newTemplates(source).newTransformer().transform(null, null); + } + + @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); + } + + public void testSAXSourceInputStream(Socket socket) throws Exception { + SAXSource source = new SAXSource(new InputSource(socket.getInputStream())); + TransformerFactory.newInstance().newTemplates(source).newTransformer().transform(null, null); + } + + 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); + } + + public void testStAXSourceEventReader(Socket socket) throws Exception { + StAXSource source = new StAXSource(XMLInputFactory.newInstance().createXMLEventReader(socket.getInputStream())); + TransformerFactory.newInstance().newTransformer(source).transform(null, null); + } + + 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); + } + + public void testDOMSource(Socket socket) throws Exception { + DOMSource source = new DOMSource(DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(socket.getInputStream())); + TransformerFactory.newInstance().newTransformer(source).transform(null, null); + } + + public void testDisabledXXE(Socket socket) throws Exception { + StreamSource source = new StreamSource(socket.getInputStream()); + TransformerFactory factory = TransformerFactory.newInstance(); + factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); + factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, ""); + factory.newTransformer(source).transform(null, null); + } + + 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); + } + + 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); + } + + @RequestMapping + public void testSaxonXsltPackage(@RequestParam String param, Socket socket) throws Exception { + 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(); + } + + 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); + } + + 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(); + } +} \ No newline at end of file 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 new file mode 100644 index 000000000000..eb4c280c5df4 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-074/XsltInjection.qlref @@ -0,0 +1 @@ +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 new file mode 100644 index 000000000000..9d29696af338 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-074/options @@ -0,0 +1 @@ +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.2.3:${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/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/Configuration.java new file mode 100644 index 000000000000..24417801bc9b --- /dev/null +++ b/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/Configuration.java @@ -0,0 +1,8 @@ +package net.sf.saxon; + +import net.sf.saxon.lib.*; +import net.sf.saxon.om.*; + +public class Configuration implements SourceResolver, NotationSet { + public interface ApiProvider {} +} \ No newline at end of file diff --git a/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/lib/SourceResolver.java b/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/lib/SourceResolver.java new file mode 100644 index 000000000000..3f59f59e3b35 --- /dev/null +++ b/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/lib/SourceResolver.java @@ -0,0 +1,3 @@ +package net.sf.saxon.lib; + +public interface SourceResolver { } diff --git a/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/om/NotationSet.java b/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/om/NotationSet.java new file mode 100644 index 000000000000..2c68a303b599 --- /dev/null +++ b/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/om/NotationSet.java @@ -0,0 +1,3 @@ +package net.sf.saxon.om; + +public interface NotationSet { } diff --git a/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/AbstractXsltTransformer.java b/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/AbstractXsltTransformer.java new file mode 100644 index 000000000000..34fd17a5dd25 --- /dev/null +++ b/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/AbstractXsltTransformer.java @@ -0,0 +1,3 @@ +package net.sf.saxon.s9api; + +abstract class AbstractXsltTransformer { } diff --git a/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/Destination.java b/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/Destination.java new file mode 100644 index 000000000000..ebd57a5dda89 --- /dev/null +++ b/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/Destination.java @@ -0,0 +1,3 @@ +package net.sf.saxon.s9api; + +public interface Destination { } diff --git a/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/Processor.java b/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/Processor.java new file mode 100644 index 000000000000..d9c8bd89c3a9 --- /dev/null +++ b/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/Processor.java @@ -0,0 +1,9 @@ +package net.sf.saxon.s9api; + +import net.sf.saxon.Configuration; + +public class Processor implements Configuration.ApiProvider { + public Processor(boolean licensedEdition) {} + + public XsltCompiler newXsltCompiler() { return null; } +} diff --git a/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/QName.java b/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/QName.java new file mode 100644 index 000000000000..d0195dc2c9e3 --- /dev/null +++ b/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/QName.java @@ -0,0 +1,3 @@ +package net.sf.saxon.s9api; + +public class QName { } diff --git a/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/SaxonApiException.java b/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/SaxonApiException.java new file mode 100644 index 000000000000..74e8b7a2dbec --- /dev/null +++ b/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/SaxonApiException.java @@ -0,0 +1,3 @@ +package net.sf.saxon.s9api; + +public class SaxonApiException extends Exception { } diff --git a/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/SaxonApiUncheckedException.java b/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/SaxonApiUncheckedException.java new file mode 100644 index 000000000000..062f5674b37a --- /dev/null +++ b/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/SaxonApiUncheckedException.java @@ -0,0 +1,3 @@ +package net.sf.saxon.s9api; + +public class SaxonApiUncheckedException extends RuntimeException {} diff --git a/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XdmItem.java b/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XdmItem.java new file mode 100644 index 000000000000..7eb1a50fa0cb --- /dev/null +++ b/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XdmItem.java @@ -0,0 +1,3 @@ +package net.sf.saxon.s9api; + +public abstract class XdmItem extends XdmValue { } diff --git a/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XdmValue.java b/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XdmValue.java new file mode 100644 index 000000000000..2fda5ef0feda --- /dev/null +++ b/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XdmValue.java @@ -0,0 +1,8 @@ +package net.sf.saxon.s9api; + +import java.lang.Iterable; +import java.util.Iterator; + +public class XdmValue implements Iterable { + public Iterator iterator() throws SaxonApiUncheckedException { return null; } + } diff --git a/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/Xslt30Transformer.java b/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/Xslt30Transformer.java new file mode 100644 index 000000000000..a9540067fa5d --- /dev/null +++ b/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/Xslt30Transformer.java @@ -0,0 +1,15 @@ +package net.sf.saxon.s9api; + +import javax.xml.transform.Source; + +public class Xslt30Transformer extends AbstractXsltTransformer { + public void transform(Source source, Destination destination) throws SaxonApiException {} + public void applyTemplates(Source source, Destination destination) throws SaxonApiException {} + public XdmValue applyTemplates(Source source) throws SaxonApiException { return null; } + public void applyTemplates(XdmValue selection, Destination destination) throws SaxonApiException {} + public XdmValue applyTemplates(XdmValue selection) throws SaxonApiException { return null; } + public XdmValue callFunction(QName function, XdmValue[] arguments) throws SaxonApiException { return null; } + public void callFunction(QName function, XdmValue[] arguments, Destination destination) throws SaxonApiException {} + public XdmValue callTemplate(QName templateName) throws SaxonApiException { return null; } + public void callTemplate(QName templateName, Destination destination) throws SaxonApiException {} +} diff --git a/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XsltCompiler.java b/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XsltCompiler.java new file mode 100644 index 000000000000..08b5d7d1dc80 --- /dev/null +++ b/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XsltCompiler.java @@ -0,0 +1,11 @@ +package net.sf.saxon.s9api; + +import javax.xml.transform.Source; +import java.net.URI; + +public class XsltCompiler { + public XsltExecutable compile(Source source) throws SaxonApiException { return null; } + public XsltExecutable loadExecutablePackage(URI location) throws SaxonApiException { return null; } + public XsltPackage compilePackage(Source source) throws SaxonApiException { return null; } + public XsltPackage loadLibraryPackage(URI location) throws SaxonApiException { return null; } +} diff --git a/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XsltExecutable.java b/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XsltExecutable.java new file mode 100644 index 000000000000..1bf23f8a3393 --- /dev/null +++ b/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XsltExecutable.java @@ -0,0 +1,6 @@ +package net.sf.saxon.s9api; + +public class XsltExecutable { + public XsltTransformer load() { return null; } + public Xslt30Transformer load30() { return null; } +} diff --git a/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XsltPackage.java b/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XsltPackage.java new file mode 100644 index 000000000000..d53eb62765b7 --- /dev/null +++ b/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XsltPackage.java @@ -0,0 +1,5 @@ +package net.sf.saxon.s9api; + +public class XsltPackage { + public XsltExecutable link() throws SaxonApiException { return null; } +} diff --git a/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XsltTransformer.java b/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XsltTransformer.java new file mode 100644 index 000000000000..7d5debfe5106 --- /dev/null +++ b/java/ql/test/experimental/stubs/Saxon-HE-9.9.1-7/net/sf/saxon/s9api/XsltTransformer.java @@ -0,0 +1,6 @@ +package net.sf.saxon.s9api; + +public class XsltTransformer extends AbstractXsltTransformer implements Destination { + public void transform() throws SaxonApiException {} + public void close() {} +}