CVE-2023-26119 (Java, published 2023-04-03T05:15Z)
Description: value: Versions of the package net.sourceforge.htmlunit:htmlunit from 0 and before 3.0.0 are vulnerable to Remote Code Execution (RCE) via XSTL, when browsing the attacker’s webpage.
Severity: nan (CVSS v3 base score: 9.8)
Weakness: NVD-CWE-noinfo \u2014 Insufficient Information
There is insufficient information about the issue to classify it; details are unkown or unspecified.
Repository: https://github.com/HtmlUnit/htmlunit
Files changed:
src/changes/changes.xml
src/main/java/com/gargoylesoftware/htmlunit/javascript/host/xml/XSLTProcessor.java
src/test/java/com/gargoylesoftware/htmlunit/ErrorOutputChecker.java
src/test/java/com/gargoylesoftware/htmlunit/javascript/host/xml/XSLTProcessorTest.java
Commit message: enable FEATURE_SECURE_PROCESSING for the XSLT processor


Vulnerable code:
            Pattern.compile(".*Quercus finished initialization in \\d*ms\r?\n")
    @Alerts("exception")
            + "      var xmlDoc = createXmlDocument();\n"
            + "      xmlDoc.async = false;\n"
            + "      xmlDoc.load('" + URL_SECOND + "1');\n"

            + "      var xslDoc;\n"
            + "      xslDoc = createXmlDocument();\n"
            + "      xslDoc.async = false;\n"
            + "      xslDoc.load('" + URL_SECOND + "2');\n"
            + "      log(new XMLSerializer().serializeToString(newDocument.documentElement).length);\n"
            + "      newDocument = processor.transformToDocument(xmlDoc.documentElement);\n"
            + "      log(newDocument.documentElement);\n"

Fixed code:
            <action type="fix" dev="rbri">
                Enable FEATURE_SECURE_PROCESSING for the XSLT processor.
            </action>
import javax.xml.XMLConstants;
            // By default, the JDK turns on FSP for DOM and SAX parsers and XML schema validators,
            // which sets a number of processing limits on the processors. Conversely, by default,
            // the JDK turns off FSP for transformers and XPath, which enables extension functions for XSLT and XPath.
            transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);

            Pattern.compile(".*Quercus finished initialization in \\d*ms\r?\n"),

            // Xalan
            Pattern.compile("ERROR:\\s*'Use of the extension function "
                    + "'http://xml\\.apache\\.org/xalan/java/.*' "
                    + "is not allowed when the secure processing feature is set to true\\.'\r?\n"),
import com.gargoylesoftware.htmlunit.junit.BrowserRunner.HtmlUnitNYI;
    @Alerts(DEFAULT = "<html xmlns=\"http://www.w3.org/1999/xhtml\">"
                + "<head></head><body> <h2>My CD Collection</h2> "
                + "<ul><li>Empire Burlesque (Bob Dylan)</li></ul> </body></html>",
            FF = "<html xmlns=\"http://www.w3.org/1999/xhtml\">"
                + "<body><h2>My CD Collection</h2>"
                + "<ul><li>Empire Burlesque (Bob Dylan)</li></ul></body></html>",
            FF_ESR = "<html xmlns=\"http://www.w3.org/1999/xhtml\">"
                + "<body><h2>My CD Collection</h2>"
                + "<ul><li>Empire Burlesque (Bob Dylan)</li></ul></body></html>",
            IE = "exception")
    @HtmlUnitNYI(CHROME = "<html><body><h2>My CD Collection</h2>"
                + "<ul><li>Empire Burlesque (Bob Dylan)</li></ul></body></html>",
            EDGE = "<html><body><h2>My CD Collection</h2>"
                + "<ul><li>Empire Burlesque (Bob Dylan)</li></ul></body></html>",
            FF = "<html><body><h2>My CD Collection</h2>"
                + "<ul><li>Empire Burlesque (Bob Dylan)</li></ul></body></html>",
            FF_ESR = "<html><body><h2>My CD Collection</h2>"
                + "<ul><li>Empire Burlesque (Bob Dylan)</li></ul></body></html>")

            + "  function createXmlDocument() {\n"
            + "    return document.implementation.createDocument('', '', null);\n"
            + "  }\n"

            + "  function  loadXMLDocument(url) {\n"
            + "    var xhttp = new XMLHttpRequest();\n"
            + "    xhttp.open('GET', url, false);\n"
            + "    xhttp.send();\n"
            + "    return xhttp.responseXML;\n"
            + "  }"

            + "      var xmlDoc = loadXMLDocument('" + URL_SECOND + "1');\n"
            + "      var xslDoc = loadXMLDocument('" + URL_SECOND + "2');\n"
            + "      log(new XMLSerializer().serializeToString(newDocument.documentElement));\n"

    /**
     * @throws Exception if the test fails
     */
    @Test
    @Alerts(DEFAULT = {"preparation done", "null"},
            FF = {"preparation done", "exception"},
            FF_ESR = {"preparation done", "exception"},
            IE = "exception")
    @HtmlUnitNYI(CHROME = {"preparation done", "exception"},
            EDGE = {"preparation done", "exception"})
    public void testSecurity() throws Exception {
        final String html = "<html><head>\n"
            + "<script>\n"
            + LOG_TITLE_FUNCTION

            + "  function createXmlDocument() {\n"
            + "    return document.implementation.createDocument('', '', null);\n"
            + "  }\n"

            + "  function  loadXMLDocument(url) {\n"
            + "    var xhttp = new XMLHttpRequest();\n"
            + "    xhttp.open('GET', url, false);\n"
            + "    xhttp.send();\n"
            + "    return xhttp.responseXML;\n"
            + "  }"

            + "  function test() {\n"
            + "    try {\n"
            + "      var xmlDoc = loadXMLDocument('" + URL_SECOND + "1');\n"
            + "      var xslDoc = loadXMLDocument('" + URL_SECOND + "2');\n"

            + "      var processor = new XSLTProcessor();\n"
            + "      processor.importStylesheet(xslDoc);\n"
            + "      log('preparation done');\n"
            + "      var newDocument = processor.transformToDocument(xmlDoc);\n"
            + "      log(newDocument);\n"
            + "    } catch(e) { log('exception'); }\n"
            + "  }\n"
            + "</script></head>"
            + "<body onload='test()'>\n"
            + "</body></html>";

        final String xml
            = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n"
            + "

";

        final String xsl
            = " <xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" "
                    + "xmlns:rt=\"http://xml.apache.org/xalan/java/java.lang.Runtime\" "
                    + "xmlns:ob=\"http://xml.apache.org/xalan/java/java.lang.Object\">\r\n"
            + "    <xsl:template match='/'>\n"
            + "      <xsl:variable name='rtobject' select='rt:getRuntime()'/>\n"
            + "      <xsl:variable name=\"rtString\" select=\"ob:toString($rtobject)\"/>\n"
            + "      <xsl:value-of select=\"$rtString\"/>\n"
            + "    </xsl:template>\r\n"
            + "  </xsl:stylesheet>";

        final MockWebConnection conn = getMockWebConnection();
        conn.setResponse(new URL(URL_SECOND, "1"), xml, MimeType.TEXT_XML);
        conn.setResponse(new URL(URL_SECOND, "2"), xsl, MimeType.TEXT_XML);

        loadPageVerifyTitle2(html);
    }