Search code examples
javaxmlsonarqubestaxsonarlint

How to be Sonar compliant with the XMLInputFactory and woodstox library registered implementation?


I'm trying to be compliant with the following Sonar blocker rule :

XML parsers should not be vulnerable to XXE attacks (java:S2755)

XML specification allows the use of entities that can be internal or external (file system / network access ...) which could lead to vulnerabilities such as confidential file disclosures or SSRFs.

In the Sonar rule description, they give an example of how to be compliant :

XMLInputFactory factory = XMLInputFactory.newInstance();
factory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, ""); // Compliant
factory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");  // compliant

My issue is that in the classpath of the application, there are 2 libraries woodstox-core-asl and wstx-asl that register their own implementation com.ctc.wstx.stax.WstxInputFactory in /META-INF/services/javax.xml.stream.XMLInputFactory. This implementation com.ctc.wstx.stax.WstxInputFactory does not support accessExternalDTD so it fails with the following error message :

Unrecognized property 'http://javax.xml.XMLConstants/property/accessExternalDTD'

I tried successfully to do directly a new com.sun.xml.internal.stream.XMLInputFactoryImpl() or Class.forName("com.sun.xml.internal.stream.XMLInputFactoryImpl").newInstance() but then it's just getting rid of a warning for another, i.e. the one on the restricted APIs.

Is there a good solution for this ?

Below a minimal reproducible example with the workaround as commented line :

import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;

import javax.xml.XMLConstants;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.util.StreamReaderDelegate;

public class XMLReader extends StreamReaderDelegate implements AutoCloseable {
    
    private final Reader reader;

    public XMLReader(Reader reader) throws XMLStreamException, InstantiationException, IllegalAccessException, ClassNotFoundException {
        this.reader = reader;
        //XMLInputFactory factory = (XMLInputFactory) Class.forName("com.sun.xml.internal.stream.XMLInputFactoryImpl").newInstance();
        XMLInputFactory factory = XMLInputFactory.newInstance();
        factory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
        factory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
        setParent(factory.createXMLStreamReader(reader));
    }

    @Override
    public void close() throws XMLStreamException {
        try {
            super.close();
            reader.close();
        } catch (IOException e) {
            throw new XMLStreamException(e.getMessage(), e);
        }
    }

    public static void main(String[] args) throws XMLStreamException, InstantiationException, IllegalAccessException, ClassNotFoundException {
        try (XMLReader xmlReader = new XMLReader(new StringReader("</test>"))) {

        }
    }
}

Also you will find below the dependencies listed in the following Maven POM file :

<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>test-xml</groupId>
    <artifactId>test-xml</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>org.codehaus.woodstox</groupId>
            <artifactId>wstx-asl</artifactId>
            <version>3.2.8</version>
        </dependency>
        <dependency>
            <groupId>org.codehaus.woodstox</groupId>
            <artifactId>woodstox-core-asl</artifactId>
            <version>4.4.1</version>
        </dependency>
    </dependencies>
</project> 

Solution

  • Apparently, eventhough it's not in the rule description, Sonar also recognizes the property XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES which is the Stax standard property that does the same:

    factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);

    See also :

    https://github.com/FasterXML/woodstox/issues/50