View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.xml.secure;
19  
20  import javax.xml.transform.Source;
21  import javax.xml.validation.Validator;
22  
23  import org.junit.jupiter.api.Assumptions;
24  import org.junit.jupiter.api.Tag;
25  import org.junit.jupiter.api.Test;
26  
27  /**
28   * Tests whether parsers can pull in an external general entity declared inline in the internal subset.
29   *
30   * <p>
31   * The wrapper declares {@code <!ENTITY xxe SYSTEM "file:.../referenced.txt">} and uses {@code &xxe;} in the body. The general entity expands to the
32   * content of {@code src/test/resources/leaked/referenced.txt} when the external reference is resolved. A secure parser keeps the entity declared but
33   * resolves its content to empty, so the parse completes without the file's content; an unconfigured parser fetches the file, the entity resolves, and the
34   * parse succeeds.
35   * </p>
36   *
37   * <p>
38   * Each parser type is exercised twice as a pair (unconfigured factory, expected to parse; secure factory, expected to complete without leaked
39   * content):
40   * </p>
41   *
42   * <ul>
43   *   <li>DOM, SAX and StAX direct XML parsing.</li>
44   *   <li>{@code SchemaFactory.newSchema(Source)} compilation of an XSD whose source has the entity-bearing DOCTYPE.</li>
45   *   <li>{@link Validator#validate(Source)} of an instance whose source has the entity-bearing DOCTYPE.</li>
46   *   <li>Identity {@code Transformer} reading the input XML.</li>
47   *   <li>{@code TransformerFactory.newTransformer(Source)} compilation of a stylesheet whose source has the entity-bearing DOCTYPE.</li>
48   * </ul>
49   */
50  class ExternalGeneralEntityTest {
51  
52      private static final String INSERTION = "&xxe;";
53  
54      private static String withDoctype(final String rootQName, final String body) {
55          return "<?xml version=\"1.0\"?>\n"
56                  + "<!DOCTYPE " + rootQName + " [\n"
57                  + "  <!ENTITY xxe SYSTEM \"" + AttackTestSupport.resourceUrl("referenced.txt") + "\">\n"
58                  + "]>\n"
59                  + body + "\n";
60      }
61  
62      private static String xmlPayload() {
63          return withDoctype("root", AttackTestSupport.xmlBody(INSERTION));
64      }
65  
66      private static String xsdPayload() {
67          return withDoctype("xs:schema", AttackTestSupport.xsdBody(INSERTION));
68      }
69  
70      private static String xsltPayload() {
71          return withDoctype("xsl:stylesheet", AttackTestSupport.xsltBody(INSERTION));
72      }
73  
74      @Test
75      @Tag("dom")
76      void secureDomDoesNotLeak() {
77          Assumptions.assumeTrue(AttackTestSupport.DOM_RESOLVES_INTERNAL_ENTITIES,
78                  "Skipped: platform DOM does not resolve user-defined entities");
79          AttackTestSupport.assertDomDoesNotLeak(xmlPayload());
80      }
81  
82      @Test
83      @Tag("sax")
84      void secureSaxDoesNotLeak() {
85          AttackTestSupport.assertSaxDoesNotLeak(xmlPayload());
86      }
87  
88      @Test
89      @Tag("schema")
90      void secureSchemaDoesNotLeak() {
91          AttackTestSupport.assertSchemaDoesNotLeak(AttackTestSupport.streamSource(xsdPayload()));
92      }
93  
94      @Test
95      @Tag("stax")
96      void secureStaxDoesNotLeak() {
97          AttackTestSupport.assertStaxDoesNotLeak(xmlPayload());
98      }
99  
100     @Test
101     @Tag("trax")
102     void secureTemplatesDoesNotLeak() {
103         AttackTestSupport.assertTemplatesDoesNotLeak(AttackTestSupport.streamSource(xsltPayload()));
104     }
105 
106     @Test
107     @Tag("trax")
108     void secureTransformerDoesNotLeak() {
109         AttackTestSupport.assertTransformerDoesNotLeak(xmlPayload());
110     }
111 
112     @Test
113     @Tag("schema")
114     void secureValidatorDoesNotLeak() {
115         AttackTestSupport.assertValidatorDoesNotLeak(xmlPayload());
116     }
117 
118     @Test
119     @Tag("sax")
120     void secureXmlReaderDoesNotLeak() {
121         AttackTestSupport.assertXmlReaderDoesNotLeak(xmlPayload());
122     }
123 
124     @Test
125     @Tag("dom")
126     void unconfiguredDomParses() {
127         Assumptions.assumeTrue(AttackTestSupport.DOM_RESOLVES_INTERNAL_ENTITIES,
128                 "Skipped: platform DOM does not resolve user-defined entities");
129         AttackTestSupport.assertPermissiveDomParses(xmlPayload());
130     }
131 
132     @Test
133     @Tag("sax")
134     void unconfiguredSaxParses() {
135         AttackTestSupport.assertPermissiveSaxParses(xmlPayload());
136     }
137 
138     @Test
139     @Tag("schema")
140     void unconfiguredSchemaCompiles() {
141         AttackTestSupport.assertPermissiveSchemaCompiles(AttackTestSupport.streamSource(xsdPayload()));
142     }
143 
144     @Test
145     @Tag("stax")
146     void unconfiguredStaxParses() {
147         AttackTestSupport.assertPermissiveStaxParses(xmlPayload());
148     }
149 
150     @Test
151     @Tag("trax")
152     void unconfiguredTemplatesCompiles() {
153         AttackTestSupport.assertPermissiveTemplatesCompiles(xsltPayload());
154     }
155 
156     @Test
157     @Tag("trax")
158     void unconfiguredTransformerTransforms() {
159         AttackTestSupport.assertPermissiveTransformerTransforms(xmlPayload());
160     }
161 
162     @Test
163     @Tag("schema")
164     void unconfiguredValidatorValidates() {
165         AttackTestSupport.assertPermissiveValidatorValidates(xmlPayload());
166     }
167 }