Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/** Provides definitions related to XML parsing in the Woodstox StAX library. */
overlay[local?]
module;

import java
private import semmle.code.java.security.XmlParsers

/**
* The class `com.ctc.wstx.stax.WstxInputFactory` or its abstract supertype
* `org.codehaus.stax2.XMLInputFactory2`.
*/
private class WstxInputFactory extends RefType {
WstxInputFactory() {
this.hasQualifiedName("com.ctc.wstx.stax", "WstxInputFactory") or
this.hasQualifiedName("org.codehaus.stax2", "XMLInputFactory2")
}
}

/** A call to `WstxInputFactory.createXMLStreamReader`. */
private class WstxInputFactoryStreamReader extends XmlParserCall {
WstxInputFactoryStreamReader() {
exists(Method m |
this.getMethod() = m and
m.getDeclaringType() instanceof WstxInputFactory and
m.hasName("createXMLStreamReader")
)
}

override Expr getSink() {
if this.getMethod().getParameterType(0) instanceof TypeString
then result = this.getArgument(1)
else result = this.getArgument(0)
}

override predicate isSafe() {
SafeWstxInputFactoryFlow::flowsTo(DataFlow::exprNode(this.getQualifier()))
}
}

/** A call to `WstxInputFactory.createXMLEventReader`. */
private class WstxInputFactoryEventReader extends XmlParserCall {
WstxInputFactoryEventReader() {
exists(Method m |
this.getMethod() = m and
m.getDeclaringType() instanceof WstxInputFactory and
m.hasName("createXMLEventReader")
)
}

override Expr getSink() {
if this.getMethod().getParameterType(0) instanceof TypeString
then result = this.getArgument(1)
else result = this.getArgument(0)
}

override predicate isSafe() {
SafeWstxInputFactoryFlow::flowsTo(DataFlow::exprNode(this.getQualifier()))
}
}

/** A `ParserConfig` specific to `WstxInputFactory`. */
private class WstxInputFactoryConfig extends ParserConfig {
WstxInputFactoryConfig() {
exists(Method m |
m = this.getMethod() and
m.getDeclaringType() instanceof WstxInputFactory and
m.hasName("setProperty")
)
}
}

/**
* A safely configured `WstxInputFactory`.
*/
private class SafeWstxInputFactory extends VarAccess {
SafeWstxInputFactory() {
exists(Variable v |
v = this.getVariable() and
exists(WstxInputFactoryConfig config | config.getQualifier() = v.getAnAccess() |
config.disables(configOptionIsSupportingExternalEntities())
) and
exists(WstxInputFactoryConfig config | config.getQualifier() = v.getAnAccess() |
config.disables(configOptionSupportDtd())
)
)
}
}

private predicate safeWstxInputFactoryNode(DataFlow::Node src) {
src.asExpr() instanceof SafeWstxInputFactory
}

private module SafeWstxInputFactoryFlow = DataFlow::SimpleGlobal<safeWstxInputFactoryNode/1>;
1 change: 1 addition & 0 deletions java/ql/lib/semmle/code/java/security/XmlParsers.qll
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ private module Frameworks {
private import semmle.code.java.frameworks.javase.Beans
private import semmle.code.java.frameworks.mdht.MdhtXml
private import semmle.code.java.frameworks.rundeck.RundeckXml
private import semmle.code.java.frameworks.woodstox.WoodstoxXml
}

