Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d590f3f

Browse files
committed
CodeQL query to detect XSLT injections
1 parent cbe417f commit d590f3f

10 files changed

Lines changed: 397 additions & 0 deletions

File tree

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import javax.xml.XMLConstants;
2+
import javax.xml.transform.TransformerFactory;
3+
import javax.xml.transform.stream.StreamResult;
4+
import javax.xml.transform.stream.StreamSource;
5+
6+
public void transform(Socket socket, String inputXml) throws Exception {
7+
StreamSource xslt = new StreamSource(socket.getInputStream());
8+
StreamSource xml = new StreamSource(new StringReader(inputXml));
9+
StringWriter result = new StringWriter();
10+
TransformerFactory factory = TransformerFactory.newInstance();
11+
12+
// BAD: User provided XSLT stylesheet is processed
13+
factory.newTransformer(xslt).transform(xml, new StreamResult(result));
14+
15+
// GOOD: The secure processing mode is enabled
16+
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
17+
factory.newTransformer(xslt).transform(xml, new StreamResult(result));
18+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
<overview>
6+
<p>XSLT (Extensible Stylesheet Language Transformations) is a language for transforming XML
7+
documents into other XML documents or other formats. Processing of unvalidated XSLT stylesheet can
8+
let attacker to read arbitrary files from the filesystem or to execute arbitrary code.</p>
9+
</overview>
10+
11+
<recommendation>
12+
<p>The general recommendation is to not process untrusted XSLT stylesheets. If user provided
13+
stylesheets must be processed, enable the secure processing mode.</p>
14+
</recommendation>
15+
16+
<example>
17+
<p>In the following examples, the code accepts an XSLT stylesheet from the user and processes it.
18+
</p>
19+
20+
<p>In the first example, the user provided XSLT stylesheet is parsed and processed.</p>
21+
22+
<p>In the second example, secure processing mode is enabled.</p>
23+
24+
<sample src="XsltInjection.java" />
25+
</example>
26+
27+
<references>
28+
<li>Wikipedia: <a href="https://en.wikipedia.org/wiki/XSLT">XSLT</a>.</li>
29+
<li>Java Tutorial: <a href="https://docs.oracle.com/javase/tutorial/jaxp/xslt/transformingXML.html">Transforming XML Data with XSLT</a>.</li>
30+
<li><a href="https://blog.hunniccyber.com/ektron-cms-remote-code-execution-xslt-transform-injection-java/">XSLT Injection Basics</a>.</li>
31+
</references>
32+
</qhelp>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* @name XSLT transformation with user-controlled stylesheet
3+
* @description Doing an XSLT transformation with user-controlled stylesheet can lead to
4+
* information disclosure or execution of arbitrary code.
5+
* @kind path-problem
6+
* @problem.severity error
7+
* @precision high
8+
* @id java/xslt-injection
9+
* @tags security
10+
* external/cwe/cwe-074
11+
*/
12+
13+
import java
14+
import semmle.code.java.dataflow.FlowSources
15+
import XsltInjectionLib
16+
import DataFlow::PathGraph
17+
18+
from DataFlow::PathNode source, DataFlow::PathNode sink, XsltInjectionFlowConfig conf
19+
where conf.hasFlowPath(source, sink)
20+
select sink.getNode(), source, sink, "XSLT transformation might include stylesheet from $@.",
21+
source.getNode(), "this user input"
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
import java
2+
import semmle.code.java.dataflow.FlowSources
3+
import semmle.code.java.security.XmlParsers
4+
import DataFlow
5+
6+
/**
7+
* A taint-tracking configuration for unvalidated user input that is used in XSLT transformation.
8+
*/
9+
class XsltInjectionFlowConfig extends TaintTracking::Configuration {
10+
XsltInjectionFlowConfig() { this = "XsltInjectionFlowConfig" }
11+
12+
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
13+
14+
override predicate isSink(DataFlow::Node sink) { sink instanceof XsltInjectionSink }
15+
16+
override predicate isSanitizer(DataFlow::Node node) {
17+
node.getType() instanceof PrimitiveType or node.getType() instanceof BoxedType
18+
}
19+
20+
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
21+
xmlStreamReaderStep(node1, node2) or
22+
xmlEventReaderStep(node1, node2) or
23+
staxSourceStep(node1, node2) or
24+
documentBuilderStep(node1, node2) or
25+
domSourceStep(node1, node2) or
26+
newTransformerOrTemplatesStep(node1, node2) or
27+
newTransformerFromTemplatesStep(node1, node2)
28+
}
29+
}
30+
31+
/** The class `javax.xml.transform.stax.StAXSource`. */
32+
class TypeStAXSource extends Class {
33+
TypeStAXSource() { this.hasQualifiedName("javax.xml.transform.stax", "StAXSource") }
34+
}
35+
36+
/** The class `javax.xml.transform.dom.DOMSource`. */
37+
class TypeDOMSource extends Class {
38+
TypeDOMSource() { this.hasQualifiedName("javax.xml.transform.dom", "DOMSource") }
39+
}
40+
41+
/** The interface `javax.xml.transform.Templates`. */
42+
class TypeTemplates extends Interface {
43+
TypeTemplates() { this.hasQualifiedName("javax.xml.transform", "Templates") }
44+
}
45+
46+
/** A data flow sink for unvalidated user input that is used in XSLT transformation. */
47+
class XsltInjectionSink extends DataFlow::ExprNode {
48+
XsltInjectionSink() {
49+
exists(MethodAccess ma |
50+
ma.getQualifier() = this.getExpr() and
51+
ma instanceof TransformerTransform
52+
)
53+
}
54+
}
55+
56+
/**
57+
* Holds if `n1` to `n2` is a dataflow step that converts between `InputStream` or `Reader` and
58+
* `XMLStreamReader`, i.e. `XMLInputFactory.createXMLStreamReader(tainted)`.
59+
*/
60+
predicate xmlStreamReaderStep(ExprNode n1, ExprNode n2) {
61+
exists(XmlInputFactoryStreamReader xmlStreamReader |
62+
n1.asExpr() = xmlStreamReader.getSink() and
63+
n2.asExpr() = xmlStreamReader
64+
)
65+
}
66+
67+
/**
68+
* Holds if `n1` to `n2` is a dataflow step that converts between `InputStream` or `Reader` and
69+
* `XMLEventReader`, i.e. `XMLInputFactory.createXMLEventReader(tainted)`.
70+
*/
71+
predicate xmlEventReaderStep(ExprNode n1, ExprNode n2) {
72+
exists(XmlInputFactoryEventReader xmlEventReader |
73+
n1.asExpr() = xmlEventReader.getSink() and
74+
n2.asExpr() = xmlEventReader
75+
)
76+
}
77+
78+
/**
79+
* Holds if `n1` to `n2` is a dataflow step that converts between `XMLStreamReader` or
80+
* `XMLEventReader` and `StAXSource`, i.e. `new StAXSource(tainted)`.
81+
*/
82+
predicate staxSourceStep(ExprNode n1, ExprNode n2) {
83+
exists(ConstructorCall cc | cc.getConstructedType() instanceof TypeStAXSource |
84+
n1.asExpr() = cc.getAnArgument() and
85+
n2.asExpr() = cc
86+
)
87+
}
88+
89+
/**
90+
* Holds if `n1` to `n2` is a dataflow step that converts between `InputStream` and `Document`,
91+
* i.e. `DocumentBuilder.parse(tainted)`.
92+
*/
93+
predicate documentBuilderStep(ExprNode n1, ExprNode n2) {
94+
exists(DocumentBuilderParse documentBuilder |
95+
n1.asExpr() = documentBuilder.getSink() and
96+
n2.asExpr() = documentBuilder
97+
)
98+
}
99+
100+
/**
101+
* Holds if `n1` to `n2` is a dataflow step that converts between `Document` and `DOMSource`, i.e.
102+
* `new DOMSource(tainted)`.
103+
*/
104+
predicate domSourceStep(ExprNode n1, ExprNode n2) {
105+
exists(ConstructorCall cc | cc.getConstructedType() instanceof TypeDOMSource |
106+
n1.asExpr() = cc.getAnArgument() and
107+
n2.asExpr() = cc
108+
)
109+
}
110+
111+
/**
112+
* A data flow configuration for secure processing feature that is enabled on `TransformerFactory`.
113+
*/
114+
private class TransformerFactoryWithSecureProcessingFeatureFlowConfig extends DataFlow2::Configuration {
115+
TransformerFactoryWithSecureProcessingFeatureFlowConfig() {
116+
this = "TransformerFactoryWithSecureProcessingFeatureFlowConfig"
117+
}
118+
119+
override predicate isSource(DataFlow::Node src) {
120+
exists(Variable v | v = src.asExpr().(VarAccess).getVariable() |
121+
exists(TransformerFactoryFeatureConfig config | config.getQualifier() = v.getAnAccess() |
122+
config.enables(configSecureProcessing())
123+
)
124+
)
125+
}
126+
127+
override predicate isSink(DataFlow::Node sink) {
128+
exists(MethodAccess ma |
129+
sink.asExpr() = ma.getQualifier() and
130+
ma.getMethod().getDeclaringType() instanceof TransformerFactory
131+
)
132+
}
133+
134+
override int fieldFlowBranchLimit() { result = 0 }
135+
}
136+
137+
/** A `ParserConfig` specific to `TransformerFactory`. */
138+
private class TransformerFactoryFeatureConfig extends ParserConfig {
139+
TransformerFactoryFeatureConfig() {
140+
exists(Method m |
141+
m = this.getMethod() and
142+
m.getDeclaringType() instanceof TransformerFactory and
143+
m.hasName("setFeature")
144+
)
145+
}
146+
}
147+
148+
/**
149+
* Holds if `n1` to `n2` is a dataflow step that converts between `Source` and `Transformer` or
150+
* `Templates`, i.e. `TransformerFactory.newTransformer(tainted)` or
151+
* `TransformerFactory.newTemplates(tainted)`.
152+
*/
153+
predicate newTransformerOrTemplatesStep(ExprNode n1, ExprNode n2) {
154+
exists(MethodAccess ma, Method m | ma.getMethod() = m |
155+
n1.asExpr() = ma.getAnArgument() and
156+
n2.asExpr() = ma and
157+
(
158+
m.getDeclaringType() instanceof TransformerFactory and m.hasName("newTransformer")
159+
or
160+
m.getDeclaringType() instanceof TransformerFactory and m.hasName("newTemplates")
161+
) and
162+
not exists(TransformerFactoryWithSecureProcessingFeatureFlowConfig conf |
163+
conf.hasFlowToExpr(ma.getQualifier())
164+
)
165+
)
166+
}
167+
168+
/**
169+
* Holds if `n1` to `n2` is a dataflow step that converts between `Templates` and `Transformer`,
170+
* i.e. `tainted.newTransformer()`.
171+
*/
172+
predicate newTransformerFromTemplatesStep(ExprNode n1, ExprNode n2) {
173+
exists(MethodAccess ma, Method m | ma.getMethod() = m |
174+
n1.asExpr() = ma.getQualifier() and
175+
n2.asExpr() = ma and
176+
m.getDeclaringType() instanceof TypeTemplates and
177+
m.hasName("newTransformer")
178+
)
179+
}

java/ql/src/semmle/code/java/security/XmlParsers.qll

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1172,3 +1172,15 @@ class SimpleXMLFormatterCall extends XmlParserCall {
11721172

11731173
override predicate isSafe() { none() }
11741174
}
1175+
1176+
/** A configuration for secure processing. */
1177+
Expr configSecureProcessing() {
1178+
result.(ConstantStringExpr).getStringValue() =
1179+
"http://javax.xml.XMLConstants/feature/secure-processing"
1180+
or
1181+
exists(Field f |
1182+
result = f.getAnAccess() and
1183+
f.hasName("FEATURE_SECURE_PROCESSING") and
1184+
f.getDeclaringType() instanceof XmlConstants
1185+
)
1186+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
edges
2+
| XsltInjection.java:21:44:21:66 | getInputStream(...) : InputStream | XsltInjection.java:22:5:22:59 | newTransformer(...) |
3+
| XsltInjection.java:26:66:26:88 | getInputStream(...) : InputStream | XsltInjection.java:27:5:27:74 | newTransformer(...) |
4+
| XsltInjection.java:30:45:30:70 | param : String | XsltInjection.java:33:5:33:59 | newTransformer(...) |
5+
| XsltInjection.java:37:54:37:76 | getInputStream(...) : InputStream | XsltInjection.java:38:5:38:74 | newTransformer(...) |
6+
| XsltInjection.java:42:82:42:104 | getInputStream(...) : InputStream | XsltInjection.java:43:5:43:59 | newTransformer(...) |
7+
| XsltInjection.java:47:91:47:113 | getInputStream(...) : InputStream | XsltInjection.java:48:5:48:59 | newTransformer(...) |
8+
| XsltInjection.java:52:120:52:142 | getInputStream(...) : InputStream | XsltInjection.java:53:5:53:74 | newTransformer(...) |
9+
| XsltInjection.java:57:102:57:124 | getInputStream(...) : InputStream | XsltInjection.java:58:5:58:59 | newTransformer(...) |
10+
| XsltInjection.java:62:44:62:66 | getInputStream(...) : InputStream | XsltInjection.java:66:5:66:34 | newTransformer(...) |
11+
| XsltInjection.java:70:44:70:66 | getInputStream(...) : InputStream | XsltInjection.java:73:5:73:34 | newTransformer(...) |
12+
nodes
13+
| XsltInjection.java:21:44:21:66 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
14+
| XsltInjection.java:22:5:22:59 | newTransformer(...) | semmle.label | newTransformer(...) |
15+
| XsltInjection.java:26:66:26:88 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
16+
| XsltInjection.java:27:5:27:74 | newTransformer(...) | semmle.label | newTransformer(...) |
17+
| XsltInjection.java:30:45:30:70 | param : String | semmle.label | param : String |
18+
| XsltInjection.java:33:5:33:59 | newTransformer(...) | semmle.label | newTransformer(...) |
19+
| XsltInjection.java:37:54:37:76 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
20+
| XsltInjection.java:38:5:38:74 | newTransformer(...) | semmle.label | newTransformer(...) |
21+
| XsltInjection.java:42:82:42:104 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
22+
| XsltInjection.java:43:5:43:59 | newTransformer(...) | semmle.label | newTransformer(...) |
23+
| XsltInjection.java:47:91:47:113 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
24+
| XsltInjection.java:48:5:48:59 | newTransformer(...) | semmle.label | newTransformer(...) |
25+
| XsltInjection.java:52:120:52:142 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
26+
| XsltInjection.java:53:5:53:74 | newTransformer(...) | semmle.label | newTransformer(...) |
27+
| XsltInjection.java:57:102:57:124 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
28+
| XsltInjection.java:58:5:58:59 | newTransformer(...) | semmle.label | newTransformer(...) |
29+
| XsltInjection.java:62:44:62:66 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
30+
| XsltInjection.java:66:5:66:34 | newTransformer(...) | semmle.label | newTransformer(...) |
31+
| XsltInjection.java:70:44:70:66 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
32+
| XsltInjection.java:73:5:73:34 | newTransformer(...) | semmle.label | newTransformer(...) |
33+
#select
34+
| XsltInjection.java:22:5:22:59 | newTransformer(...) | XsltInjection.java:21:44:21:66 | getInputStream(...) : InputStream | XsltInjection.java:22:5:22:59 | newTransformer(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:21:44:21:66 | getInputStream(...) | this user input |
35+
| XsltInjection.java:27:5:27:74 | newTransformer(...) | XsltInjection.java:26:66:26:88 | getInputStream(...) : InputStream | XsltInjection.java:27:5:27:74 | newTransformer(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:26:66:26:88 | getInputStream(...) | this user input |
36+
| XsltInjection.java:33:5:33:59 | newTransformer(...) | XsltInjection.java:30:45:30:70 | param : String | XsltInjection.java:33:5:33:59 | newTransformer(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:30:45:30:70 | param | this user input |
37+
| XsltInjection.java:38:5:38:74 | newTransformer(...) | XsltInjection.java:37:54:37:76 | getInputStream(...) : InputStream | XsltInjection.java:38:5:38:74 | newTransformer(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:37:54:37:76 | getInputStream(...) | this user input |
38+
| XsltInjection.java:43:5:43:59 | newTransformer(...) | XsltInjection.java:42:82:42:104 | getInputStream(...) : InputStream | XsltInjection.java:43:5:43:59 | newTransformer(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:42:82:42:104 | getInputStream(...) | this user input |
39+
| XsltInjection.java:48:5:48:59 | newTransformer(...) | XsltInjection.java:47:91:47:113 | getInputStream(...) : InputStream | XsltInjection.java:48:5:48:59 | newTransformer(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:47:91:47:113 | getInputStream(...) | this user input |
40+
| XsltInjection.java:53:5:53:74 | newTransformer(...) | XsltInjection.java:52:120:52:142 | getInputStream(...) : InputStream | XsltInjection.java:53:5:53:74 | newTransformer(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:52:120:52:142 | getInputStream(...) | this user input |
41+
| XsltInjection.java:58:5:58:59 | newTransformer(...) | XsltInjection.java:57:102:57:124 | getInputStream(...) : InputStream | XsltInjection.java:58:5:58:59 | newTransformer(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:57:102:57:124 | getInputStream(...) | this user input |
42+
| XsltInjection.java:66:5:66:34 | newTransformer(...) | XsltInjection.java:62:44:62:66 | getInputStream(...) : InputStream | XsltInjection.java:66:5:66:34 | newTransformer(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:62:44:62:66 | getInputStream(...) | this user input |
43+
| XsltInjection.java:73:5:73:34 | newTransformer(...) | XsltInjection.java:70:44:70:66 | getInputStream(...) : InputStream | XsltInjection.java:73:5:73:34 | newTransformer(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:70:44:70:66 | getInputStream(...) | this user input |
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import java.io.InputStreamReader;
2+
import java.io.StringReader;
3+
import java.io.StringWriter;
4+
import java.net.Socket;
5+
6+
import javax.xml.XMLConstants;
7+
import javax.xml.parsers.DocumentBuilderFactory;
8+
import javax.xml.stream.XMLInputFactory;
9+
import javax.xml.transform.TransformerFactory;
10+
import javax.xml.transform.dom.DOMSource;
11+
import javax.xml.transform.sax.SAXSource;
12+
import javax.xml.transform.stax.StAXSource;
13+
import javax.xml.transform.stream.StreamResult;
14+
import javax.xml.transform.stream.StreamSource;
15+
16+
import org.springframework.web.bind.annotation.RequestParam;
17+
import org.xml.sax.InputSource;
18+
19+
public class XsltInjection {
20+
public void testStreamSourceInputStream(Socket socket) throws Exception {
21+
StreamSource source = new StreamSource(socket.getInputStream());
22+
TransformerFactory.newInstance().newTransformer(source).transform(null, null);
23+
}
24+
25+
public void testStreamSourceReader(Socket socket) throws Exception {
26+
StreamSource source = new StreamSource(new InputStreamReader(socket.getInputStream()));
27+
TransformerFactory.newInstance().newTemplates(source).newTransformer().transform(null, null);
28+
}
29+
30+
public void testStreamSourceInjectedParam(@RequestParam String param) throws Exception {
31+
String xslt = "<xsl:stylesheet [...]" + param + "</xsl:stylesheet>";
32+
StreamSource source = new StreamSource(new StringReader(xslt));
33+
TransformerFactory.newInstance().newTransformer(source).transform(null, null);
34+
}
35+
36+
public void testSAXSourceInputStream(Socket socket) throws Exception {
37+
SAXSource source = new SAXSource(new InputSource(socket.getInputStream()));
38+
TransformerFactory.newInstance().newTemplates(source).newTransformer().transform(null, null);
39+
}
40+
41+
public void testSAXSourceReader(Socket socket) throws Exception {
42+
SAXSource source = new SAXSource(null, new InputSource(new InputStreamReader(socket.getInputStream())));
43+
TransformerFactory.newInstance().newTransformer(source).transform(null, null);
44+
}
45+
46+
public void testStAXSourceEventReader(Socket socket) throws Exception {
47+
StAXSource source = new StAXSource(XMLInputFactory.newInstance().createXMLEventReader(socket.getInputStream()));
48+
TransformerFactory.newInstance().newTransformer(source).transform(null, null);
49+
}
50+
51+
public void testStAXSourceEventStream(Socket socket) throws Exception {
52+
StAXSource source = new StAXSource(XMLInputFactory.newInstance().createXMLStreamReader(null, new InputStreamReader(socket.getInputStream())));
53+
TransformerFactory.newInstance().newTemplates(source).newTransformer().transform(null, null);
54+
}
55+
56+
public void testDOMSource(Socket socket) throws Exception {
57+
DOMSource source = new DOMSource(DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(socket.getInputStream()));
58+
TransformerFactory.newInstance().newTransformer(source).transform(null, null);
59+
}
60+
61+
public void testDisabledXXE(Socket socket) throws Exception {
62+
StreamSource source = new StreamSource(socket.getInputStream());
63+
TransformerFactory factory = TransformerFactory.newInstance();
64+
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
65+
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
66+
factory.newTransformer(source).transform(null, null);
67+
}
68+
69+
public void testFeatureSecureProcessingDisabled(Socket socket) throws Exception {
70+
StreamSource source = new StreamSource(socket.getInputStream());
71+
TransformerFactory factory = TransformerFactory.newInstance();
72+
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, false);
73+
factory.newTransformer(source).transform(null, null);
74+
}
75+
76+
public void testOkFeatureSecureProcessing(Socket socket) throws Exception {
77+
StreamSource source = new StreamSource(socket.getInputStream());
78+
TransformerFactory factory = TransformerFactory.newInstance();
79+
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
80+
factory.newTransformer(source).transform(null, null);
81+
}
82+
}

0 commit comments

Comments
 (0)