Skip to content
Merged
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
4 changes: 4 additions & 0 deletions java/ql/lib/change-notes/2026-04-16-woodstox-xxe.md
Comment thread
chmodxxx marked this conversation as resolved.
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.
31 changes: 27 additions & 4 deletions java/ql/lib/semmle/code/java/security/XmlParsers.qll
Original file line number Diff line number Diff line change
Expand Up @@ -179,12 +179,29 @@ class XmlInputFactory extends RefType {
XmlInputFactory() { this.hasQualifiedName(javaxOrJakarta() + ".xml.stream", "XMLInputFactory") }
}

/** A call to `XMLInputFactory.createXMLStreamReader`. */
/**
* The class `com.ctc.wstx.stax.WstxInputFactory` or its abstract supertype
* `org.codehaus.stax2.XMLInputFactory2` from the Woodstox StAX library.
*/
class WstxInputFactory extends RefType {
WstxInputFactory() {
this.hasQualifiedName("com.ctc.wstx.stax", "WstxInputFactory") or
this.hasQualifiedName("org.codehaus.stax2", "XMLInputFactory2")
}
}

/**
* A call to `XMLInputFactory.createXMLStreamReader` or the equivalent method on the
* Woodstox `WstxInputFactory`.
*/
class XmlInputFactoryStreamReader extends XmlParserCall {
XmlInputFactoryStreamReader() {
exists(Method m |
this.getMethod() = m and
m.getDeclaringType() instanceof XmlInputFactory and
(
m.getDeclaringType() instanceof XmlInputFactory or
m.getDeclaringType() instanceof WstxInputFactory
) and
m.hasName("createXMLStreamReader")
)
}
Expand Down Expand Up @@ -212,7 +229,10 @@ class XmlInputFactoryEventReader extends XmlParserCall {
XmlInputFactoryEventReader() {
exists(Method m |
this.getMethod() = m and
m.getDeclaringType() instanceof XmlInputFactory and
(
m.getDeclaringType() instanceof XmlInputFactory or
m.getDeclaringType() instanceof WstxInputFactory
) and
m.hasName("createXMLEventReader")
)
}
Expand All @@ -235,7 +255,10 @@ class XmlInputFactoryConfig extends ParserConfig {
XmlInputFactoryConfig() {
exists(Method m |
m = this.getMethod() and
m.getDeclaringType() instanceof XmlInputFactory and
(
m.getDeclaringType() instanceof XmlInputFactory or
m.getDeclaringType() instanceof WstxInputFactory
) and
m.hasName("setProperty")
)
}
Expand Down
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