/**
Expand Down
4 changes: 4 additions & 0 deletions java/ql/src/change-notes/2026-04-16-woodstox-xxe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The queries "Resolving XML external entity in user-controlled data" (`java/xxe`) and "Resolving XML external entity in user-controlled data from local source" (`java/xxe-local`) now recognize sinks in the Woodstox StAX library when `com.ctc.wstx.stax.WstxInputFactory` or `org.codehaus.stax2.XMLInputFactory2` are used directly.
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import java.net.Socket;

import javax.xml.stream.XMLInputFactory;

import com.ctc.wstx.stax.WstxInputFactory;

public class WstxInputFactoryTests {

public void unconfiguredFactory(Socket sock) throws Exception {
WstxInputFactory factory = new WstxInputFactory();
factory.createXMLStreamReader(sock.getInputStream()); // $ Alert
factory.createXMLEventReader(sock.getInputStream()); // $ Alert
}

public void safeFactory(Socket sock) throws Exception {
WstxInputFactory factory = new WstxInputFactory();
factory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
factory.createXMLStreamReader(sock.getInputStream()); // safe
factory.createXMLEventReader(sock.getInputStream()); // safe
}

public void safeFactoryStringProperties(Socket sock) throws Exception {
WstxInputFactory factory = new WstxInputFactory();
factory.setProperty("javax.xml.stream.supportDTD", false);
factory.setProperty("javax.xml.stream.isSupportingExternalEntities", false);
factory.createXMLStreamReader(sock.getInputStream()); // safe
factory.createXMLEventReader(sock.getInputStream()); // safe
}

public void misConfiguredFactory(Socket sock) throws Exception {
WstxInputFactory factory = new WstxInputFactory();
factory.setProperty("javax.xml.stream.isSupportingExternalEntities", false);
factory.createXMLStreamReader(sock.getInputStream()); // $ Alert
factory.createXMLEventReader(sock.getInputStream()); // $ Alert
}

public void misConfiguredFactory2(Socket sock) throws Exception {
WstxInputFactory factory = new WstxInputFactory();
factory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
factory.createXMLStreamReader(sock.getInputStream()); // $ Alert
factory.createXMLEventReader(sock.getInputStream()); // $ Alert
}
}
12 changes: 12 additions & 0 deletions java/ql/test/query-tests/security/CWE-611/XXE.expected
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@
| TransformerTests.java:141:21:141:73 | new SAXSource(...) | TransformerTests.java:141:51:141:71 | getInputStream(...) : InputStream | TransformerTests.java:141:21:141:73 | new SAXSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | TransformerTests.java:141:51:141:71 | getInputStream(...) | user-provided value |
| UnmarshallerTests.java:29:18:29:38 | getInputStream(...) | UnmarshallerTests.java:29:18:29:38 | getInputStream(...) | UnmarshallerTests.java:29:18:29:38 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | UnmarshallerTests.java:29:18:29:38 | getInputStream(...) | user-provided value |
| ValidatorTests.java:22:28:22:33 | source | ValidatorTests.java:17:49:17:72 | getInputStream(...) : ServletInputStream | ValidatorTests.java:22:28:22:33 | source | XML parsing depends on a $@ without guarding against external entity expansion. | ValidatorTests.java:17:49:17:72 | getInputStream(...) | user-provided value |
| WstxInputFactoryTests.java:11:35:11:55 | getInputStream(...) | WstxInputFactoryTests.java:11:35:11:55 | getInputStream(...) | WstxInputFactoryTests.java:11:35:11:55 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | WstxInputFactoryTests.java:11:35:11:55 | getInputStream(...) | user-provided value |
| WstxInputFactoryTests.java:12:34:12:54 | getInputStream(...) | WstxInputFactoryTests.java:12:34:12:54 | getInputStream(...) | WstxInputFactoryTests.java:12:34:12:54 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | WstxInputFactoryTests.java:12:34:12:54 | getInputStream(...) | user-provided value |
| WstxInputFactoryTests.java:34:35:34:55 | getInputStream(...) | WstxInputFactoryTests.java:34:35:34:55 | getInputStream(...) | WstxInputFactoryTests.java:34:35:34:55 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | WstxInputFactoryTests.java:34:35:34:55 | getInputStream(...) | user-provided value |
| WstxInputFactoryTests.java:35:34:35:54 | getInputStream(...) | WstxInputFactoryTests.java:35:34:35:54 | getInputStream(...) | WstxInputFactoryTests.java:35:34:35:54 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | WstxInputFactoryTests.java:35:34:35:54 | getInputStream(...) | user-provided value |
| WstxInputFactoryTests.java:41:35:41:55 | getInputStream(...) | WstxInputFactoryTests.java:41:35:41:55 | getInputStream(...) | WstxInputFactoryTests.java:41:35:41:55 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | WstxInputFactoryTests.java:41:35:41:55 | getInputStream(...) | user-provided value |
| WstxInputFactoryTests.java:42:34:42:54 | getInputStream(...) | WstxInputFactoryTests.java:42:34:42:54 | getInputStream(...) | WstxInputFactoryTests.java:42:34:42:54 | getInputStream(...) | XML parsing depends on a $@ without guarding against external entity expansion. | WstxInputFactoryTests.java:42:34:42:54 | getInputStream(...) | user-provided value |
| XMLDecoderTests.java:18:9:18:18 | xmlDecoder | XMLDecoderTests.java:16:49:16:72 | getInputStream(...) : ServletInputStream | XMLDecoderTests.java:18:9:18:18 | xmlDecoder | XML parsing depends on a $@ without guarding against external entity expansion. | XMLDecoderTests.java:16:49:16:72 | getInputStream(...) | user-provided value |
| XMLReaderTests.java:16:18:16:55 | new InputSource(...) | XMLReaderTests.java:16:34:16:54 | getInputStream(...) : InputStream | XMLReaderTests.java:16:18:16:55 | new InputSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | XMLReaderTests.java:16:34:16:54 | getInputStream(...) | user-provided value |
| XMLReaderTests.java:56:18:56:55 | new InputSource(...) | XMLReaderTests.java:56:34:56:54 | getInputStream(...) : InputStream | XMLReaderTests.java:56:18:56:55 | new InputSource(...) | XML parsing depends on a $@ without guarding against external entity expansion. | XMLReaderTests.java:56:34:56:54 | getInputStream(...) | user-provided value |
Expand Down Expand Up @@ -390,6 +396,12 @@ nodes
| ValidatorTests.java:21:31:21:66 | new StreamSource(...) : StreamSource | semmle.label | new StreamSource(...) : StreamSource |
| ValidatorTests.java:21:48:21:65 | servletInputStream : ServletInputStream | semmle.label | servletInputStream : ServletInputStream |
| ValidatorTests.java:22:28:22:33 | source | semmle.label | source |
| WstxInputFactoryTests.java:11:35:11:55 | getInputStream(...) | semmle.label | getInputStream(...) |
| WstxInputFactoryTests.java:12:34:12:54 | getInputStream(...) | semmle.label | getInputStream(...) |
| WstxInputFactoryTests.java:34:35:34:55 | getInputStream(...) | semmle.label | getInputStream(...) |
| WstxInputFactoryTests.java:35:34:35:54 | getInputStream(...) | semmle.label | getInputStream(...) |
| WstxInputFactoryTests.java:41:35:41:55 | getInputStream(...) | semmle.label | getInputStream(...) |
| WstxInputFactoryTests.java:42:34:42:54 | getInputStream(...) | semmle.label | getInputStream(...) |
| XMLDecoderTests.java:16:49:16:72 | getInputStream(...) : ServletInputStream | semmle.label | getInputStream(...) : ServletInputStream |
| XMLDecoderTests.java:17:33:17:66 | new XMLDecoder(...) : XMLDecoder | semmle.label | new XMLDecoder(...) : XMLDecoder |
| XMLDecoderTests.java:17:48:17:65 | servletInputStream : ServletInputStream | semmle.label | servletInputStream : ServletInputStream |
Expand Down
2 changes: 1 addition & 1 deletion java/ql/test/query-tests/security/CWE-611/options
Original file line number Diff line number Diff line change
@@ -1 +1 @@
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/jdom-1.1.3:${testdir}/../../../stubs/dom4j-2.1.1:${testdir}/../../../stubs/simple-xml-2.7.1:${testdir}/../../../stubs/jaxb-api-2.3.1:${testdir}/../../../stubs/jaxen-1.2.0:${testdir}/../../../stubs/apache-commons-digester3-3.2:${testdir}/../../../stubs/servlet-api-2.4/:${testdir}/../../../stubs/rundeck-api-java-client-13.2:${testdir}/../../../stubs/springframework-5.8.x/:${testdir}/../../../stubs/mdht-1.2.0/
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/jdom-1.1.3:${testdir}/../../../stubs/dom4j-2.1.1:${testdir}/../../../stubs/simple-xml-2.7.1:${testdir}/../../../stubs/jaxb-api-2.3.1:${testdir}/../../../stubs/jaxen-1.2.0:${testdir}/../../../stubs/apache-commons-digester3-3.2:${testdir}/../../../stubs/servlet-api-2.4/:${testdir}/../../../stubs/rundeck-api-java-client-13.2:${testdir}/../../../stubs/springframework-5.8.x/:${testdir}/../../../stubs/mdht-1.2.0/:${testdir}/../../../stubs/woodstox-core-6.4.0

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading