VYPR
Moderate severityNVD Advisory· Published May 29, 2015· Updated May 6, 2026

CVE-2015-1833

CVE-2015-1833

Description

XML external entity (XXE) vulnerability in Apache Jackrabbit before 2.0.6, 2.2.x before 2.2.14, 2.4.x before 2.4.6, 2.6.x before 2.6.6, 2.8.x before 2.8.1, and 2.10.x before 2.10.1 allows remote attackers to read arbitrary files and send requests to intranet servers via a crafted WebDAV request.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
org.apache.jackrabbit:jackrabbit-coreMaven
< 2.0.62.0.6
org.apache.jackrabbit:jackrabbit-coreMaven
>= 2.2.0, < 2.2.142.2.14
org.apache.jackrabbit:jackrabbit-coreMaven
>= 2.4.0, < 2.4.62.4.6
org.apache.jackrabbit:jackrabbit-coreMaven
>= 2.6.0, < 2.6.62.6.6
org.apache.jackrabbit:jackrabbit-coreMaven
>= 2.8.0, < 2.8.12.8.1
org.apache.jackrabbit:jackrabbit-coreMaven
>= 2.10.0, < 2.10.12.10.1

Affected products

27
  • Apache/Jackrabbit27 versions
    cpe:2.3:a:apache:jackrabbit:2.6.4:*:*:*:*:*:*:*+ 26 more
    • cpe:2.3:a:apache:jackrabbit:2.6.4:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:jackrabbit:2.6.5:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:jackrabbit:2.8.0:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:jackrabbit:2.10.0:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:jackrabbit:*:*:*:*:*:*:*:*range: <=2.0.5
    • cpe:2.3:a:apache:jackrabbit:2.2.0:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:jackrabbit:2.2.1:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:jackrabbit:2.2.2:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:jackrabbit:2.2.4:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:jackrabbit:2.2.5:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:jackrabbit:2.2.7:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:jackrabbit:2.2.8:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:jackrabbit:2.2.9:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:jackrabbit:2.2.10:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:jackrabbit:2.2.11:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:jackrabbit:2.2.12:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:jackrabbit:2.2.13:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:jackrabbit:2.4.0:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:jackrabbit:2.4.1:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:jackrabbit:2.4.2:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:jackrabbit:2.4.3:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:jackrabbit:2.4.4:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:jackrabbit:2.4.5:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:jackrabbit:2.6.0:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:jackrabbit:2.6.1:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:jackrabbit:2.6.2:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:jackrabbit:2.6.3:*:*:*:*:*:*:*

Patches

7
b7fa1ae39641

JCR-3883: Jackrabbit WebDAV bundle susceptible to XXE/XEE attack (CVE-2015-1833) - add missing test classed (ported to 2.0)

