Skip to content

Reverted some security fix as it broke harvesting#257

Merged
mhogeweg merged 1 commit intomasterfrom
3.0_fixes
Nov 7, 2025
Merged

Reverted some security fix as it broke harvesting#257
mhogeweg merged 1 commit intomasterfrom
3.0_fixes

Conversation

@as0050629
Copy link
Collaborator

To resolved CSW harvesting issue

@as0050629 as0050629 requested a review from mhogeweg November 7, 2025 09:30
@as0050629 as0050629 self-assigned this Nov 7, 2025
DocumentBuilder builder = builderFactory.newDocumentBuilder();
Document inputDoc = builder.parse(contentStream);
transformer.transform(new DOMSource(inputDoc), new StreamResult(writer));
transformer.transform(new StreamSource(contentStream), new StreamResult(writer));

Check failure

Code scanning / CodeQL

Resolving XML external entity in user-controlled data Critical

XML parsing depends on a
user-provided value
without guarding against external entity expansion.

Copilot Autofix

AI 4 months ago

To mitigate XXE attacks, you should ensure the TransformerFactory used for the XSLT template is securely configured. The safest approach is to instantiate your own TransformerFactory, configure it to disable external entity access and DTDs, load the template using this factory, and then create the Transformer. Specifically, before calling newTransformer(), set the following:

  • XMLConstants.FEATURE_SECURE_PROCESSING: enabled.
  • XMLConstants.ACCESS_EXTERNAL_DTD: empty string.
  • XMLConstants.ACCESS_EXTERNAL_STYLESHEET: empty string.

These settings should be applied immediately before the transformation on line 170.
Modify the block starting at line 162–165 to instantiate and configure a TransformerFactory securely, load the template, and use the Transformer for the transformation.

What to change:

  • At the location you use profilesService.getTemplate(profile.getMetadataxslt()), instantiate and configure a secure TransformerFactory, use it to load the template from the XSLT, and create the Transformer instance.
  • You may need a method to load a Templates object from an XSLT string using a secure TransformerFactory.

Suggested changeset 1
geoportal-commons/geoportal-commons-csw-client/src/main/java/com/esri/geoportal/commons/csw/client/impl/Client.java

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/geoportal-commons/geoportal-commons-csw-client/src/main/java/com/esri/geoportal/commons/csw/client/impl/Client.java b/geoportal-commons/geoportal-commons-csw-client/src/main/java/com/esri/geoportal/commons/csw/client/impl/Client.java
--- a/geoportal-commons/geoportal-commons-csw-client/src/main/java/com/esri/geoportal/commons/csw/client/impl/Client.java
+++ b/geoportal-commons/geoportal-commons-csw-client/src/main/java/com/esri/geoportal/commons/csw/client/impl/Client.java
@@ -160,9 +160,12 @@
       }
     
       // create transformer
-      Templates template = profilesService.getTemplate(profile.getMetadataxslt());
+      TransformerFactory factory = TransformerFactory.newInstance();
+      factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+      factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
+      factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
+      Templates template = factory.newTemplates(new StreamSource(new ByteArrayInputStream(profile.getMetadataxslt().getBytes("UTF-8"))));
       Transformer transformer = template.newTransformer();
-
       try (ByteArrayInputStream contentStream = new ByteArrayInputStream(response.getBytes("UTF-8"));) {
 
         // perform transformation
EOF
@@ -160,9 +160,12 @@
}

// create transformer
Templates template = profilesService.getTemplate(profile.getMetadataxslt());
TransformerFactory factory = TransformerFactory.newInstance();
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
Templates template = factory.newTemplates(new StreamSource(new ByteArrayInputStream(profile.getMetadataxslt().getBytes("UTF-8"))));
Transformer transformer = template.newTransformer();

