CVE-2022-24898: Improper Restriction of XML External Entity Reference (CWE-611)
value: org.xwiki.commons:xwiki-commons-xml is a common module used by other XWiki top level projects. Starting in version 2.7 and prior to versions 12.10.10, 13.4.4, and 13.8-rc-1, it is possible for a script to access any file accessing to the user running XWiki application server with XML External Entity Injection through the XML script service. The problem has been patched in versions 12.10.10, 13.4.4, and 13.8-rc-1. There is no easy workaround for fixing this vulnerability other than upgrading and being careful when giving Script rights.
CWE description: The product processes an XML document that can contain XML entities with URIs that resolve to documents outside of the intended sphere of control, causing the product to embed incorrect documents into its output.
CVSS v3 base score: 4.9
Published: 2022-04-28T20:15Z
Repository: https://github.com/xwiki/xwiki-commons
Language: Java
Files changed: xwiki-commons-core/xwiki-commons-xml/src/main/java/org/xwiki/xml/XMLUtils.java, xwiki-commons-core/xwiki-commons-xml/src/test/java/org/xwiki/xml/XMLUtilsTest.java
Commit date: 2021-09-10 09:40:10 +0200
Commit message: XWIKI-18946: Improve the default XML parser

Diff: diff --git a/xwiki-commons-core/xwiki-commons-xml/src/main/java/org/xwiki/xml/XMLUtils.java b/xwiki-commons-core/xwiki-commons-xml/src/main/java/org/xwiki/xml/XMLUtils.java
index 3cd1794d3..d6913ee5a 100644
--- a/xwiki-commons-core/xwiki-commons-xml/src/main/java/org/xwiki/xml/XMLUtils.java
+++ b/xwiki-commons-core/xwiki-commons-xml/src/main/java/org/xwiki/xml/XMLUtils.java
@@ -108,10 +108,22 @@
     private static final DOMImplementationLS LS_IMPL;
 
     /** Xerces configuration parameter for disabling fetching and checking XMLs against their DTD. */
     private static final String DISABLE_DTD_PARAM = "http://apache.org/xml/features/nonvalidating/load-external-dtd";
 
+    /** Xerces configuration parameter for prevent DOCTYPE definition. */
+    private static final String DISABLE_EXTERNAL_DOCTYPE_DECLARATION =
+        "http://apache.org/xml/features/disallow-doctype-decl";
+
+    /** Xerces configuration parameter for disabling inserting entities defined in external files. */
+    private static final String DISABLE_EXTERNAL_PARAMETER_ENTITIES =
+        "http://xml.org/sax/features/external-parameter-entities";
+
+    /** Xerces configuration parameter for disabling inserting entities defined in external files. */
+    private static final String DISABLE_EXTERNAL_GENERAL_ENTITIES =
+        "http://xml.org/sax/features/external-general-entities";
+
     static {
         DOMImplementationLS implementation = null;
         try {
             implementation =
                 (DOMImplementationLS) DOMImplementationRegistry.newInstance().getDOMImplementation("LS 3.0");
@@ -514,10 +526,21 @@ public static Document parse(LSInput source)
             // Disable validation, since this takes a lot of time and causes unneeded network traffic
             p.getDomConfig().setParameter("validate", false);
             if (p.getDomConfig().canSetParameter(DISABLE_DTD_PARAM, false)) {
                 p.getDomConfig().setParameter(DISABLE_DTD_PARAM, false);
             }
+
+            // Avoid XML eXternal Entity injection (XXE)
+            if (p.getDomConfig().canSetParameter(DISABLE_EXTERNAL_DOCTYPE_DECLARATION, false)) {
+                p.getDomConfig().setParameter(DISABLE_EXTERNAL_DOCTYPE_DECLARATION, false);
+            }
+            if (p.getDomConfig().canSetParameter(DISABLE_EXTERNAL_PARAMETER_ENTITIES, false)) {
+                p.getDomConfig().setParameter(DISABLE_EXTERNAL_PARAMETER_ENTITIES, false);
+            }
+            if (p.getDomConfig().canSetParameter(DISABLE_EXTERNAL_GENERAL_ENTITIES, false)) {
+                p.getDomConfig().setParameter(DISABLE_EXTERNAL_GENERAL_ENTITIES, false);
+            }
             return p.parse(source);
         } catch (Exception ex) {
             LOGGER.warn("Cannot parse XML document: [{}]", ex.getMessage());
             return null;
         }
diff --git a/xwiki-commons-core/xwiki-commons-xml/src/test/java/org/xwiki/xml/XMLUtilsTest.java b/xwiki-commons-core/xwiki-commons-xml/src/test/java/org/xwiki/xml/XMLUtilsTest.java
index 22623a6f0..1b74770dc 100644
--- a/xwiki-commons-core/xwiki-commons-xml/src/test/java/org/xwiki/xml/XMLUtilsTest.java
+++ b/xwiki-commons-core/xwiki-commons-xml/src/test/java/org/xwiki/xml/XMLUtilsTest.java
@@ -17,14 +17,23 @@
  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
  */
 package org.xwiki.xml;
 
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.commons.io.FileUtils;
 import org.apache.html.dom.HTMLDocumentImpl;
 import org.junit.jupiter.api.Test;
+import org.w3c.dom.Document;
 import org.w3c.dom.Element;
+import org.w3c.dom.bootstrap.DOMImplementationRegistry;
 import org.w3c.dom.html.HTMLElement;
+import org.w3c.dom.ls.DOMImplementationLS;
+import org.w3c.dom.ls.LSInput;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -277,6 +286,24 @@ void serializeNode()
         node.setBody((HTMLElement) body);
         body.setAttribute("class", "toto");
         serialize = XMLUtils.serialize(node, false);
         assertEquals("<HTML><HEAD/><BODY class=\"toto\"/></HTML>", serialize);
     }