https://github.com/apache/jackrabbitJulian ReschkeMay 21, 2015via ghsa
2 files changed · +117 0
  • jackrabbit-webdav/src/test/java/org/apache/jackrabbit/webdav/xml/ParserTest.java+78 0 added
    @@ -0,0 +1,78 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You under the Apache License, Version 2.0
    + * (the \"License\"); you may not use this file except in compliance with
    + * the License.  You may obtain a copy of the License at
    + *
    + *      http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an \"AS IS\" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.jackrabbit.webdav.xml;
    +
    +import java.io.ByteArrayInputStream;
    +import java.io.File;
    +import java.io.FileOutputStream;
    +import java.io.IOException;
    +import java.io.InputStream;
    +import java.io.OutputStream;
    +import java.io.UnsupportedEncodingException;
    +
    +import junit.framework.TestCase;
    +
    +import org.w3c.dom.Document;
    +import org.w3c.dom.Element;
    +
    +public class ParserTest extends TestCase {
    +
    +    // see <http://en.wikipedia.org/wiki/Billion_laughs#Details>
    +    public void testBillionLaughs() throws UnsupportedEncodingException {
    +
    +        String testBody = "<?xml version=\"1.0\"?>" + "<!DOCTYPE lolz [" + " <!ENTITY lol \"lol\">" + " <!ELEMENT lolz (#PCDATA)>"
    +                + " <!ENTITY lol1 \"&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;\">"
    +                + " <!ENTITY lol2 \"&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;\">"
    +                + " <!ENTITY lol3 \"&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;\">"
    +                + " <!ENTITY lol4 \"&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;\">"
    +                + " <!ENTITY lol5 \"&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;\">"
    +                + " <!ENTITY lol6 \"&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;\">"
    +                + " <!ENTITY lol7 \"&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;\">"
    +                + " <!ENTITY lol8 \"&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;\">"
    +                + " <!ENTITY lol9 \"&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;\">" + "]>" + "<lolz>&lol9;</lolz>";
    +        InputStream is = new ByteArrayInputStream(testBody.getBytes("UTF-8"));
    +
    +        try {
    +            DomUtil.BUILDER_FACTORY.newDocumentBuilder().parse(is);
    +            fail("parsing this document should cause an exception");
    +        } catch (Exception expected) {
    +        }
    +    }
    +
    +    public void testExternalEntities() throws IOException {
    +
    +        String dname = "target";
    +        String fname = "test.xml";
    +
    +        File f = new File(dname, fname);
    +        OutputStream os = new FileOutputStream(f);
    +        os.write("testdata".getBytes());
    +        os.close();
    +
    +        String testBody = "<?xml version='1.0'?>\n<!DOCTYPE foo [" + " <!ENTITY test SYSTEM \"file:" + dname + "/" + fname + "\">"
    +                + "]>\n<foo>&test;</foo>";
    +        InputStream is = new ByteArrayInputStream(testBody.getBytes("UTF-8"));
    +
    +        try {
    +            Document d = DomUtil.BUILDER_FACTORY.newDocumentBuilder().parse(is);
    +            Element root = d.getDocumentElement();
    +            String text = DomUtil.getText(root);
    +            fail("parsing this document should cause an exception, but the following external content was included: " + text);
    +        } catch (Exception expected) {
    +        }
    +    }
    +}
    \ No newline at end of file
    
  • jackrabbit-webdav/src/test/java/org/apache/jackrabbit/webdav/xml/TestAll.java+39 0 added
    @@ -0,0 +1,39 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You under the Apache License, Version 2.0
    + * (the "License"); you may not use this file except in compliance with
    + * the License.  You may obtain a copy of the License at
    + *
    + *      http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.jackrabbit.webdav.xml;
    +
    +import junit.framework.Test;
    +import junit.framework.TestCase;
    +import junit.framework.TestSuite;
    +
    +/**
    + * Test suite that includes all testcases for package org.apache.jackrabbit.webdav.xml.
    + */
    +public class TestAll extends TestCase {
    +
    +    /**
    +     * Returns a <code>Test</code> suite that executes all tests inside this
    +     * package.
    +     */
    +    public static Test suite() {
    +        TestSuite suite = new TestSuite("org.apache.jackrabbit.webdav.xml tests");
    +
    +        suite.addTestSuite(ParserTest.class);
    +
    +        return suite;
    +    }
    +}
    
26e601934d0f

JCR-3883: Jackrabbit WebDAV bundle susceptible to XXE/XEE attack (CVE-2015-1833) (ported to 2.0)

https://github.com/apache/jackrabbitJulian ReschkeMay 21, 2015via ghsa
3 files changed · +91 10
  • jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/DavMethodBase.java+3 1 modified
    @@ -27,6 +27,7 @@
     import org.apache.jackrabbit.webdav.DavServletResponse;
     import org.apache.jackrabbit.webdav.MultiStatus;
     import org.apache.jackrabbit.webdav.header.Header;
    +import org.apache.jackrabbit.webdav.xml.DavDocumentBuilderFactory;
     import org.apache.jackrabbit.webdav.xml.XmlSerializable;
     import org.apache.jackrabbit.webdav.xml.DomUtil;
     import org.slf4j.Logger;
    @@ -39,6 +40,7 @@
     import javax.xml.parsers.DocumentBuilder;
     import javax.xml.parsers.DocumentBuilderFactory;
     import javax.xml.parsers.ParserConfigurationException;
    +
     import java.io.IOException;
     import java.io.InputStream;
     
    @@ -49,7 +51,7 @@ public abstract class DavMethodBase extends EntityEnclosingMethod implements Dav
     
         private static Logger log = LoggerFactory.getLogger(DavMethodBase.class);
     
    -    static final DocumentBuilderFactory BUILDER_FACTORY = DomUtil.BUILDER_FACTORY;
    +    static final DavDocumentBuilderFactory BUILDER_FACTORY = DomUtil.BUILDER_FACTORY;
     
         private boolean success;
         private Document responseDocument;
    
  • jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/xml/DavDocumentBuilderFactory.java+86 0 added
    @@ -0,0 +1,86 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You under the Apache License, Version 2.0
    + * (the "License"); you may not use this file except in compliance with
    + * the License.  You may obtain a copy of the License at
    + *
    + *      http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.jackrabbit.webdav.xml;
    +
    +import java.io.IOException;
    +
    +import javax.xml.XMLConstants;
    +import javax.xml.parsers.DocumentBuilder;
    +import javax.xml.parsers.DocumentBuilderFactory;
    +import javax.xml.parsers.ParserConfigurationException;
    +
    +import org.slf4j.Logger;
    +import org.slf4j.LoggerFactory;
    +import org.xml.sax.EntityResolver;
    +import org.xml.sax.InputSource;
    +import org.xml.sax.helpers.DefaultHandler;
    +
    +/**
    + * Custom {@link DocumentBuilderFactory} extended for use in WebDAV.
    + */
    +public class DavDocumentBuilderFactory {
    +
    +    private static final Logger LOG = LoggerFactory.getLogger(DomUtil.class);
    +
    +    private final DocumentBuilderFactory DEFAULT_FACTORY = createFactory();
    +
    +    private DocumentBuilderFactory BUILDER_FACTORY = DEFAULT_FACTORY;
    +
    +    private DocumentBuilderFactory createFactory() {
    +        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    +        factory.setNamespaceAware(true);
    +        factory.setIgnoringComments(true);
    +        factory.setIgnoringElementContentWhitespace(true);
    +        factory.setCoalescing(true);
    +        try {
    +            factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
    +        } catch (ParserConfigurationException e) {
    +            LOG.warn("Secure XML processing is not supported", e);
    +        } catch (AbstractMethodError e) {
    +            LOG.warn("Secure XML processing is not supported", e);
    +        }
    +        return factory;
    +    }
    +
    +    public void setFactory(DocumentBuilderFactory documentBuilderFactory) {
    +        LOG.debug("DocumentBuilderFactory changed to: " + documentBuilderFactory);
    +        BUILDER_FACTORY = documentBuilderFactory != null ? documentBuilderFactory : DEFAULT_FACTORY;
    +    }
    +
    +    /**
    +     * An entity resolver that does not allow external entity resolution. See
    +     * RFC 4918, Section 20.6
    +     */
    +    private static final EntityResolver DEFAULT_ENTITY_RESOLVER = new EntityResolver() {
    +        public InputSource resolveEntity(String publicId, String systemId) throws IOException {
    +            LOG.debug("Resolution of external entities in XML payload not supported - publicId: " + publicId + ", systemId: "
    +                    + systemId);
    +            throw new IOException("This parser does not support resolution of external entities (publicId: " + publicId
    +                    + ", systemId: " + systemId + ")");
    +        }
    +    };
    +
    +    public DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {
    +        DocumentBuilder db = BUILDER_FACTORY.newDocumentBuilder();
    +        if (BUILDER_FACTORY == DEFAULT_FACTORY) {
    +            // if this is the default factory: set the default entity resolver as well
    +            db.setEntityResolver(DEFAULT_ENTITY_RESOLVER);
    +        }
    +        db.setErrorHandler(new DefaultHandler());
    +        return db;
    +    }
    +}
    
  • jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/xml/DomUtil.java+2 9 modified
    @@ -28,7 +28,6 @@
     import org.w3c.dom.Text;
     import org.w3c.dom.NamedNodeMap;
     
    -import javax.xml.parsers.DocumentBuilderFactory;
     import java.util.ArrayList;
     import java.util.List;
     
    @@ -40,16 +39,10 @@ public class DomUtil {
         private static Logger log = LoggerFactory.getLogger(DomUtil.class);
     
         /**
    -     * Constant for <code>DocumentBuilderFactory</code> which is used
    +     * Constant for <code>DavDocumentBuilderFactory</code> which is used
          * widely to create new <code>Document</code>s
          */
    -    public static DocumentBuilderFactory BUILDER_FACTORY = DocumentBuilderFactory.newInstance();
    -    static {
    -        BUILDER_FACTORY.setNamespaceAware(true);
    -        BUILDER_FACTORY.setIgnoringComments(true);
    -        BUILDER_FACTORY.setIgnoringElementContentWhitespace(true);
    -        BUILDER_FACTORY.setCoalescing(true);
    -    }
    +    public static DavDocumentBuilderFactory BUILDER_FACTORY = new DavDocumentBuilderFactory();
     
         /**
          * Returns the value of the named attribute of the current element.
    
6191b366c607

JCR-3883: Jackrabbit WebDAV bundle susceptible to XXE/XEE attack (CVE-2015-1833) (ported to 2.2)

https://github.com/apache/jackrabbitJulian ReschkeMay 21, 2015via ghsa
4 files changed · +168 20
  • jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/xml/DavDocumentBuilderFactory.java+86 0 added
    @@ -0,0 +1,86 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You under the Apache License, Version 2.0
    + * (the "License"); you may not use this file except in compliance with
    + * the License.  You may obtain a copy of the License at
    + *
    + *      http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.jackrabbit.webdav.xml;
    +
    +import java.io.IOException;
    +
    +import javax.xml.XMLConstants;
    +import javax.xml.parsers.DocumentBuilder;
    +import javax.xml.parsers.DocumentBuilderFactory;
    +import javax.xml.parsers.ParserConfigurationException;
    +
    +import org.slf4j.Logger;
    +import org.slf4j.LoggerFactory;
    +import org.xml.sax.EntityResolver;
    +import org.xml.sax.InputSource;
    +import org.xml.sax.helpers.DefaultHandler;
    +
    +/**
    + * Custom {@link DocumentBuilderFactory} extended for use in WebDAV.
    + */
    +public class DavDocumentBuilderFactory {
    +
    +    private static final Logger LOG = LoggerFactory.getLogger(DomUtil.class);
    +
    +    private final DocumentBuilderFactory DEFAULT_FACTORY = createFactory();
    +
    +    private DocumentBuilderFactory BUILDER_FACTORY = DEFAULT_FACTORY;
    +
    +    private DocumentBuilderFactory createFactory() {
    +        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    +        factory.setNamespaceAware(true);
    +        factory.setIgnoringComments(true);
    +        factory.setIgnoringElementContentWhitespace(true);
    +        factory.setCoalescing(true);
    +        try {
    +            factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
    +        } catch (ParserConfigurationException e) {
    +            LOG.warn("Secure XML processing is not supported", e);
    +        } catch (AbstractMethodError e) {
    +            LOG.warn("Secure XML processing is not supported", e);
    +        }
    +        return factory;
    +    }
    +
    +    public void setFactory(DocumentBuilderFactory documentBuilderFactory) {
    +        LOG.debug("DocumentBuilderFactory changed to: " + documentBuilderFactory);
    +        BUILDER_FACTORY = documentBuilderFactory != null ? documentBuilderFactory : DEFAULT_FACTORY;
    +    }
    +
    +    /**
    +     * An entity resolver that does not allow external entity resolution. See
    +     * RFC 4918, Section 20.6
    +     */
    +    private static final EntityResolver DEFAULT_ENTITY_RESOLVER = new EntityResolver() {
    +        public InputSource resolveEntity(String publicId, String systemId) throws IOException {
    +            LOG.debug("Resolution of external entities in XML payload not supported - publicId: " + publicId + ", systemId: "
    +                    + systemId);
    +            throw new IOException("This parser does not support resolution of external entities (publicId: " + publicId
    +                    + ", systemId: " + systemId + ")");
    +        }
    +    };
    +
    +    public DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {
    +        DocumentBuilder db = BUILDER_FACTORY.newDocumentBuilder();
    +        if (BUILDER_FACTORY == DEFAULT_FACTORY) {
    +            // if this is the default factory: set the default entity resolver as well
    +            db.setEntityResolver(DEFAULT_ENTITY_RESOLVER);
    +        }
    +        db.setErrorHandler(new DefaultHandler());
    +        return db;
    +    }
    +}
    
  • jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/xml/DomUtil.java+3 20 modified
    @@ -30,7 +30,6 @@
     import org.xml.sax.SAXException;
     import org.xml.sax.helpers.DefaultHandler;
     
    -import javax.xml.XMLConstants;
     import javax.xml.parsers.DocumentBuilder;
     import javax.xml.parsers.DocumentBuilderFactory;
     import javax.xml.parsers.ParserConfigurationException;
    @@ -48,26 +47,10 @@ public class DomUtil {
         private static Logger log = LoggerFactory.getLogger(DomUtil.class);
     
         /**
    -     * Constant for <code>DocumentBuilderFactory</code> which is used
    +     * Constant for <code>DavDocumentBuilderFactory</code> which is used
          * to create and parse DOM documents.
          */
    -    private static DocumentBuilderFactory BUILDER_FACTORY = createFactory();
    -
    -    private static DocumentBuilderFactory createFactory() {
    -        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    -        factory.setNamespaceAware(true);
    -        factory.setIgnoringComments(true);
    -        factory.setIgnoringElementContentWhitespace(true);
    -        factory.setCoalescing(true);
    -        try {
    -            factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
    -        } catch (ParserConfigurationException e) {
    -            log.warn("Secure XML processing is not supported", e);
    -        } catch (AbstractMethodError e) {
    -            log.warn("Secure XML processing is not supported", e);
    -        }
    -        return factory;
    -    }
    +    private static DavDocumentBuilderFactory BUILDER_FACTORY = new DavDocumentBuilderFactory();
     
         /**
          * Support the replacement of {@link #BUILDER_FACTORY}. This is useful
    @@ -80,7 +63,7 @@ private static DocumentBuilderFactory createFactory() {
          */
         public static void setBuilderFactory(
                 DocumentBuilderFactory documentBuilderFactory) {
    -        BUILDER_FACTORY = documentBuilderFactory;
    +        BUILDER_FACTORY.setFactory(documentBuilderFactory);
         }
     
         /**
    
  • jackrabbit-webdav/src/test/java/org/apache/jackrabbit/webdav/xml/ParserTest.java+78 0 added
    @@ -0,0 +1,78 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You under the Apache License, Version 2.0
    + * (the \"License\"); you may not use this file except in compliance with
    + * the License.  You may obtain a copy of the License at
    + *
    + *      http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an \"AS IS\" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.jackrabbit.webdav.xml;
    +
    +import java.io.ByteArrayInputStream;
    +import java.io.File;
    +import java.io.FileOutputStream;
    +import java.io.IOException;
    +import java.io.InputStream;
    +import java.io.OutputStream;
    +import java.io.UnsupportedEncodingException;
    +
    +import junit.framework.TestCase;
    +
    +import org.w3c.dom.Document;
    +import org.w3c.dom.Element;
    +
    +public class ParserTest extends TestCase {
    +
    +    // see <http://en.wikipedia.org/wiki/Billion_laughs#Details>
    +    public void testBillionLaughs() throws UnsupportedEncodingException {
    +
    +        String testBody = "<?xml version=\"1.0\"?>" + "<!DOCTYPE lolz [" + " <!ENTITY lol \"lol\">" + " <!ELEMENT lolz (#PCDATA)>"
    +                + " <!ENTITY lol1 \"&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;\">"
    +                + " <!ENTITY lol2 \"&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;\">"
    +                + " <!ENTITY lol3 \"&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;\">"
    +                + " <!ENTITY lol4 \"&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;\">"
    +                + " <!ENTITY lol5 \"&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;\">"
    +                + " <!ENTITY lol6 \"&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;\">"
    +                + " <!ENTITY lol7 \"&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;\">"
    +                + " <!ENTITY lol8 \"&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;\">"
    +                + " <!ENTITY lol9 \"&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;\">" + "]>" + "<lolz>&lol9;</lolz>";
    +        InputStream is = new ByteArrayInputStream(testBody.getBytes("UTF-8"));
    +
    +        try {
    +            DomUtil.parseDocument(is);
    +            fail("parsing this document should cause an exception");
    +        } catch (Exception expected) {
    +        }
    +    }
    +
    +    public void testExternalEntities() throws IOException {
    +
    +        String dname = "target";
    +        String fname = "test.xml";
    +
    +        File f = new File(dname, fname);
    +        OutputStream os = new FileOutputStream(f);
    +        os.write("testdata".getBytes());
    +        os.close();
    +
    +        String testBody = "<?xml version='1.0'?>\n<!DOCTYPE foo [" + " <!ENTITY test SYSTEM \"file:" + dname + "/" + fname + "\">"
    +                + "]>\n<foo>&test;</foo>";
    +        InputStream is = new ByteArrayInputStream(testBody.getBytes("UTF-8"));
    +
    +        try {
    +            Document d = DomUtil.parseDocument(is);
    +            Element root = d.getDocumentElement();
    +            String text = DomUtil.getText(root);
    +            fail("parsing this document should cause an exception, but the following external content was included: " + text);
    +        } catch (Exception expected) {
    +        }
    +    }
    +}
    \ No newline at end of file
    
  • jackrabbit-webdav/src/test/java/org/apache/jackrabbit/webdav/xml/TestAll.java+1 0 modified
    @@ -33,6 +33,7 @@ public static Test suite() {
             TestSuite suite = new TestSuite("org.apache.jackrabbit.webdav.xml tests");
     
             suite.addTestSuite(NamespaceTest.class);
    +        suite.addTestSuite(ParserTest.class);
     
             return suite;
         }
    
17e9f68f5a3f

JCR-3883: Jackrabbit WebDAV bundle susceptible to XXE/XEE attack (CVE-2015-1833) (ported to 2.4)

https://github.com/apache/jackrabbitJulian ReschkeMay 21, 2015via ghsa
4 files changed · +234 26
  • jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/xml/DavDocumentBuilderFactory.java+87 0 added
    @@ -0,0 +1,87 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You under the Apache License, Version 2.0
    + * (the "License"); you may not use this file except in compliance with
    + * the License.  You may obtain a copy of the License at
    + *
    + *      http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.jackrabbit.webdav.xml;
    +
    +import java.io.IOException;
    +
    +import javax.xml.XMLConstants;
    +import javax.xml.parsers.DocumentBuilder;
    +import javax.xml.parsers.DocumentBuilderFactory;
    +import javax.xml.parsers.ParserConfigurationException;
    +
    +import org.slf4j.Logger;
    +import org.slf4j.LoggerFactory;
    +import org.xml.sax.EntityResolver;
    +import org.xml.sax.InputSource;
    +import org.xml.sax.helpers.DefaultHandler;
    +
    +/**
    + * Custom {@link DocumentBuilderFactory} extended for use in WebDAV.
    + */
    +public class DavDocumentBuilderFactory {
    +
    +    private static final Logger LOG = LoggerFactory.getLogger(DomUtil.class);
    +
    +    private final DocumentBuilderFactory DEFAULT_FACTORY = createFactory();
    +
    +    private DocumentBuilderFactory BUILDER_FACTORY = DEFAULT_FACTORY;
    +
    +    private DocumentBuilderFactory createFactory() {
    +        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    +        factory.setNamespaceAware(true);
    +        factory.setIgnoringComments(true);
    +        factory.setIgnoringElementContentWhitespace(true);
    +        factory.setCoalescing(true);
    +        try {
    +            factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
    +        } catch (ParserConfigurationException e) {
    +            LOG.warn("Secure XML processing is not supported", e);
    +        } catch (AbstractMethodError e) {
    +            LOG.warn("Secure XML processing is not supported", e);
    +        }
    +        return factory;
    +    }
    +
    +    public void setFactory(DocumentBuilderFactory documentBuilderFactory) {
    +        LOG.debug("DocumentBuilderFactory changed to: " + documentBuilderFactory);
    +        BUILDER_FACTORY = documentBuilderFactory != null ? documentBuilderFactory : DEFAULT_FACTORY;
    +    }
    +
    +    /**
    +     * An entity resolver that does not allow external entity resolution. See
    +     * RFC 4918, Section 20.6
    +     */
    +    private static final EntityResolver DEFAULT_ENTITY_RESOLVER = new EntityResolver() {
    +        @Override
    +        public InputSource resolveEntity(String publicId, String systemId) throws IOException {
    +            LOG.debug("Resolution of external entities in XML payload not supported - publicId: " + publicId + ", systemId: "
    +                    + systemId);
    +            throw new IOException("This parser does not support resolution of external entities (publicId: " + publicId
    +                    + ", systemId: " + systemId + ")");
    +        }
    +    };
    +
    +    public DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {
    +        DocumentBuilder db = BUILDER_FACTORY.newDocumentBuilder();
    +        if (BUILDER_FACTORY == DEFAULT_FACTORY) {
    +            // if this is the default factory: set the default entity resolver as well
    +            db.setEntityResolver(DEFAULT_ENTITY_RESOLVER);
    +        }
    +        db.setErrorHandler(new DefaultHandler());
    +        return db;
    +    }
    +}
    
  • jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/xml/DomUtil.java+3 26 modified
    @@ -28,9 +28,7 @@
     import org.w3c.dom.Text;
     import org.w3c.dom.NamedNodeMap;
     import org.xml.sax.SAXException;
    -import org.xml.sax.helpers.DefaultHandler;
     
    -import javax.xml.XMLConstants;
     import javax.xml.namespace.QName;
     import javax.xml.parsers.DocumentBuilder;
     import javax.xml.parsers.DocumentBuilderFactory;
    @@ -56,26 +54,10 @@ public class DomUtil {
         private static Logger log = LoggerFactory.getLogger(DomUtil.class);
     
         /**
    -     * Constant for <code>DocumentBuilderFactory</code> which is used
    +     * Constant for <code>DavDocumentBuilderFactory</code> which is used
          * to create and parse DOM documents.
          */
    -    private static DocumentBuilderFactory BUILDER_FACTORY = createFactory();
    -
    -    private static DocumentBuilderFactory createFactory() {
    -        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    -        factory.setNamespaceAware(true);
    -        factory.setIgnoringComments(true);
    -        factory.setIgnoringElementContentWhitespace(true);
    -        factory.setCoalescing(true);
    -        try {
    -            factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
    -        } catch (ParserConfigurationException e) {
    -            log.warn("Secure XML processing is not supported", e);
    -        } catch (AbstractMethodError e) {
    -            log.warn("Secure XML processing is not supported", e);
    -        }
    -        return factory;
    -    }
    +    private static final DavDocumentBuilderFactory BUILDER_FACTORY = new DavDocumentBuilderFactory();
     
         /**
          * Support the replacement of {@link #BUILDER_FACTORY}. This is useful
    @@ -88,7 +70,7 @@ private static DocumentBuilderFactory createFactory() {
          */
         public static void setBuilderFactory(
                 DocumentBuilderFactory documentBuilderFactory) {
    -        BUILDER_FACTORY = documentBuilderFactory;
    +        BUILDER_FACTORY.setFactory(documentBuilderFactory);
         }
     
         /**
    @@ -119,11 +101,6 @@ public static Document createDocument()
         public static Document parseDocument(InputStream stream)
                 throws ParserConfigurationException, SAXException, IOException {
             DocumentBuilder docBuilder = BUILDER_FACTORY.newDocumentBuilder();
    -
    -        // Set an error handler to prevent parsers from printing error messages
    -        // to standard output!
    -        docBuilder.setErrorHandler(new DefaultHandler());
    -
             return docBuilder.parse(stream);
         }
     
    
  • jackrabbit-webdav/src/test/java/org/apache/jackrabbit/webdav/xml/ParserTest.java+143 0 added
    @@ -0,0 +1,143 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You under the Apache License, Version 2.0
    + * (the \"License\"); you may not use this file except in compliance with
    + * the License.  You may obtain a copy of the License at
    + *
    + *      http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an \"AS IS\" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.jackrabbit.webdav.xml;
    +
    +import java.io.ByteArrayInputStream;
    +import java.io.File;
    +import java.io.FileOutputStream;
    +import java.io.IOException;
    +import java.io.InputStream;
    +import java.io.OutputStream;
    +import java.io.UnsupportedEncodingException;
    +
    +import javax.xml.parsers.DocumentBuilder;
    +import javax.xml.parsers.DocumentBuilderFactory;
    +import javax.xml.parsers.ParserConfigurationException;
    +
    +import junit.framework.TestCase;
    +
    +import org.w3c.dom.Document;
    +import org.w3c.dom.Element;
    +import org.xml.sax.EntityResolver;
    +import org.xml.sax.InputSource;
    +import org.xml.sax.SAXException;
    +
    +public class ParserTest extends TestCase {
    +
    +    // see <http://en.wikipedia.org/wiki/Billion_laughs#Details>
    +    public void testBillionLaughs() throws UnsupportedEncodingException {
    +
    +        String testBody = "<?xml version=\"1.0\"?>" + "<!DOCTYPE lolz [" + " <!ENTITY lol \"lol\">" + " <!ELEMENT lolz (#PCDATA)>"
    +                + " <!ENTITY lol1 \"&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;\">"
    +                + " <!ENTITY lol2 \"&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;\">"
    +                + " <!ENTITY lol3 \"&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;\">"
    +                + " <!ENTITY lol4 \"&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;\">"
    +                + " <!ENTITY lol5 \"&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;\">"
    +                + " <!ENTITY lol6 \"&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;\">"
    +                + " <!ENTITY lol7 \"&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;\">"
    +                + " <!ENTITY lol8 \"&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;\">"
    +                + " <!ENTITY lol9 \"&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;\">" + "]>" + "<lolz>&lol9;</lolz>";
    +        InputStream is = new ByteArrayInputStream(testBody.getBytes("UTF-8"));
    +
    +        try {
    +            DomUtil.parseDocument(is);
    +            fail("parsing this document should cause an exception");
    +        } catch (Exception expected) {
    +        }
    +    }
    +
    +    public void testExternalEntities() throws IOException {
    +
    +        String dname = "target";
    +        String fname = "test.xml";
    +
    +        File f = new File(dname, fname);
    +        OutputStream os = new FileOutputStream(f);
    +        os.write("testdata".getBytes());
    +        os.close();
    +
    +        String testBody = "<?xml version='1.0'?>\n<!DOCTYPE foo [" + " <!ENTITY test SYSTEM \"file:" + dname + "/" + fname + "\">"
    +                + "]>\n<foo>&test;</foo>";
    +        InputStream is = new ByteArrayInputStream(testBody.getBytes("UTF-8"));
    +
    +        try {
    +            Document d = DomUtil.parseDocument(is);
    +            Element root = d.getDocumentElement();
    +            String text = DomUtil.getText(root);
    +            fail("parsing this document should cause an exception, but the following external content was included: " + text);
    +        } catch (Exception expected) {
    +        }
    +    }
    +
    +    public void testCustomEntityResolver() throws ParserConfigurationException, SAXException, IOException {
    +
    +        try {
    +            DocumentBuilderFactory dbf = new DocumentBuilderFactory() {
    +
    +                DocumentBuilderFactory def = DocumentBuilderFactory.newInstance();
    +
    +                @Override
    +                public void setFeature(String name, boolean value) throws ParserConfigurationException {
    +                    def.setFeature(name, value);
    +                }
    +
    +                @Override
    +                public void setAttribute(String name, Object value) throws IllegalArgumentException {
    +                    def.setAttribute(name, value);
    +                }
    +
    +                @Override
    +                public DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {
    +                    DocumentBuilder db = def.newDocumentBuilder();
    +                    db.setEntityResolver(new EntityResolver() {
    +                        @Override
    +                        public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
    +                            if ("foo:test".equals(systemId)) {
    +                                return new InputSource(new ByteArrayInputStream("foo&amp;bar".getBytes("UTF-8")));
    +                            } else {
    +                                return null;
    +                            }
    +                        }
    +                    });
    +                    return db;
    +                }
    +
    +                @Override
    +                public boolean getFeature(String name) throws ParserConfigurationException {
    +                    return def.getFeature(name);
    +                }
    +
    +                @Override
    +                public Object getAttribute(String name) throws IllegalArgumentException {
    +                    return def.getAttribute(name);
    +                }
    +            };
    +
    +            DomUtil.setBuilderFactory(dbf);
    +            String testBody = "<?xml version='1.0'?>\n<!DOCTYPE foo [" + " <!ENTITY test SYSTEM \"foo:test\">"
    +                    + "]>\n<foo>&test;</foo>";
    +            InputStream is = new ByteArrayInputStream(testBody.getBytes("UTF-8"));
    +
    +            Document d = DomUtil.parseDocument(is);
    +            Element root = d.getDocumentElement();
    +            String text = DomUtil.getText(root);
    +            assertEquals("custom entity resolver apparently not called", "foo&bar", text);
    +        } finally {
    +            DomUtil.setBuilderFactory(null);
    +        }
    +    }
    +}
    \ No newline at end of file
    
  • jackrabbit-webdav/src/test/java/org/apache/jackrabbit/webdav/xml/TestAll.java+1 0 modified
    @@ -33,6 +33,7 @@ public static Test suite() {
             TestSuite suite = new TestSuite("org.apache.jackrabbit.webdav.xml tests");
     
             suite.addTestSuite(NamespaceTest.class);
    +        suite.addTestSuite(ParserTest.class);
     
             return suite;
         }
    
89c5c4ed6ab2

JCR-3883: Jackrabbit WebDAV bundle susceptible to XXE/XEE attack (CVE-2015-1833) (ported to 2.6)

https://github.com/apache/jackrabbitJulian ReschkeMay 21, 2015via ghsa
4 files changed · +234 26
  • jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/xml/DavDocumentBuilderFactory.java+87 0 added
    @@ -0,0 +1,87 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You under the Apache License, Version 2.0
    + * (the "License"); you may not use this file except in compliance with
    + * the License.  You may obtain a copy of the License at
    + *
    + *      http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.jackrabbit.webdav.xml;
    +
    +import java.io.IOException;
    +
    +import javax.xml.XMLConstants;
    +import javax.xml.parsers.DocumentBuilder;
    +import javax.xml.parsers.DocumentBuilderFactory;
    +import javax.xml.parsers.ParserConfigurationException;
    +
    +import org.slf4j.Logger;
    +import org.slf4j.LoggerFactory;
    +import org.xml.sax.EntityResolver;
    +import org.xml.sax.InputSource;
    +import org.xml.sax.helpers.DefaultHandler;
    +
    +/**
    + * Custom {@link DocumentBuilderFactory} extended for use in WebDAV.
    + */
    +public class DavDocumentBuilderFactory {
    +
    +    private static final Logger LOG = LoggerFactory.getLogger(DomUtil.class);
    +
    +    private final DocumentBuilderFactory DEFAULT_FACTORY = createFactory();
    +
    +    private DocumentBuilderFactory BUILDER_FACTORY = DEFAULT_FACTORY;
    +
    +    private DocumentBuilderFactory createFactory() {
    +        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    +        factory.setNamespaceAware(true);
    +        factory.setIgnoringComments(true);
    +        factory.setIgnoringElementContentWhitespace(true);
    +        factory.setCoalescing(true);
    +        try {
    +            factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
    +        } catch (ParserConfigurationException e) {
    +            LOG.warn("Secure XML processing is not supported", e);
    +        } catch (AbstractMethodError e) {
    +            LOG.warn("Secure XML processing is not supported", e);
    +        }
    +        return factory;
    +    }
    +
    +    public void setFactory(DocumentBuilderFactory documentBuilderFactory) {
    +        LOG.debug("DocumentBuilderFactory changed to: " + documentBuilderFactory);
    +        BUILDER_FACTORY = documentBuilderFactory != null ? documentBuilderFactory : DEFAULT_FACTORY;
    +    }
    +
    +    /**
    +     * An entity resolver that does not allow external entity resolution. See
    +     * RFC 4918, Section 20.6
    +     */
    +    private static final EntityResolver DEFAULT_ENTITY_RESOLVER = new EntityResolver() {
    +        @Override
    +        public InputSource resolveEntity(String publicId, String systemId) throws IOException {
    +            LOG.debug("Resolution of external entities in XML payload not supported - publicId: " + publicId + ", systemId: "
    +                    + systemId);
    +            throw new IOException("This parser does not support resolution of external entities (publicId: " + publicId
    +                    + ", systemId: " + systemId + ")");
    +        }
    +    };
    +
    +    public DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {
    +        DocumentBuilder db = BUILDER_FACTORY.newDocumentBuilder();
    +        if (BUILDER_FACTORY == DEFAULT_FACTORY) {
    +            // if this is the default factory: set the default entity resolver as well
    +            db.setEntityResolver(DEFAULT_ENTITY_RESOLVER);
    +        }
    +        db.setErrorHandler(new DefaultHandler());
    +        return db;
    +    }
    +}
    
  • jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/xml/DomUtil.java+3 26 modified
    @@ -28,9 +28,7 @@
     import org.w3c.dom.Text;
     import org.w3c.dom.NamedNodeMap;
     import org.xml.sax.SAXException;
    -import org.xml.sax.helpers.DefaultHandler;
     
    -import javax.xml.XMLConstants;
     import javax.xml.namespace.QName;
     import javax.xml.parsers.DocumentBuilder;
     import javax.xml.parsers.DocumentBuilderFactory;
    @@ -56,26 +54,10 @@ public class DomUtil {
         private static Logger log = LoggerFactory.getLogger(DomUtil.class);
     
         /**
    -     * Constant for <code>DocumentBuilderFactory</code> which is used
    +     * Constant for <code>DavDocumentBuilderFactory</code> which is used
          * to create and parse DOM documents.
          */
    -    private static DocumentBuilderFactory BUILDER_FACTORY = createFactory();
    -
    -    private static DocumentBuilderFactory createFactory() {
    -        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    -        factory.setNamespaceAware(true);
    -        factory.setIgnoringComments(true);
    -        factory.setIgnoringElementContentWhitespace(true);
    -        factory.setCoalescing(true);
    -        try {
    -            factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
    -        } catch (ParserConfigurationException e) {
    -            log.warn("Secure XML processing is not supported", e);
    -        } catch (AbstractMethodError e) {
    -            log.warn("Secure XML processing is not supported", e);
    -        }
    -        return factory;
    -    }
    +    private static final DavDocumentBuilderFactory BUILDER_FACTORY = new DavDocumentBuilderFactory();
     
         /**
          * Support the replacement of {@link #BUILDER_FACTORY}. This is useful
    @@ -88,7 +70,7 @@ private static DocumentBuilderFactory createFactory() {
          */
         public static void setBuilderFactory(
                 DocumentBuilderFactory documentBuilderFactory) {
    -        BUILDER_FACTORY = documentBuilderFactory;
    +        BUILDER_FACTORY.setFactory(documentBuilderFactory);
         }
     
         /**
    @@ -119,11 +101,6 @@ public static Document createDocument()
         public static Document parseDocument(InputStream stream)
                 throws ParserConfigurationException, SAXException, IOException {
             DocumentBuilder docBuilder = BUILDER_FACTORY.newDocumentBuilder();
    -
    -        // Set an error handler to prevent parsers from printing error messages
    -        // to standard output!
    -        docBuilder.setErrorHandler(new DefaultHandler());
    -
             return docBuilder.parse(stream);
         }
     
    
  • jackrabbit-webdav/src/test/java/org/apache/jackrabbit/webdav/xml/ParserTest.java+143 0 added
    @@ -0,0 +1,143 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You under the Apache License, Version 2.0
    + * (the \"License\"); you may not use this file except in compliance with
    + * the License.  You may obtain a copy of the License at
    + *
    + *      http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an \"AS IS\" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.jackrabbit.webdav.xml;
    +
    +import java.io.ByteArrayInputStream;
    +import java.io.File;
    +import java.io.FileOutputStream;
    +import java.io.IOException;
    +import java.io.InputStream;
    +import java.io.OutputStream;
    +import java.io.UnsupportedEncodingException;
    +
    +import javax.xml.parsers.DocumentBuilder;
    +import javax.xml.parsers.DocumentBuilderFactory;
    +import javax.xml.parsers.ParserConfigurationException;
    +
    +import junit.framework.TestCase;
    +
    +import org.w3c.dom.Document;
    +import org.w3c.dom.Element;
    +import org.xml.sax.EntityResolver;
    +import org.xml.sax.InputSource;
    +import org.xml.sax.SAXException;
    +
    +public class ParserTest extends TestCase {
    +
    +    // see <http://en.wikipedia.org/wiki/Billion_laughs#Details>
    +    public void testBillionLaughs() throws UnsupportedEncodingException {
    +
    +        String testBody = "<?xml version=\"1.0\"?>" + "<!DOCTYPE lolz [" + " <!ENTITY lol \"lol\">" + " <!ELEMENT lolz (#PCDATA)>"
    +                + " <!ENTITY lol1 \"&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;\">"
    +                + " <!ENTITY lol2 \"&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;\">"
    +                + " <!ENTITY lol3 \"&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;\">"
    +                + " <!ENTITY lol4 \"&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;\">"
    +                + " <!ENTITY lol5 \"&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;\">"
    +                + " <!ENTITY lol6 \"&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;\">"
    +                + " <!ENTITY lol7 \"&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;\">"
    +                + " <!ENTITY lol8 \"&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;\">"
    +                + " <!ENTITY lol9 \"&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;\">" + "]>" + "<lolz>&lol9;</lolz>";
    +        InputStream is = new ByteArrayInputStream(testBody.getBytes("UTF-8"));
    +
    +        try {
    +            DomUtil.parseDocument(is);
    +            fail("parsing this document should cause an exception");
    +        } catch (Exception expected) {
    +        }
    +    }
    +
    +    public void testExternalEntities() throws IOException {
    +
    +        String dname = "target";
    +        String fname = "test.xml";
    +
    +        File f = new File(dname, fname);
    +        OutputStream os = new FileOutputStream(f);
    +        os.write("testdata".getBytes());
    +        os.close();
    +
    +        String testBody = "<?xml version='1.0'?>\n<!DOCTYPE foo [" + " <!ENTITY test SYSTEM \"file:" + dname + "/" + fname + "\">"
    +                + "]>\n<foo>&test;</foo>";
    +        InputStream is = new ByteArrayInputStream(testBody.getBytes("UTF-8"));
    +
    +        try {
    +            Document d = DomUtil.parseDocument(is);
    +            Element root = d.getDocumentElement();
    +            String text = DomUtil.getText(root);
    +            fail("parsing this document should cause an exception, but the following external content was included: " + text);
    +        } catch (Exception expected) {
    +        }
    +    }
    +
    +    public void testCustomEntityResolver() throws ParserConfigurationException, SAXException, IOException {
    +
    +        try {
    +            DocumentBuilderFactory dbf = new DocumentBuilderFactory() {
    +
    +                DocumentBuilderFactory def = DocumentBuilderFactory.newInstance();
    +
    +                @Override
    +                public void setFeature(String name, boolean value) throws ParserConfigurationException {
    +                    def.setFeature(name, value);
    +                }
    +
    +                @Override
    +                public void setAttribute(String name, Object value) throws IllegalArgumentException {
    +                    def.setAttribute(name, value);
    +                }
    +
    +                @Override
    +                public DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {
    +                    DocumentBuilder db = def.newDocumentBuilder();
    +                    db.setEntityResolver(new EntityResolver() {
    +                        @Override
    +                        public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
    +                            if ("foo:test".equals(systemId)) {
    +                                return new InputSource(new ByteArrayInputStream("foo&amp;bar".getBytes("UTF-8")));
    +                            } else {
    +                                return null;
    +                            }
    +                        }
    +                    });
    +                    return db;
    +                }
    +
    +                @Override
    +                public boolean getFeature(String name) throws ParserConfigurationException {
    +                    return def.getFeature(name);
    +                }
    +
    +                @Override
    +                public Object getAttribute(String name) throws IllegalArgumentException {
    +                    return def.getAttribute(name);
    +                }
    +            };
    +
    +            DomUtil.setBuilderFactory(dbf);
    +            String testBody = "<?xml version='1.0'?>\n<!DOCTYPE foo [" + " <!ENTITY test SYSTEM \"foo:test\">"
    +                    + "]>\n<foo>&test;</foo>";
    +            InputStream is = new ByteArrayInputStream(testBody.getBytes("UTF-8"));
    +
    +            Document d = DomUtil.parseDocument(is);
    +            Element root = d.getDocumentElement();
    +            String text = DomUtil.getText(root);
    +            assertEquals("custom entity resolver apparently not called", "foo&bar", text);
    +        } finally {
    +            DomUtil.setBuilderFactory(null);
    +        }
    +    }
    +}
    \ No newline at end of file
    
  • jackrabbit-webdav/src/test/java/org/apache/jackrabbit/webdav/xml/TestAll.java+1 0 modified
    @@ -33,6 +33,7 @@ public static Test suite() {
             TestSuite suite = new TestSuite("org.apache.jackrabbit.webdav.xml tests");
     
             suite.addTestSuite(NamespaceTest.class);
    +        suite.addTestSuite(ParserTest.class);
     
             return suite;
         }
    
ddf9a3cd4083

JCR-3883: Jackrabbit WebDAV bundle susceptible to XXE/XEE attack (CVE-2015-1833) (ported to 2.8)

https://github.com/apache/jackrabbitJulian ReschkeMay 21, 2015via ghsa
4 files changed · +234 26
  • jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/xml/DavDocumentBuilderFactory.java+87 0 added
    @@ -0,0 +1,87 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You under the Apache License, Version 2.0
    + * (the "License"); you may not use this file except in compliance with
    + * the License.  You may obtain a copy of the License at
    + *
    + *      http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.jackrabbit.webdav.xml;
    +
    +import java.io.IOException;
    +
    +import javax.xml.XMLConstants;
    +import javax.xml.parsers.DocumentBuilder;
    +import javax.xml.parsers.DocumentBuilderFactory;
    +import javax.xml.parsers.ParserConfigurationException;
    +
    +import org.slf4j.Logger;
    +import org.slf4j.LoggerFactory;
    +import org.xml.sax.EntityResolver;
    +import org.xml.sax.InputSource;
    +import org.xml.sax.helpers.DefaultHandler;
    +
    +/**
    + * Custom {@link DocumentBuilderFactory} extended for use in WebDAV.
    + */
    +public class DavDocumentBuilderFactory {
    +
    +    private static final Logger LOG = LoggerFactory.getLogger(DomUtil.class);
    +
    +    private final DocumentBuilderFactory DEFAULT_FACTORY = createFactory();
    +
    +    private DocumentBuilderFactory BUILDER_FACTORY = DEFAULT_FACTORY;
    +
    +    private DocumentBuilderFactory createFactory() {
    +        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    +        factory.setNamespaceAware(true);
    +        factory.setIgnoringComments(true);
    +        factory.setIgnoringElementContentWhitespace(true);
    +        factory.setCoalescing(true);
    +        try {
    +            factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
    +        } catch (ParserConfigurationException e) {
    +            LOG.warn("Secure XML processing is not supported", e);
    +        } catch (AbstractMethodError e) {
    +            LOG.warn("Secure XML processing is not supported", e);
    +        }
    +        return factory;
    +    }
    +
    +    public void setFactory(DocumentBuilderFactory documentBuilderFactory) {
    +        LOG.debug("DocumentBuilderFactory changed to: " + documentBuilderFactory);
    +        BUILDER_FACTORY = documentBuilderFactory != null ? documentBuilderFactory : DEFAULT_FACTORY;
    +    }
    +
    +    /**
    +     * An entity resolver that does not allow external entity resolution. See
    +     * RFC 4918, Section 20.6
    +     */
    +    private static final EntityResolver DEFAULT_ENTITY_RESOLVER = new EntityResolver() {
    +        @Override
    +        public InputSource resolveEntity(String publicId, String systemId) throws IOException {
    +            LOG.debug("Resolution of external entities in XML payload not supported - publicId: " + publicId + ", systemId: "
    +                    + systemId);
    +            throw new IOException("This parser does not support resolution of external entities (publicId: " + publicId
    +                    + ", systemId: " + systemId + ")");
    +        }
    +    };
    +
    +    public DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {
    +        DocumentBuilder db = BUILDER_FACTORY.newDocumentBuilder();
    +        if (BUILDER_FACTORY == DEFAULT_FACTORY) {
    +            // if this is the default factory: set the default entity resolver as well
    +            db.setEntityResolver(DEFAULT_ENTITY_RESOLVER);
    +        }
    +        db.setErrorHandler(new DefaultHandler());
    +        return db;
    +    }
    +}
    
  • jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/xml/DomUtil.java+3 26 modified
    @@ -28,9 +28,7 @@
     import org.w3c.dom.Text;
     import org.w3c.dom.NamedNodeMap;
     import org.xml.sax.SAXException;
    -import org.xml.sax.helpers.DefaultHandler;
     
    -import javax.xml.XMLConstants;
     import javax.xml.namespace.QName;
     import javax.xml.parsers.DocumentBuilder;
     import javax.xml.parsers.DocumentBuilderFactory;
    @@ -56,26 +54,10 @@ public class DomUtil {
         private static Logger log = LoggerFactory.getLogger(DomUtil.class);
     
         /**
    -     * Constant for <code>DocumentBuilderFactory</code> which is used
    +     * Constant for <code>DavDocumentBuilderFactory</code> which is used
          * to create and parse DOM documents.
          */
    -    private static DocumentBuilderFactory BUILDER_FACTORY = createFactory();
    -
    -    private static DocumentBuilderFactory createFactory() {
    -        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    -        factory.setNamespaceAware(true);
    -        factory.setIgnoringComments(true);
    -        factory.setIgnoringElementContentWhitespace(true);
    -        factory.setCoalescing(true);
    -        try {
    -            factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
    -        } catch (ParserConfigurationException e) {
    -            log.warn("Secure XML processing is not supported", e);
    -        } catch (AbstractMethodError e) {
    -            log.warn("Secure XML processing is not supported", e);
    -        }
    -        return factory;
    -    }
    +    private static final DavDocumentBuilderFactory BUILDER_FACTORY = new DavDocumentBuilderFactory();
     
         /**
          * Support the replacement of {@link #BUILDER_FACTORY}. This is useful
    @@ -88,7 +70,7 @@ private static DocumentBuilderFactory createFactory() {
          */
         public static void setBuilderFactory(
                 DocumentBuilderFactory documentBuilderFactory) {
    -        BUILDER_FACTORY = documentBuilderFactory;
    +        BUILDER_FACTORY.setFactory(documentBuilderFactory);
         }
     
         /**
    @@ -119,11 +101,6 @@ public static Document createDocument()
         public static Document parseDocument(InputStream stream)
                 throws ParserConfigurationException, SAXException, IOException {
             DocumentBuilder docBuilder = BUILDER_FACTORY.newDocumentBuilder();
    -
    -        // Set an error handler to prevent parsers from printing error messages
    -        // to standard output!
    -        docBuilder.setErrorHandler(new DefaultHandler());
    -
             return docBuilder.parse(stream);
         }
     
    
  • jackrabbit-webdav/src/test/java/org/apache/jackrabbit/webdav/xml/ParserTest.java+143 0 added
    @@ -0,0 +1,143 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You under the Apache License, Version 2.0
    + * (the \"License\"); you may not use this file except in compliance with
    + * the License.  You may obtain a copy of the License at
    + *
    + *      http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an \"AS IS\" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.jackrabbit.webdav.xml;
    +
    +import java.io.ByteArrayInputStream;
    +import java.io.File;
    +import java.io.FileOutputStream;
    +import java.io.IOException;
    +import java.io.InputStream;
    +import java.io.OutputStream;
    +import java.io.UnsupportedEncodingException;
    +
    +import javax.xml.parsers.DocumentBuilder;
    +import javax.xml.parsers.DocumentBuilderFactory;
    +import javax.xml.parsers.ParserConfigurationException;
    +
    +import junit.framework.TestCase;
    +
    +import org.w3c.dom.Document;
    +import org.w3c.dom.Element;
    +import org.xml.sax.EntityResolver;
    +import org.xml.sax.InputSource;
    +import org.xml.sax.SAXException;
    +
    +public class ParserTest extends TestCase {
    +
    +    // see <http://en.wikipedia.org/wiki/Billion_laughs#Details>
    +    public void testBillionLaughs() throws UnsupportedEncodingException {
    +
    +        String testBody = "<?xml version=\"1.0\"?>" + "<!DOCTYPE lolz [" + " <!ENTITY lol \"lol\">" + " <!ELEMENT lolz (#PCDATA)>"
    +                + " <!ENTITY lol1 \"&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;\">"
    +                + " <!ENTITY lol2 \"&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;\">"
    +                + " <!ENTITY lol3 \"&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;\">"
    +                + " <!ENTITY lol4 \"&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;\">"
    +                + " <!ENTITY lol5 \"&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;\">"
    +                + " <!ENTITY lol6 \"&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;\">"
    +                + " <!ENTITY lol7 \"&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;\">"
    +                + " <!ENTITY lol8 \"&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;\">"
    +                + " <!ENTITY lol9 \"&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;\">" + "]>" + "<lolz>&lol9;</lolz>";
    +        InputStream is = new ByteArrayInputStream(testBody.getBytes("UTF-8"));
    +
    +        try {
    +            DomUtil.parseDocument(is);
    +            fail("parsing this document should cause an exception");
    +        } catch (Exception expected) {
    +        }
    +    }
    +
    +    public void testExternalEntities() throws IOException {
    +
    +        String dname = "target";
    +        String fname = "test.xml";
    +
    +        File f = new File(dname, fname);
    +        OutputStream os = new FileOutputStream(f);
    +        os.write("testdata".getBytes());
    +        os.close();
    +
    +        String testBody = "<?xml version='1.0'?>\n<!DOCTYPE foo [" + " <!ENTITY test SYSTEM \"file:" + dname + "/" + fname + "\">"
    +                + "]>\n<foo>&test;</foo>";
    +        InputStream is = new ByteArrayInputStream(testBody.getBytes("UTF-8"));
    +
    +        try {
    +            Document d = DomUtil.parseDocument(is);
    +            Element root = d.getDocumentElement();
    +            String text = DomUtil.getText(root);
    +            fail("parsing this document should cause an exception, but the following external content was included: " + text);
    +        } catch (Exception expected) {
    +        }
    +    }
    +
    +    public void testCustomEntityResolver() throws ParserConfigurationException, SAXException, IOException {
    +
    +        try {
    +            DocumentBuilderFactory dbf = new DocumentBuilderFactory() {
    +
    +                DocumentBuilderFactory def = DocumentBuilderFactory.newInstance();
    +
    +                @Override
    +                public void setFeature(String name, boolean value) throws ParserConfigurationException {
    +                    def.setFeature(name, value);
    +                }
    +
    +                @Override
    +                public void setAttribute(String name, Object value) throws IllegalArgumentException {
    +                    def.setAttribute(name, value);
    +                }
    +
    +                @Override
    +                public DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {
    +                    DocumentBuilder db = def.newDocumentBuilder();
    +                    db.setEntityResolver(new EntityResolver() {
    +                        @Override
    +                        public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
    +                            if ("foo:test".equals(systemId)) {
    +                                return new InputSource(new ByteArrayInputStream("foo&amp;bar".getBytes("UTF-8")));
    +                            } else {
    +                                return null;
    +                            }
    +                        }
    +                    });
    +                    return db;
    +                }
    +
    +                @Override
    +                public boolean getFeature(String name) throws ParserConfigurationException {
    +                    return def.getFeature(name);
    +                }
    +
    +                @Override
    +                public Object getAttribute(String name) throws IllegalArgumentException {
    +                    return def.getAttribute(name);
    +                }
    +            };
    +
    +            DomUtil.setBuilderFactory(dbf);
    +            String testBody = "<?xml version='1.0'?>\n<!DOCTYPE foo [" + " <!ENTITY test SYSTEM \"foo:test\">"
    +                    + "]>\n<foo>&test;</foo>";
    +            InputStream is = new ByteArrayInputStream(testBody.getBytes("UTF-8"));
    +
    +            Document d = DomUtil.parseDocument(is);
    +            Element root = d.getDocumentElement();
    +            String text = DomUtil.getText(root);
    +            assertEquals("custom entity resolver apparently not called", "foo&bar", text);
    +        } finally {
    +            DomUtil.setBuilderFactory(null);
    +        }
    +    }
    +}
    \ No newline at end of file
    
  • jackrabbit-webdav/src/test/java/org/apache/jackrabbit/webdav/xml/TestAll.java+1 0 modified
    @@ -33,6 +33,7 @@ public static Test suite() {
             TestSuite suite = new TestSuite("org.apache.jackrabbit.webdav.xml tests");
     
             suite.addTestSuite(NamespaceTest.class);
    +        suite.addTestSuite(ParserTest.class);
     
             return suite;
         }
    
3903739363b7

JCR-3883: Jackrabbit WebDAV bundle susceptible to XXE/XEE attack

https://github.com/apache/jackrabbitMarcel ReuteggerMay 21, 2015via ghsa
4 files changed · +234 26
  • jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/xml/DavDocumentBuilderFactory.java+87 0 added
    @@ -0,0 +1,87 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You under the Apache License, Version 2.0
    + * (the "License"); you may not use this file except in compliance with
    + * the License.  You may obtain a copy of the License at
    + *
    + *      http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.jackrabbit.webdav.xml;
    +
    +import java.io.IOException;
    +
    +import javax.xml.XMLConstants;
    +import javax.xml.parsers.DocumentBuilder;
    +import javax.xml.parsers.DocumentBuilderFactory;
    +import javax.xml.parsers.ParserConfigurationException;
    +
    +import org.slf4j.Logger;
    +import org.slf4j.LoggerFactory;
    +import org.xml.sax.EntityResolver;
    +import org.xml.sax.InputSource;
    +import org.xml.sax.helpers.DefaultHandler;
    +
    +/**
    + * Custom {@link DocumentBuilderFactory} extended for use in WebDAV.
    + */
    +public class DavDocumentBuilderFactory {
    +
    +    private static final Logger LOG = LoggerFactory.getLogger(DomUtil.class);
    +
    +    private final DocumentBuilderFactory DEFAULT_FACTORY = createFactory();
    +
    +    private DocumentBuilderFactory BUILDER_FACTORY = DEFAULT_FACTORY;
    +
    +    private DocumentBuilderFactory createFactory() {
    +        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    +        factory.setNamespaceAware(true);
    +        factory.setIgnoringComments(true);
    +        factory.setIgnoringElementContentWhitespace(true);
    +        factory.setCoalescing(true);
    +        try {
    +            factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
    +        } catch (ParserConfigurationException e) {
    +            LOG.warn("Secure XML processing is not supported", e);
    +        } catch (AbstractMethodError e) {
    +            LOG.warn("Secure XML processing is not supported", e);
    +        }
    +        return factory;
    +    }
    +
    +    public void setFactory(DocumentBuilderFactory documentBuilderFactory) {
    +        LOG.debug("DocumentBuilderFactory changed to: " + documentBuilderFactory);
    +        BUILDER_FACTORY = documentBuilderFactory != null ? documentBuilderFactory : DEFAULT_FACTORY;
    +    }
    +
    +    /**
    +     * An entity resolver that does not allow external entity resolution. See
    +     * RFC 4918, Section 20.6
    +     */
    +    private static final EntityResolver DEFAULT_ENTITY_RESOLVER = new EntityResolver() {
    +        @Override
    +        public InputSource resolveEntity(String publicId, String systemId) throws IOException {
    +            LOG.debug("Resolution of external entities in XML payload not supported - publicId: " + publicId + ", systemId: "
    +                    + systemId);
    +            throw new IOException("This parser does not support resolution of external entities (publicId: " + publicId
    +                    + ", systemId: " + systemId + ")");
    +        }
    +    };
    +
    +    public DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {
    +        DocumentBuilder db = BUILDER_FACTORY.newDocumentBuilder();
    +        if (BUILDER_FACTORY == DEFAULT_FACTORY) {
    +            // if this is the default factory: set the default entity resolver as well
    +            db.setEntityResolver(DEFAULT_ENTITY_RESOLVER);
    +        }
    +        db.setErrorHandler(new DefaultHandler());
    +        return db;
    +    }
    +}
    
  • jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/xml/DomUtil.java+3 26 modified
    @@ -28,9 +28,7 @@
     import org.w3c.dom.Text;
     import org.w3c.dom.NamedNodeMap;
     import org.xml.sax.SAXException;
    -import org.xml.sax.helpers.DefaultHandler;
     
    -import javax.xml.XMLConstants;
     import javax.xml.namespace.QName;
     import javax.xml.parsers.DocumentBuilder;
     import javax.xml.parsers.DocumentBuilderFactory;
    @@ -56,26 +54,10 @@ public class DomUtil {
         private static Logger log = LoggerFactory.getLogger(DomUtil.class);
     
         /**
    -     * Constant for <code>DocumentBuilderFactory</code> which is used
    +     * Constant for <code>DavDocumentBuilderFactory</code> which is used
          * to create and parse DOM documents.
          */
    -    private static DocumentBuilderFactory BUILDER_FACTORY = createFactory();
    -
    -    private static DocumentBuilderFactory createFactory() {
    -        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    -        factory.setNamespaceAware(true);
    -        factory.setIgnoringComments(true);
    -        factory.setIgnoringElementContentWhitespace(true);
    -        factory.setCoalescing(true);
    -        try {
    -            factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
    -        } catch (ParserConfigurationException e) {
    -            log.warn("Secure XML processing is not supported", e);
    -        } catch (AbstractMethodError e) {
    -            log.warn("Secure XML processing is not supported", e);
    -        }
    -        return factory;
    -    }
    +    private static final DavDocumentBuilderFactory BUILDER_FACTORY = new DavDocumentBuilderFactory();
     
         /**
          * Support the replacement of {@link #BUILDER_FACTORY}. This is useful
    @@ -88,7 +70,7 @@ private static DocumentBuilderFactory createFactory() {
          */
         public static void setBuilderFactory(
                 DocumentBuilderFactory documentBuilderFactory) {
    -        BUILDER_FACTORY = documentBuilderFactory;
    +        BUILDER_FACTORY.setFactory(documentBuilderFactory);
         }
     
         /**
    @@ -119,11 +101,6 @@ public static Document createDocument()
         public static Document parseDocument(InputStream stream)
                 throws ParserConfigurationException, SAXException, IOException {
             DocumentBuilder docBuilder = BUILDER_FACTORY.newDocumentBuilder();
    -
    -        // Set an error handler to prevent parsers from printing error messages
    -        // to standard output!
    -        docBuilder.setErrorHandler(new DefaultHandler());
    -
             return docBuilder.parse(stream);
         }
     
    
  • jackrabbit-webdav/src/test/java/org/apache/jackrabbit/webdav/xml/ParserTest.java+143 0 added
    @@ -0,0 +1,143 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You under the Apache License, Version 2.0
    + * (the \"License\"); you may not use this file except in compliance with
    + * the License.  You may obtain a copy of the License at
    + *
    + *      http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an \"AS IS\" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.jackrabbit.webdav.xml;
    +
    +import java.io.ByteArrayInputStream;
    +import java.io.File;
    +import java.io.FileOutputStream;
    +import java.io.IOException;
    +import java.io.InputStream;
    +import java.io.OutputStream;
    +import java.io.UnsupportedEncodingException;
    +
    +import javax.xml.parsers.DocumentBuilder;
    +import javax.xml.parsers.DocumentBuilderFactory;
    +import javax.xml.parsers.ParserConfigurationException;
    +
    +import junit.framework.TestCase;
    +
    +import org.w3c.dom.Document;
    +import org.w3c.dom.Element;
    +import org.xml.sax.EntityResolver;
    +import org.xml.sax.InputSource;
    +import org.xml.sax.SAXException;
    +
    +public class ParserTest extends TestCase {
    +
    +    // see <http://en.wikipedia.org/wiki/Billion_laughs#Details>
    +    public void testBillionLaughs() throws UnsupportedEncodingException {
    +
    +        String testBody = "<?xml version=\"1.0\"?>" + "<!DOCTYPE lolz [" + " <!ENTITY lol \"lol\">" + " <!ELEMENT lolz (#PCDATA)>"
    +                + " <!ENTITY lol1 \"&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;\">"
    +                + " <!ENTITY lol2 \"&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;\">"
    +                + " <!ENTITY lol3 \"&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;\">"
    +                + " <!ENTITY lol4 \"&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;\">"
    +                + " <!ENTITY lol5 \"&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;\">"
    +                + " <!ENTITY lol6 \"&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;\">"
    +                + " <!ENTITY lol7 \"&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;\">"
    +                + " <!ENTITY lol8 \"&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;\">"
    +                + " <!ENTITY lol9 \"&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;\">" + "]>" + "<lolz>&lol9;</lolz>";
    +        InputStream is = new ByteArrayInputStream(testBody.getBytes("UTF-8"));
    +
    +        try {
    +            DomUtil.parseDocument(is);
    +            fail("parsing this document should cause an exception");
    +        } catch (Exception expected) {
    +        }
    +    }
    +
    +    public void testExternalEntities() throws IOException {
    +
    +        String dname = "target";
    +        String fname = "test.xml";
    +
    +        File f = new File(dname, fname);
    +        OutputStream os = new FileOutputStream(f);
    +        os.write("testdata".getBytes());
    +        os.close();
    +
    +        String testBody = "<?xml version='1.0'?>\n<!DOCTYPE foo [" + " <!ENTITY test SYSTEM \"file:" + dname + "/" + fname + "\">"
    +                + "]>\n<foo>&test;</foo>";
    +        InputStream is = new ByteArrayInputStream(testBody.getBytes("UTF-8"));
    +
    +        try {
    +            Document d = DomUtil.parseDocument(is);
    +            Element root = d.getDocumentElement();
    +            String text = DomUtil.getText(root);
    +            fail("parsing this document should cause an exception, but the following external content was included: " + text);
    +        } catch (Exception expected) {
    +        }
    +    }
    +
    +    public void testCustomEntityResolver() throws ParserConfigurationException, SAXException, IOException {
    +
    +        try {
    +            DocumentBuilderFactory dbf = new DocumentBuilderFactory() {
    +
    +                DocumentBuilderFactory def = DocumentBuilderFactory.newInstance();
    +
    +                @Override
    +                public void setFeature(String name, boolean value) throws ParserConfigurationException {
    +                    def.setFeature(name, value);
    +                }
    +
    +                @Override
    +                public void setAttribute(String name, Object value) throws IllegalArgumentException {
    +                    def.setAttribute(name, value);
    +                }
    +
    +                @Override
    +                public DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {
    +                    DocumentBuilder db = def.newDocumentBuilder();
    +                    db.setEntityResolver(new EntityResolver() {
    +                        @Override
    +                        public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
    +                            if ("foo:test".equals(systemId)) {
    +                                return new InputSource(new ByteArrayInputStream("foo&amp;bar".getBytes("UTF-8")));
    +                            } else {
    +                                return null;
    +                            }
    +                        }
    +                    });
    +                    return db;
    +                }
    +
    +                @Override
    +                public boolean getFeature(String name) throws ParserConfigurationException {
    +                    return def.getFeature(name);
    +                }
    +
    +                @Override
    +                public Object getAttribute(String name) throws IllegalArgumentException {
    +                    return def.getAttribute(name);
    +                }
    +            };
    +
    +            DomUtil.setBuilderFactory(dbf);
    +            String testBody = "<?xml version='1.0'?>\n<!DOCTYPE foo [" + " <!ENTITY test SYSTEM \"foo:test\">"
    +                    + "]>\n<foo>&test;</foo>";
    +            InputStream is = new ByteArrayInputStream(testBody.getBytes("UTF-8"));
    +
    +            Document d = DomUtil.parseDocument(is);
    +            Element root = d.getDocumentElement();
    +            String text = DomUtil.getText(root);
    +            assertEquals("custom entity resolver apparently not called", "foo&bar", text);
    +        } finally {
    +            DomUtil.setBuilderFactory(null);
    +        }
    +    }
    +}
    \ No newline at end of file
    
  • jackrabbit-webdav/src/test/java/org/apache/jackrabbit/webdav/xml/TestAll.java+1 0 modified
    @@ -33,6 +33,7 @@ public static Test suite() {
             TestSuite suite = new TestSuite("org.apache.jackrabbit.webdav.xml tests");
     
             suite.addTestSuite(NamespaceTest.class);
    +        suite.addTestSuite(ParserTest.class);
     
             return suite;
         }
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

18

News mentions

0

No linked articles in our index yet.