CVE-2014-125087 (Java, published 2023-02-19T17:15Z)
Description: value: A vulnerability was found in java-xmlbuilder up to 1.1. It has been rated as problematic. Affected by this issue is some unknown functionality. The manipulation leads to xml external entity reference. Upgrading to version 1.2 is able to address this issue. The name of the patch is e6fddca201790abab4f2c274341c0bb8835c3e73. It is recommended to upgrade the affected component. The identifier of this vulnerability is VDB-221480.
Severity: nan (CVSS v3 base score: 9.8)
Weakness: CWE-611 \u2014 Improper Restriction of XML External Entity Reference
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.
Repository: https://github.com/jmurty/java-xmlbuilder
Files changed:
CHANGES.md
src/main/java/com/jamesmurty/utils/BaseXMLBuilder.java
src/main/java/com/jamesmurty/utils/XMLBuilder.java
src/main/java/com/jamesmurty/utils/XMLBuilder2.java
src/test/java/com/jamesmurty/utils/BaseXMLBuilderTests.java
src/test/java/com/jamesmurty/utils/external.txt
Commit message: Disable external entities by default to prevent XXE injection attacks, re #6

XML Builder classes now explicitly enable or disable
'external-general-entities' and 'external-parameter-entities' features
of the DocumentBuilderFactory when #create or #parse methods are used.

To prevent XML External Entity (XXE) injection attacks, these features
are disabled by default. They can only be enabled by passing a true
boolean value to new versions of the #create and #parse methods that
accept a flag for this feature.


Vulnerable code:
    protected static Document createDocumentImpl(String name, String namespaceURI)
    protected static Document parseDocumentImpl(InputSource inputSource)
        return new XMLBuilder(createDocumentImpl(name, namespaceURI));
        return new XMLBuilder(parseDocumentImpl(inputSource));
        return XMLBuilder.parse(new InputSource(new StringReader(xmlString)));
        return XMLBuilder.parse(new InputSource(new FileReader(xmlFile)));
    public static XMLBuilder2 create(String name, String namespaceURI)
            return new XMLBuilder2(createDocumentImpl(name, namespaceURI));
        return create(name, null);
    public static XMLBuilder2 parse(InputSource inputSource)
            return new XMLBuilder2(parseDocumentImpl(inputSource));
    public static XMLBuilder2 parse(String xmlString)
        return XMLBuilder2.parse(new InputSource(new StringReader(xmlString)));
    public static XMLBuilder2 parse(File xmlFile)
            return XMLBuilder2.parse(new InputSource(new FileReader(xmlFile)));

Fixed code:
Version 1.2 - Pending
---------------------

Fixes:

* Prevent XML External Entity (XXE) injection attacks by disabling parsing of
  general and parameter external entities by default (#6). External entities
  are now only parsed if this feature is explicitly enabled by passing a boolean
  flag value to the #create and #parse methods.  
  WARNING: This will break code that expects external entities to be parsed.

    /**
     * If true, the builder will raise an {@link XMLBuilderRuntimeException}
     * if external general and parameter entities cannot be explicitly enabled
     * or disabled.
     */
    public static boolean failIfExternalEntityParsingCannotBeConfigured = true;

    /**
     * Explicitly enable or disable the 'external-general-entities' and
     * 'external-parameter-entities' features of the underlying
     * DocumentBuilderFactory.
     *
     * TODO This is a naive approach that simply tries to apply all known
     * feature name/URL values in turn until one succeeds, or none do.
     *
     * @param factory
     * factory which will have external general and parameter entities enabled
     * or disabled.
     * @param enableExternalEntities
     * if true external entities will be explicitly enabled, otherwise they
     * will be explicitly disabled.
     */
    protected static void enableOrDisableExternalEntityParsing(
        DocumentBuilderFactory factory, boolean enableExternalEntities)
    {
        // Feature list drawn from:
        // https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Processing

        /* Enable or disable external general entities */
        String[] externalGeneralEntitiesFeatures = {
            // General
            "http://xml.org/sax/features/external-general-entities",
            // Xerces 1
            "http://xerces.apache.org/xerces-j/features.html#external-general-entities",
            // Xerces 2
            "http://xerces.apache.org/xerces2-j/features.html#external-general-entities",
        };
        boolean success = false;
        for (String feature: externalGeneralEntitiesFeatures) {
            try {
                factory.setFeature(feature, enableExternalEntities);
                success = true;
                break;
            } catch (ParserConfigurationException e) {
            }
        }
        if (!success && failIfExternalEntityParsingCannotBeConfigured) {
            throw new XMLBuilderRuntimeException(
                new ParserConfigurationException(
                    "Failed to set 'external-general-entities' feature to "
                    + enableExternalEntities));
        }

        /* Enable or disable external parameter entities */
        String[] externalParameterEntitiesFeatures = {
            // General
            "http://xml.org/sax/features/external-parameter-entities",
            // Xerces 1
            "http://xerces.apache.org/xerces-j/features.html#external-parameter-entities",
            // Xerces 2
            "http://xerces.apache.org/xerces2-j/features.html#external-parameter-entities",
        };
        success = false;
        for (String feature: externalParameterEntitiesFeatures) {
            try {
                factory.setFeature(feature, enableExternalEntities);
                success = true;
                break;
            } catch (ParserConfigurationException e) {
            }
        }
        if (!success && failIfExternalEntityParsingCannotBeConfigured) {
            throw new XMLBuilderRuntimeException(
                new ParserConfigurationException(
                    "Failed to set 'external-parameter-entities' feature to "
                    + enableExternalEntities));
        }
    }

    protected static Document createDocumentImpl(
        String name, String namespaceURI, boolean enableExternalEntities)
        enableOrDisableExternalEntityParsing(factory, enableExternalEntities);
    protected static Document parseDocumentImpl(
        InputSource inputSource, boolean enableExternalEntities)
        enableOrDisableExternalEntityParsing(factory, enableExternalEntities);
    /**
     * Construct a builder for new XML document with a default namespace.
     * The document will be created with the given root element, and the builder
     * returned by this method will serve as the starting-point for any further
     * document additions.
     *
     * @param name
     * the name of the document's root element.
     * @param namespaceURI
     * default namespace URI for document, ignored if null or empty.
     * @param enableExternalEntities
     * enable external entities; beware of XML External Entity (XXE) injection.
     * @return
     * a builder node that can be used to add more nodes to the XML document.
     *
     * @throws FactoryConfigurationError
     * @throws ParserConfigurationException
     */
    public static XMLBuilder create(String name, String namespaceURI,
        boolean enableExternalEntities)
        throws ParserConfigurationException, FactoryConfigurationError
    {
        return new XMLBuilder(
            createDocumentImpl(name, namespaceURI, enableExternalEntities));
    }

    /**
     * Construct a builder for new XML document. The document will be created
     * with the given root element, and the builder returned by this method
     * will serve as the starting-point for any further document additions.
     *
     * @param name
     * the name of the document's root element.
     * @param enableExternalEntities
     * enable external entities; beware of XML External Entity (XXE) injection.
     * @return
     * a builder node that can be used to add more nodes to the XML document.
     *
     * @throws FactoryConfigurationError
     * @throws ParserConfigurationException
     */
    public static XMLBuilder create(String name, boolean enableExternalEntities)
        throws ParserConfigurationException, FactoryConfigurationError
    {
        return create(name, null, enableExternalEntities);
    }

        return create(name, namespaceURI, false);
    /**
     * Construct a builder from an existing XML document. The provided XML
     * document will be parsed and an XMLBuilder object referencing the
     * document's root element will be returned.
     *
     * @param inputSource
     * an XML document input source that will be parsed into a DOM.
     * @param enableExternalEntities
     * enable external entities; beware of XML External Entity (XXE) injection.
     * @return
     * a builder node that can be used to add more nodes to the XML document.
     * @throws ParserConfigurationException
     *
     * @throws FactoryConfigurationError
     * @throws ParserConfigurationException
     * @throws IOException
     * @throws SAXException
     */
    public static XMLBuilder parse(
        InputSource inputSource, boolean enableExternalEntities)
        throws ParserConfigurationException, SAXException, IOException
    {
        return new XMLBuilder(
            parseDocumentImpl(inputSource, enableExternalEntities));
    }

    /**
     * Construct a builder from an existing XML document string.
     * The provided XML document will be parsed and an XMLBuilder
     * object referencing the document's root element will be returned.
     *
     * @param xmlString
     * an XML document string that will be parsed into a DOM.
     * @param enableExternalEntities
     * enable external entities; beware of XML External Entity (XXE) injection.
     * @return
     * a builder node that can be used to add more nodes to the XML document.
     *
     * @throws ParserConfigurationException
     * @throws FactoryConfigurationError
     * @throws ParserConfigurationException
     * @throws IOException
     * @throws SAXException
     */
    public static XMLBuilder parse(
        String xmlString, boolean enableExternalEntities)
        throws ParserConfigurationException, SAXException, IOException
    {
        return XMLBuilder.parse(
            new InputSource(new StringReader(xmlString)),
            enableExternalEntities);
    }

    /**
     * Construct a builder from an existing XML document file.
     * The provided XML document will be parsed and an XMLBuilder
     * object referencing the document's root element will be returned.
     *
     * @param xmlFile
     * an XML document file that will be parsed into a DOM.
     * @param enableExternalEntities
     * enable external entities; beware of XML External Entity (XXE) injection.
     * @return
     * a builder node that can be used to add more nodes to the XML document.
     *
     * @throws ParserConfigurationException
     * @throws FactoryConfigurationError
     * @throws ParserConfigurationException
     * @throws IOException
     * @throws SAXException
     */
    public static XMLBuilder parse(File xmlFile, boolean enableExternalEntities)
        throws ParserConfigurationException, SAXException, IOException
    {
        return XMLBuilder.parse(
            new InputSource(new FileReader(xmlFile)), enableExternalEntities);
    }

        return XMLBuilder.parse(inputSource, false);
        return XMLBuilder.parse(xmlString, false);
        return XMLBuilder.parse(xmlFile, false);
     * @param enableExternalEntities
     * enable external entities; beware of XML External Entity (XXE) injection.
    public static XMLBuilder2 create(
        String name, String namespaceURI, boolean enableExternalEntities)
            return new XMLBuilder2(
                createDocumentImpl(name, namespaceURI, enableExternalEntities));
    /**
     * Construct a builder for new XML document. The document will be created
     * with the given root element, and the builder returned by this method
     * will serve as the starting-point for any further document additions.
     *
     * @param name
     * the name of the document's root element.
     * @param enableExternalEntities
     * enable external entities; beware of XML External Entity (XXE) injection.
     * @return
     * a builder node that can be used to add more nodes to the XML document.
     * @throws XMLBuilderRuntimeException
     * to wrap {@link ParserConfigurationException}
     */
    public static XMLBuilder2 create(String name, boolean enableExternalEntities)
    {
        return XMLBuilder2.create(name, null, enableExternalEntities);
    }

    /**
     * Construct a builder for new XML document with a default namespace.
     * The document will be created with the given root element, and the builder
     * returned by this method will serve as the starting-point for any further
     * document additions.
     *
     * @param name
     * the name of the document's root element.
     * @param namespaceURI
     * default namespace URI for document, ignored if null or empty.
     * @return
     * a builder node that can be used to add more nodes to the XML document.
     * @throws XMLBuilderRuntimeException
     * to wrap {@link ParserConfigurationException}
     */
    public static XMLBuilder2 create(String name, String namespaceURI)
    {
        return XMLBuilder2.create(name, namespaceURI, false);
    }

        return XMLBuilder2.create(name, null, false);
     * @param enableExternalEntities
     * enable external entities; beware of XML External Entity (XXE) injection.
    public static XMLBuilder2 parse(
        InputSource inputSource, boolean enableExternalEntities)
            return new XMLBuilder2(
                parseDocumentImpl(inputSource, enableExternalEntities));
     * @param enableExternalEntities
     * enable external entities; beware of XML External Entity (XXE) injection.
    public static XMLBuilder2 parse(
        String xmlString, boolean enableExternalEntities)
        return XMLBuilder2.parse(
            new InputSource(new StringReader(xmlString)),
            enableExternalEntities);
     * @param enableExternalEntities
     * enable external entities; beware of XML External Entity (XXE) injection.
    public static XMLBuilder2 parse(File xmlFile, boolean enableExternalEntities)
            return XMLBuilder2.parse(
                new InputSource(new FileReader(xmlFile)),
                enableExternalEntities);
    /**
     * Construct a builder from an existing XML document. The provided XML
     * document will be parsed and an XMLBuilder2 object referencing the
     * document's root element will be returned.
     *
     * @param inputSource
     * an XML document input source that will be parsed into a DOM.
     * @return
     * a builder node that can be used to add more nodes to the XML document.
     * @throws XMLBuilderRuntimeException
     * to wrap {@link ParserConfigurationException}, {@link SAXException},
     * {@link IOException}
     */
    public static XMLBuilder2 parse(InputSource inputSource)
    {
        return XMLBuilder2.parse(inputSource, false);
    }

    /**
     * Construct a builder from an existing XML document string.
     * The provided XML document will be parsed and an XMLBuilder2
     * object referencing the document's root element will be returned.
     *
     * @param xmlString
     * an XML document string that will be parsed into a DOM.
     * @return
     * a builder node that can be used to add more nodes to the XML document.
     */
    public static XMLBuilder2 parse(String xmlString)
    {
        return XMLBuilder2.parse(xmlString, false);
    }

    /**
     * Construct a builder from an existing XML document file.
     * The provided XML document will be parsed and an XMLBuilder2
     * object referencing the document's root element will be returned.
     *
     * @param xmlFile
     * an XML document file that will be parsed into a DOM.
     * @return
     * a builder node that can be used to add more nodes to the XML document.
     * @throws XMLBuilderRuntimeException
     * to wrap {@link ParserConfigurationException}, {@link SAXException},
     * {@link IOException}, {@link FileNotFoundException}
     */
    public static XMLBuilder2 parse(File xmlFile)
    {
        return XMLBuilder2.parse(xmlFile, false);
    }

import javax.annotation.Resource;
    protected BaseXMLBuilder XMLBuilder_parse(
        String documentString, boolean enableExternalEntities) throws Exception
    {
        return (BaseXMLBuilder) XMLBuilderToTest().getMethod(
            "parse", String.class, boolean.class).invoke(
                null, documentString, enableExternalEntities);
    }

    /**
     * Ensure XML Builder parse methods use a default configuration that
     * prevents XML External Entity (XXE) injection attacks.
     *
     * @throws Exception
     */
    public void testXMLBuilderParserImmuneToXXEAttackByDefault() throws Exception {
        String externalFilePath = "src/test/java/com/jamesmurty/utils/external.txt";
        File externalFile = new File(externalFilePath);
        String XML_DOC_WITH_XXE =
            "<?xml version=\"1.0\"?>" +
            "<!DOCTYPE Projects [" +
                "<!ELEMENT JetS3t ANY>" +
                "<!ENTITY xx1 SYSTEM \"" + externalFile.toURI() + "\"> ]>" +
            EXAMPLE_XML_DOC_START + "&xx1;" + EXAMPLE_XML_DOC_END;
        // By default, builder is immune from XXE injection
        BaseXMLBuilder builder = XMLBuilder_parse(XML_DOC_WITH_XXE);
        String parsedXml = builder.asString();
        assertFalse(parsedXml.indexOf("Injected XXE Data") >= 0);
        // If you enable external entity processing, builder becomes subject to XXE injection
        builder = XMLBuilder_parse(XML_DOC_WITH_XXE, true);
        parsedXml = builder.asString();
        assertTrue(parsedXml.indexOf("Injected XXE Data") >= 0);
    }

Injected XXE Data