+
+    @Test
+    void disableExternalEntities()
+        throws ClassNotFoundException, InstantiationException, IllegalAccessException, ClassCastException, IOException
+    {
+        File tempFile = File.createTempFile("file", ".txt");
+
+        FileUtils.write(tempFile, "external", StandardCharsets.UTF_8);
+
+        DOMImplementationLS ls =
+            (DOMImplementationLS) DOMImplementationRegistry.newInstance().getDOMImplementation("LS 3.0");
+        LSInput input = ls.createLSInput();
+        input.setStringData("<?xml version='1.0' encoding='UTF-8'?>" + "<!DOCTYPE root[<!ENTITY xxe SYSTEM 'file://"
+            + tempFile.getAbsolutePath() + "' >]><root>&xxe;</root>");
+
+        Document result = XMLUtils.parse(input);
+        assertNotEquals("external", result.getDocumentElement().getTextContent());
+    }
 }

Fixed code:     /** Xerces configuration parameter for prevent DOCTYPE definition. */
    private static final String DISABLE_EXTERNAL_DOCTYPE_DECLARATION =
        "http://apache.org/xml/features/disallow-doctype-decl";

    /** Xerces configuration parameter for disabling inserting entities defined in external files. */
    private static final String DISABLE_EXTERNAL_PARAMETER_ENTITIES =
        "http://xml.org/sax/features/external-parameter-entities";

    /** Xerces configuration parameter for disabling inserting entities defined in external files. */
    private static final String DISABLE_EXTERNAL_GENERAL_ENTITIES =
        "http://xml.org/sax/features/external-general-entities";


            // Avoid XML eXternal Entity injection (XXE)
            if (p.getDomConfig().canSetParameter(DISABLE_EXTERNAL_DOCTYPE_DECLARATION, false)) {
                p.getDomConfig().setParameter(DISABLE_EXTERNAL_DOCTYPE_DECLARATION, false);
            }
            if (p.getDomConfig().canSetParameter(DISABLE_EXTERNAL_PARAMETER_ENTITIES, false)) {
                p.getDomConfig().setParameter(DISABLE_EXTERNAL_PARAMETER_ENTITIES, false);
            }
            if (p.getDomConfig().canSetParameter(DISABLE_EXTERNAL_GENERAL_ENTITIES, false)) {
                p.getDomConfig().setParameter(DISABLE_EXTERNAL_GENERAL_ENTITIES, false);
            }
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

import org.apache.commons.io.FileUtils;
import org.w3c.dom.Document;
import org.w3c.dom.bootstrap.DOMImplementationRegistry;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSInput;

    @Test
    void disableExternalEntities()
        throws ClassNotFoundException, InstantiationException, IllegalAccessException, ClassCastException, IOException
    {
        File tempFile = File.createTempFile("file", ".txt");

        FileUtils.write(tempFile, "external", StandardCharsets.UTF_8);

        DOMImplementationLS ls =
            (DOMImplementationLS) DOMImplementationRegistry.newInstance().getDOMImplementation("LS 3.0");
        LSInput input = ls.createLSInput();
        input.setStringData("<?xml version='1.0' encoding='UTF-8'?>" + "<!DOCTYPE root[<!ENTITY xxe SYSTEM 'file://"
            + tempFile.getAbsolutePath() + "' >]><root>&xxe;</root>");

        Document result = XMLUtils.parse(input);
        assertNotEquals("external", result.getDocumentElement().getTextContent());
    }