This is an automated email from the ASF dual-hosted git repository.
gnodet pushed a commit to branch maven-4.0.x
in repository https://gitbox.apache.org/repos/asf/maven.git
The following commit(s) were added to refs/heads/maven-4.0.x by this push:
new e691b93a6f Backport #12256: Centralize XXE hardening for StAX XML
parsers
e691b93a6f is described below
commit e691b93a6fdbd95d2bc93525fccfc8841702bc87
Author: Guillaume Nodet <[email protected]>
AuthorDate: Tue Jun 16 10:56:32 2026 +0200
Backport #12256: Centralize XXE hardening for StAX XML parsers
---
.../java/org/apache/maven/api/xml/XmlService.java | 15 ++++++++++++++
.../maven/model/root/DefaultRootLocator.java | 5 +++--
.../plugin/descriptor/PluginDescriptorBuilder.java | 21 +++++++++----------
.../maven/project/ExtensionDescriptorBuilder.java | 3 +--
.../project/ExtensionDescriptorBuilderTest.java | 24 ++++++++++++++++++++++
.../apache/maven/impl/DefaultModelXmlFactory.java | 6 +++---
.../impl/model/rootlocator/PomXmlRootDetector.java | 4 ++--
.../maven/internal/xml/DefaultXmlService.java | 5 ++---
.../maven/internal/xml/XmlNodeStaxBuilder.java | 5 ++---
src/mdo/reader-stax.vm | 2 ++
src/mdo/reader.vm | 6 ++++++
11 files changed, 70 insertions(+), 26 deletions(-)
diff --git
a/api/maven-api-xml/src/main/java/org/apache/maven/api/xml/XmlService.java
b/api/maven-api-xml/src/main/java/org/apache/maven/api/xml/XmlService.java
index ea4b1593d4..13ca0bae15 100644
--- a/api/maven-api-xml/src/main/java/org/apache/maven/api/xml/XmlService.java
+++ b/api/maven-api-xml/src/main/java/org/apache/maven/api/xml/XmlService.java
@@ -18,6 +18,7 @@
*/
package org.apache.maven.api.xml;
+import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
@@ -103,6 +104,20 @@ public abstract class XmlService {
*/
public static final String KEYS_COMBINATION_MODE_ATTRIBUTE =
"combine.keys";
+ /**
+ * Creates a new {@link XMLInputFactory} hardened against XXE attacks.
+ * The returned factory has DTD support and external entity resolution
disabled.
+ *
+ * @return a hardened XMLInputFactory
+ * @since 4.0.0-rc-6
+ */
+ public static XMLInputFactory newXMLInputFactory() {
+ XMLInputFactory factory = XMLInputFactory.newFactory();
+ factory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
+ factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES,
false);
+ return factory;
+ }
+
/**
* Convenience method to merge two XML nodes using default settings.
*/
diff --git
a/compat/maven-model-builder/src/main/java/org/apache/maven/model/root/DefaultRootLocator.java
b/compat/maven-model-builder/src/main/java/org/apache/maven/model/root/DefaultRootLocator.java
index d248ec94f8..9620ad7ef2 100644
---
a/compat/maven-model-builder/src/main/java/org/apache/maven/model/root/DefaultRootLocator.java
+++
b/compat/maven-model-builder/src/main/java/org/apache/maven/model/root/DefaultRootLocator.java
@@ -19,7 +19,6 @@
package org.apache.maven.model.root;
import javax.inject.Named;
-import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
@@ -28,6 +27,8 @@
import java.nio.file.Files;
import java.nio.file.Path;
+import org.apache.maven.api.xml.XmlService;
+
/**
* @deprecated use {@code org.apache.maven.api.services.model.RootLocator}
instead
*/
@@ -43,7 +44,7 @@ public boolean isRootDirectory(Path dir) {
// we're too early to use the modelProcessor ...
Path pom = dir.resolve("pom.xml");
try (InputStream is = Files.newInputStream(pom)) {
- XMLStreamReader parser =
XMLInputFactory.newFactory().createXMLStreamReader(is);
+ XMLStreamReader parser =
XmlService.newXMLInputFactory().createXMLStreamReader(is);
if (parser.nextTag() == XMLStreamReader.START_ELEMENT
&& parser.getLocalName().equals("project")) {
for (int i = 0; i < parser.getAttributeCount(); i++) {
diff --git
a/compat/maven-plugin-api/src/main/java/org/apache/maven/plugin/descriptor/PluginDescriptorBuilder.java
b/compat/maven-plugin-api/src/main/java/org/apache/maven/plugin/descriptor/PluginDescriptorBuilder.java
index 086a49daaf..4db13772d6 100644
---
a/compat/maven-plugin-api/src/main/java/org/apache/maven/plugin/descriptor/PluginDescriptorBuilder.java
+++
b/compat/maven-plugin-api/src/main/java/org/apache/maven/plugin/descriptor/PluginDescriptorBuilder.java
@@ -18,7 +18,6 @@
*/
package org.apache.maven.plugin.descriptor;
-import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
@@ -74,12 +73,12 @@ public PluginDescriptor build(Reader reader, String source)
throws PlexusConfigu
try {
BufferedReader br = new BufferedReader(reader, BUFFER_SIZE);
br.mark(BUFFER_SIZE);
- XMLStreamReader xsr =
XMLInputFactory.newFactory().createXMLStreamReader(br);
+ XMLStreamReader xsr =
XmlService.newXMLInputFactory().createXMLStreamReader(br);
xsr.nextTag();
String nsUri = xsr.getNamespaceURI();
br.reset();
if (PLUGIN_2_0_0.equals(nsUri)) {
- xsr = XMLInputFactory.newFactory().createXMLStreamReader(br);
+ xsr =
XmlService.newXMLInputFactory().createXMLStreamReader(br);
return new PluginDescriptor(new
PluginDescriptorStaxReader().read(xsr, true));
} else {
// Call buildConfiguration() for backward compatibility with
subclasses that override it
@@ -98,11 +97,11 @@ public PluginDescriptor build(ReaderSupplier
readerSupplier) throws PlexusConfig
public PluginDescriptor build(ReaderSupplier readerSupplier, String
source) throws PlexusConfigurationException {
try (BufferedReader br = new BufferedReader(readerSupplier.open(),
BUFFER_SIZE)) {
br.mark(BUFFER_SIZE);
- XMLStreamReader xsr =
XMLInputFactory.newFactory().createXMLStreamReader(br);
+ XMLStreamReader xsr =
XmlService.newXMLInputFactory().createXMLStreamReader(br);
xsr.nextTag();
String nsUri = xsr.getNamespaceURI();
try (BufferedReader br2 = reset(readerSupplier, br)) {
- xsr = XMLInputFactory.newFactory().createXMLStreamReader(br2);
+ xsr =
XmlService.newXMLInputFactory().createXMLStreamReader(br2);
return build(source, nsUri, xsr);
}
} catch (XMLStreamException | IOException e) {
@@ -118,12 +117,12 @@ public PluginDescriptor build(InputStream input, String
source) throws PlexusCon
try {
BufferedInputStream bis = new BufferedInputStream(input,
BUFFER_SIZE);
bis.mark(BUFFER_SIZE);
- XMLStreamReader xsr =
XMLInputFactory.newFactory().createXMLStreamReader(bis);
+ XMLStreamReader xsr =
XmlService.newXMLInputFactory().createXMLStreamReader(bis);
xsr.nextTag();
String nsUri = xsr.getNamespaceURI();
bis.reset();
if (PLUGIN_2_0_0.equals(nsUri)) {
- xsr = XMLInputFactory.newFactory().createXMLStreamReader(bis);
+ xsr =
XmlService.newXMLInputFactory().createXMLStreamReader(bis);
return new PluginDescriptor(new
PluginDescriptorStaxReader().read(xsr, true));
} else {
// Call buildConfiguration() for backward compatibility with
subclasses that override it
@@ -142,11 +141,11 @@ public PluginDescriptor build(StreamSupplier
inputSupplier) throws PlexusConfigu
public PluginDescriptor build(StreamSupplier inputSupplier, String source)
throws PlexusConfigurationException {
try (BufferedInputStream bis = new
BufferedInputStream(inputSupplier.open(), BUFFER_SIZE)) {
bis.mark(BUFFER_SIZE);
- XMLStreamReader xsr =
XMLInputFactory.newFactory().createXMLStreamReader(bis);
+ XMLStreamReader xsr =
XmlService.newXMLInputFactory().createXMLStreamReader(bis);
xsr.nextTag();
String nsUri = xsr.getNamespaceURI();
try (BufferedInputStream bis2 = reset(inputSupplier, bis)) {
- xsr = XMLInputFactory.newFactory().createXMLStreamReader(bis2);
+ xsr =
XmlService.newXMLInputFactory().createXMLStreamReader(bis2);
return build(source, nsUri, xsr);
}
} catch (XMLStreamException | IOException e) {
@@ -504,7 +503,7 @@ public MojoDescriptor
buildComponentDescriptor(PlexusConfiguration c, PluginDesc
public PlexusConfiguration buildConfiguration(Reader configuration) throws
PlexusConfigurationException {
try {
- XMLStreamReader reader =
XMLInputFactory.newFactory().createXMLStreamReader(configuration);
+ XMLStreamReader reader =
XmlService.newXMLInputFactory().createXMLStreamReader(configuration);
return
XmlPlexusConfiguration.toPlexusConfiguration(XmlService.read(reader));
} catch (XMLStreamException e) {
throw new PlexusConfigurationException(e.getMessage(), e);
@@ -513,7 +512,7 @@ public PlexusConfiguration buildConfiguration(Reader
configuration) throws Plexu
public PlexusConfiguration buildConfiguration(InputStream configuration)
throws PlexusConfigurationException {
try {
- XMLStreamReader reader =
XMLInputFactory.newFactory().createXMLStreamReader(configuration);
+ XMLStreamReader reader =
XmlService.newXMLInputFactory().createXMLStreamReader(configuration);
return
XmlPlexusConfiguration.toPlexusConfiguration(XmlService.read(reader));
} catch (XMLStreamException e) {
throw new PlexusConfigurationException(e.getMessage(), e);
diff --git
a/impl/maven-core/src/main/java/org/apache/maven/project/ExtensionDescriptorBuilder.java
b/impl/maven-core/src/main/java/org/apache/maven/project/ExtensionDescriptorBuilder.java
index a29cc4bd80..e2bbbc36ca 100644
---
a/impl/maven-core/src/main/java/org/apache/maven/project/ExtensionDescriptorBuilder.java
+++
b/impl/maven-core/src/main/java/org/apache/maven/project/ExtensionDescriptorBuilder.java
@@ -18,7 +18,6 @@
*/
package org.apache.maven.project;
-import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
@@ -88,7 +87,7 @@ public ExtensionDescriptor build(InputStream is) throws
IOException {
XmlNode dom;
try {
- XMLStreamReader reader =
XMLInputFactory.newFactory().createXMLStreamReader(is);
+ XMLStreamReader reader =
XmlService.newXMLInputFactory().createXMLStreamReader(is);
dom = XmlService.read(reader);
} catch (XMLStreamException e) {
throw new IOException(e.getMessage(), e);
diff --git
a/impl/maven-core/src/test/java/org/apache/maven/project/ExtensionDescriptorBuilderTest.java
b/impl/maven-core/src/test/java/org/apache/maven/project/ExtensionDescriptorBuilderTest.java
index 5e73daa279..982fe59ae5 100644
---
a/impl/maven-core/src/test/java/org/apache/maven/project/ExtensionDescriptorBuilderTest.java
+++
b/impl/maven-core/src/test/java/org/apache/maven/project/ExtensionDescriptorBuilderTest.java
@@ -19,8 +19,11 @@
package org.apache.maven.project;
import java.io.ByteArrayInputStream;
+import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.util.Arrays;
import org.junit.jupiter.api.AfterEach;
@@ -31,6 +34,7 @@
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
/**
@@ -82,4 +86,24 @@ void testCompleteDescriptor() throws Exception {
assertEquals(Arrays.asList("a", "b", "c"), ed.getExportedPackages());
assertEquals(Arrays.asList("x", "y", "z"), ed.getExportedArtifacts());
}
+
+ @Test
+ void testExternalEntityIsNotResolved() throws Exception {
+ Path secret = Files.createTempFile("extension-xxe", ".txt");
+ Files.writeString(secret, "TOPSECRET");
+ try {
+ String xml = "<?xml version='1.0'?>\n" + "<!DOCTYPE extension [
<!ENTITY xxe SYSTEM \""
+ + secret.toUri() + "\"> ]>\n"
+ +
"<extension><exportedPackages><exportedPackage>&xxe;</exportedPackage></exportedPackages></extension>";
+
+ try {
+ ExtensionDescriptor ed = builder.build(toStream(xml));
+ assertFalse(ed.getExportedPackages().contains("TOPSECRET"));
+ } catch (IOException expected) {
+ // doctype / external entities rejected at parse time
+ }
+ } finally {
+ Files.deleteIfExists(secret);
+ }
+ }
}
diff --git
a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultModelXmlFactory.java
b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultModelXmlFactory.java
index e18e723d8d..2b00e49162 100644
---
a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultModelXmlFactory.java
+++
b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultModelXmlFactory.java
@@ -18,7 +18,6 @@
*/
package org.apache.maven.impl;
-import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
@@ -47,6 +46,7 @@
import org.apache.maven.api.services.xml.XmlReaderRequest;
import org.apache.maven.api.services.xml.XmlWriterException;
import org.apache.maven.api.services.xml.XmlWriterRequest;
+import org.apache.maven.api.xml.XmlService;
import org.apache.maven.model.v4.MavenStaxReader;
import org.apache.maven.model.v4.MavenStaxWriter;
@@ -147,7 +147,7 @@ private Model doRead(XmlReaderRequest request) throws
XmlReaderException {
private static String extractModelId(InputStream inputStream) {
try {
- XMLStreamReader xmlReader =
XMLInputFactory.newFactory().createXMLStreamReader(inputStream);
+ XMLStreamReader xmlReader =
XmlService.newXMLInputFactory().createXMLStreamReader(inputStream);
try {
return extractModelId(xmlReader);
} finally {
@@ -160,7 +160,7 @@ private static String extractModelId(InputStream
inputStream) {
private static String extractModelId(Reader reader) {
try {
- XMLStreamReader xmlReader =
XMLInputFactory.newFactory().createXMLStreamReader(reader);
+ XMLStreamReader xmlReader =
XmlService.newXMLInputFactory().createXMLStreamReader(reader);
try {
return extractModelId(xmlReader);
} finally {
diff --git
a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/rootlocator/PomXmlRootDetector.java
b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/rootlocator/PomXmlRootDetector.java
index 506b634253..56151a593f 100644
---
a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/rootlocator/PomXmlRootDetector.java
+++
b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/rootlocator/PomXmlRootDetector.java
@@ -18,7 +18,6 @@
*/
package org.apache.maven.impl.model.rootlocator;
-import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
@@ -29,6 +28,7 @@
import org.apache.maven.api.di.Named;
import org.apache.maven.api.services.model.RootDetector;
+import org.apache.maven.api.xml.XmlService;
@Named
public class PomXmlRootDetector implements RootDetector {
@@ -37,7 +37,7 @@ public boolean isRootDirectory(Path dir) {
// we're too early to use the modelProcessor ...
Path pom = dir.resolve("pom.xml");
try (InputStream is = Files.newInputStream(pom)) {
- XMLStreamReader parser =
XMLInputFactory.newFactory().createXMLStreamReader(is);
+ XMLStreamReader parser =
XmlService.newXMLInputFactory().createXMLStreamReader(is);
if (parser.nextTag() == XMLStreamReader.START_ELEMENT
&& parser.getLocalName().equals("project")) {
for (int i = 0; i < parser.getAttributeCount(); i++) {
diff --git
a/impl/maven-xml/src/main/java/org/apache/maven/internal/xml/DefaultXmlService.java
b/impl/maven-xml/src/main/java/org/apache/maven/internal/xml/DefaultXmlService.java
index 0904ebadb4..a05b66a2b0 100644
---
a/impl/maven-xml/src/main/java/org/apache/maven/internal/xml/DefaultXmlService.java
+++
b/impl/maven-xml/src/main/java/org/apache/maven/internal/xml/DefaultXmlService.java
@@ -18,7 +18,6 @@
*/
package org.apache.maven.internal.xml;
-import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
@@ -53,7 +52,7 @@ public class DefaultXmlService extends XmlService {
@Override
public XmlNode doRead(InputStream input, @Nullable
XmlService.InputLocationBuilder locationBuilder)
throws XMLStreamException {
- XMLStreamReader parser =
XMLInputFactory.newFactory().createXMLStreamReader(input);
+ XMLStreamReader parser =
XmlService.newXMLInputFactory().createXMLStreamReader(input);
return doRead(parser, locationBuilder);
}
@@ -61,7 +60,7 @@ public XmlNode doRead(InputStream input, @Nullable
XmlService.InputLocationBuild
@Override
public XmlNode doRead(Reader reader, @Nullable
XmlService.InputLocationBuilder locationBuilder)
throws XMLStreamException {
- XMLStreamReader parser =
XMLInputFactory.newFactory().createXMLStreamReader(reader);
+ XMLStreamReader parser =
XmlService.newXMLInputFactory().createXMLStreamReader(reader);
return doRead(parser, locationBuilder);
}
diff --git
a/impl/maven-xml/src/main/java/org/apache/maven/internal/xml/XmlNodeStaxBuilder.java
b/impl/maven-xml/src/main/java/org/apache/maven/internal/xml/XmlNodeStaxBuilder.java
index c346275630..c666afa8fd 100644
---
a/impl/maven-xml/src/main/java/org/apache/maven/internal/xml/XmlNodeStaxBuilder.java
+++
b/impl/maven-xml/src/main/java/org/apache/maven/internal/xml/XmlNodeStaxBuilder.java
@@ -18,7 +18,6 @@
*/
package org.apache.maven.internal.xml;
-import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
@@ -40,12 +39,12 @@ public class XmlNodeStaxBuilder {
public static XmlNode build(InputStream stream, InputLocationBuilderStax
locationBuilder)
throws XMLStreamException {
- XMLStreamReader parser =
XMLInputFactory.newFactory().createXMLStreamReader(stream);
+ XMLStreamReader parser =
XmlService.newXMLInputFactory().createXMLStreamReader(stream);
return build(parser, DEFAULT_TRIM, locationBuilder);
}
public static XmlNode build(Reader reader, InputLocationBuilderStax
locationBuilder) throws XMLStreamException {
- XMLStreamReader parser =
XMLInputFactory.newFactory().createXMLStreamReader(reader);
+ XMLStreamReader parser =
XmlService.newXMLInputFactory().createXMLStreamReader(reader);
return build(parser, DEFAULT_TRIM, locationBuilder);
}
diff --git a/src/mdo/reader-stax.vm b/src/mdo/reader-stax.vm
index 09d43a6bb8..73cd2b38c9 100644
--- a/src/mdo/reader-stax.vm
+++ b/src/mdo/reader-stax.vm
@@ -110,6 +110,8 @@ public class ${className} {
static {
XMLInputFactory factory = XMLInputFactory.newFactory();
factory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, false);
+ factory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
+
factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
XML_INPUT_FACTORY = factory;
}
}
diff --git a/src/mdo/reader.vm b/src/mdo/reader.vm
index dd2150eb66..f9674a677b 100644
--- a/src/mdo/reader.vm
+++ b/src/mdo/reader.vm
@@ -360,6 +360,8 @@ public class ${className} {
public ${root.name} read(Reader reader, boolean strict) throws
IOException, XMLStreamException {
XMLInputFactory factory = XMLInputFactory.newFactory();
factory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES,
false);
+ factory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
+ factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES,
false);
XMLStreamReader parser = null;
try {
parser = factory.createXMLStreamReader(reader);
@@ -394,6 +396,8 @@ public class ${className} {
public ${root.name} read(InputStream in, boolean strict) throws
IOException, XMLStreamException {
XMLInputFactory factory = XMLInputFactory.newFactory();
factory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES,
false);
+ factory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
+ factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES,
false);
StreamSource streamSource = new StreamSource(in, null);
XMLStreamReader parser = factory.createXMLStreamReader(streamSource);
return read(parser, strict);
@@ -411,6 +415,8 @@ public class ${className} {
public ${root.name} read(InputStream in) throws IOException,
XMLStreamException {
XMLInputFactory factory = XMLInputFactory.newFactory();
factory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES,
false);
+ factory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
+ factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES,
false);
StreamSource streamSource = new StreamSource(in, null);
XMLStreamReader parser = factory.createXMLStreamReader(streamSource);
return read(parser,true);