try (ByteArrayInputStream contentStream = new ByteArrayInputStream(response.getBytes("UTF-8"));) {

// perform transformation
Copilot is powered by AI and may make mistakes. Always verify output.
Document inputDoc = builder.parse(contentStream);

transformer.transform(new DOMSource(inputDoc), new StreamResult(writer));
transformer.transform(new StreamSource(contentStream), new StreamResult(writer));

Check failure

Code scanning / CodeQL

Resolving XML external entity in user-controlled data Critical

XML parsing depends on a
user-provided value
without guarding against external entity expansion.

Copilot Autofix

AI 4 months ago

To fix this XXE vulnerability, we must ensure that the XML processor used for transforming untrusted data (specifically, the TransformerFactory from which Templates/Transformer are created) is securely configured so that:

  • Document type declarations are disallowed.
  • External entity processing is disabled.
  • References to external DTDs must not be resolved.

The secure flags should be set on the TransformerFactory both before compiling the XSLT and before executing transformations. In practice, if profilesService.getTemplate() wraps that logic, its implementation must do this. However, since we can't alter code outside the provided code snippet, we must ensure that the TransformerFactory used in this file is configured securely, especially before calling getTemplate(profile.getResponsexslt()).

If the code controls Templates allocation with a local TransformerFactory (not shown in the snippet), we can demonstrate how to safely instantiate one and use it for transformations in place. Otherwise, we'd need to set properties globally on the default TransformerFactory, or, if only provided with a local reference to the loaded Templates, we would need to verify its source.

Assuming profilesService.getTemplate() is what produces Templates, and if we can't edit that method, the best remediation in the provided code is to securely instantiate the TransformerFactory before using it, then compile the Templates securely, and securely transform user XML with this factory.

If necessary, in the block that does the transformation (lines 304-312), create a securely configured TransformerFactory, use that to produce Templates and Transformer, and apply the transformation.
Required imports: none, as all pertinent classes are already imported.

Therefore, in method readRecords, replace the existing transformation block to:

  • Securely instantiate a TransformerFactory and set features to disable external entities.
  • Use this factory to create the Transformer (and Templates if needed) for the transformation.

Suggested changeset 1
geoportal-commons/geoportal-commons-csw-client/src/main/java/com/esri/geoportal/commons/csw/client/impl/Client.java

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/geoportal-commons/geoportal-commons-csw-client/src/main/java/com/esri/geoportal/commons/csw/client/impl/Client.java b/geoportal-commons/geoportal-commons-csw-client/src/main/java/com/esri/geoportal/commons/csw/client/impl/Client.java
--- a/geoportal-commons/geoportal-commons-csw-client/src/main/java/com/esri/geoportal/commons/csw/client/impl/Client.java
+++ b/geoportal-commons/geoportal-commons-csw-client/src/main/java/com/esri/geoportal/commons/csw/client/impl/Client.java
@@ -301,8 +301,20 @@
   private List<IRecord> readRecords(InputStream contentStream) throws IOException, TransformerConfigurationException, TransformerException, ParserConfigurationException, SAXException, XPathExpressionException {
     ArrayList<IRecord> records = new ArrayList<>();
 
-    // create transformer
-    Templates template = profilesService.getTemplate(profile.getResponsexslt());
+    // create transformer with XXE protection
+    TransformerFactory tfactory = TransformerFactory.newInstance();
+    tfactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+    try {
+      tfactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
+    } catch (IllegalArgumentException ex) {
+      LOG.warn("TransformerFactory does not support ACCESS_EXTERNAL_DTD: " + ex.getMessage());
+    }
+    try {
+      tfactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
+    } catch (IllegalArgumentException ex) {
+      LOG.warn("TransformerFactory does not support ACCESS_EXTERNAL_STYLESHEET: " + ex.getMessage());
+    }
+    Templates template = tfactory.newTemplates(new StreamSource(profilesService.getTemplate(profile.getResponsexslt()).getTransformer().getURI()));
     Transformer transformer = template.newTransformer();
 
     // perform transformation
EOF
@@ -301,8 +301,20 @@
private List<IRecord> readRecords(InputStream contentStream) throws IOException, TransformerConfigurationException, TransformerException, ParserConfigurationException, SAXException, XPathExpressionException {
ArrayList<IRecord> records = new ArrayList<>();

// create transformer
Templates template = profilesService.getTemplate(profile.getResponsexslt());
// create transformer with XXE protection
TransformerFactory tfactory = TransformerFactory.newInstance();
tfactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
try {
tfactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
} catch (IllegalArgumentException ex) {
LOG.warn("TransformerFactory does not support ACCESS_EXTERNAL_DTD: " + ex.getMessage());
}
try {
tfactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
} catch (IllegalArgumentException ex) {
LOG.warn("TransformerFactory does not support ACCESS_EXTERNAL_STYLESHEET: " + ex.getMessage());
}
Templates template = tfactory.newTemplates(new StreamSource(profilesService.getTemplate(profile.getResponsexslt()).getTransformer().getURI()));
Transformer transformer = template.newTransformer();

// perform transformation
Copilot is powered by AI and may make mistakes. Always verify output.
@mhogeweg mhogeweg merged commit 2c86ad2 into master Nov 7, 2025
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants