• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

xmlunit / xmlunit / 1a614304-5505-40ee-96f7-a81bcc756c5a

20 Jun 2026 03:04PM UTC coverage: 91.731% (-0.02%) from 91.747%
1a614304-5505-40ee-96f7-a81bcc756c5a

push

circleci

web-flow
Merge pull request #332 from jmestwa-coder/parsing-validator-external-entities

disable external entities in ParsingValidator instance parsing

4038 of 4750 branches covered (85.01%)

10 of 12 new or added lines in 1 file covered. (83.33%)

9 existing lines in 1 file now uncovered.

11848 of 12916 relevant lines covered (91.73%)

2.34 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.67
/xmlunit-core/src/main/java/org/xmlunit/validation/ParsingValidator.java
1
/*
2
  This file is licensed to You under the Apache License, Version 2.0
3
  (the "License"); you may not use this file except in compliance with
4
  the License.  You may obtain a copy of the License at
5

6
  http://www.apache.org/licenses/LICENSE-2.0
7

8
  Unless required by applicable law or agreed to in writing, software
9
  distributed under the License is distributed on an "AS IS" BASIS,
10
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
  See the License for the specific language governing permissions and
12
  limitations under the License.
13
*/
14
package org.xmlunit.validation;
15

16
import javax.xml.parsers.ParserConfigurationException;
17
import javax.xml.parsers.SAXParser;
18
import javax.xml.parsers.SAXParserFactory;
19
import javax.xml.transform.Source;
20
import org.xmlunit.ConfigurationException;
21
import org.xmlunit.XMLUnitException;
22
import org.xmlunit.util.Convert;
23
import org.xml.sax.InputSource;
24
import org.xml.sax.SAXException;
25
import org.xml.sax.SAXNotRecognizedException;
26
import org.xml.sax.SAXNotSupportedException;
27
import org.xml.sax.SAXParseException;
28
import org.xml.sax.helpers.DefaultHandler;
29

30
/**
31
 * Validator implementation that uses "the old way" of validating an
32
 * XML input by parsing the input.
33
 *
34
 * <p>Even though this implementation supports W3C Schema you
35
 * shouldn't use it for that language but rather use
36
 * JAXPValidator.</p>
37
 *
38
 * <p><strong>Security note:</strong> like the rest of the {@code
39
 * validation} package this class does not disable external entities
40
 * by default - that has been a conscious decision since XMLUnit 2.6.0.
41
 * An instance document with a {@code DOCTYPE} that declares an external
42
 * entity may therefore cause that entity to be resolved while it is
43
 * validated. If you validate untrusted input use {@link
44
 * #setDisableExternalEntities setDisableExternalEntities(true)} to
45
 * forbid this.</p>
46
 */
47
public class ParsingValidator extends Validator {
48
    private final String language;
49
    private String schemaURI;
50
    private boolean disableExternalEntities;
51

52
    /**
53
     * Creates a validator for the given schema language.
54
     * @param language the schema language
55
     */
56
    public ParsingValidator(String language) {
1✔
57
        if (!Languages.W3C_XML_SCHEMA_NS_URI.equals(language)
1✔
58
            && !Languages.XML_DTD_NS_URI.equals(language)) {
1✔
59
            throw new IllegalArgumentException("only DTD and W3C Schema"
1✔
60
                                               + " validation are supported by"
61
                                               + " ParsingValidator");
62
        }
63
        this.language = language;
1✔
64
    }
1✔
65

66
    /**
67
     * The URI (or for example the System ID in case of a DTD) that
68
     * identifies the schema to validate or use during validation.
69
     * @param uri the schema URI
70
     */
71
    public void setSchemaURI(String uri) {
72
        this.schemaURI = uri;
×
73
    }
×
74

75
    /**
76
     * The URI (or for example the System ID in case of a DTD) that
77
     * identifies the schema validated or used during validation.
78
     * @return the schema URI
79
     */
80
    protected String getSchemaURI() {
81
        return schemaURI;
1✔
82
    }
83

84
    /**
85
     * Whether external general and parameter entities should be
86
     * disabled when the instance document is parsed for validation.
87
     *
88
     * <p>The default is {@code false}, leaving external entities
89
     * enabled as they have been since XMLUnit 2.6.0. Setting this to
90
     * {@code true} turns off the {@code external-general-entities} and
91
     * {@code external-parameter-entities} features on the parser, which
92
     * closes the XXE vector when validating untrusted instances. The
93
     * DTD or schema to validate against is still resolved through the
94
     * internal entity resolver, so DTD and schema validation keep
95
     * working.</p>
96
     *
97
     * @since XMLUnit 2.12.1
98
     * @param disable whether to disable external entities
99
     */
100
    public void setDisableExternalEntities(boolean disable) {
101
        disableExternalEntities = disable;
1✔
102
    }
1✔
103

104
    /**
105
     * {@link ParsingValidator} doesn't support validation of the
106
     * schema itself.
107
     * @throws XMLUnitException always
108
     */
109
    @Override public ValidationResult validateSchema() {
110
        throw new XMLUnitException("Schema validation is not supported by"
1✔
111
                                   + " ParsingValidator");
112
    }
113

114
    @Override
115
    public ValidationResult validateInstance(Source s) {
116
        return validateInstance(s, SAXParserFactory.newInstance());
1✔
117
    }
118

119
    /**
120
     * Validates an instance against the schema using a pre-configured {@link SAXParserFactory}.
121
     *
122
     * <p>The factory given will be configured to be namespace aware and validating.</p>
123
     *
124
     * @param s the instance document
125
     * @param factory the factory to use, must not be null
126
     * @return result of the validation
127
     *
128
     * @since XMLUnit 2.6.0
129
     */
130
    public ValidationResult validateInstance(Source s, SAXParserFactory factory) {
131
        if (factory == null) {
1!
132
            throw new IllegalArgumentException("factory must not be null");
×
133
        }
134
        try {
135
            factory.setNamespaceAware(true);
1✔
136
            factory.setValidating(true);
1✔
137
            if (disableExternalEntities) {
1✔
138
                restrictExternalEntities(factory);
1✔
139
            }
140
            SAXParser parser = factory.newSAXParser();
1✔
141
            if (Languages.W3C_XML_SCHEMA_NS_URI.equals(language)) {
1✔
142
                parser.setProperty(Properties.SCHEMA_LANGUAGE,
1✔
143
                                   Languages.W3C_XML_SCHEMA_NS_URI);
144
            }
145
            final Source[] source = getSchemaSources();
1✔
146
            Handler handler = new Handler();
1✔
147
            if (source.length != 0) {
1✔
148
                if (Languages.W3C_XML_SCHEMA_NS_URI.equals(language)) {
1✔
149
                    InputSource[] schemaSource = new InputSource[source.length];
1✔
150
                    for (int i = 0; i < source.length; i++) {
1✔
151
                        schemaSource[i] = Convert.toInputSource(source[i]);
1✔
152
                    }
153
                    parser.setProperty(Properties.SCHEMA_SOURCE,
1✔
154
                                       schemaSource);
155
                } else if (source.length == 1) {
1!
156
                    handler.setSchemaSystemId(source[0].getSystemId());
1✔
157
                }
158
            }
159
            InputSource input = Convert.toInputSource(s);
1✔
160
            try {
161
                parser.parse(input, handler);
1✔
162
            } catch (SAXParseException e) {
1✔
163
                handler.error((SAXParseException) e);
1✔
164
            } catch (SAXException e) {
1✔
165
                throw new XMLUnitException(e);
1✔
166
            }
1✔
167
            return handler.getResult();
1✔
168
        } catch (ParserConfigurationException ex) {
1✔
169
            throw new ConfigurationException(ex);
1✔
170
        } catch (SAXNotRecognizedException ex) {
1✔
171
            throw new ConfigurationException(ex);
1✔
172
        } catch (SAXNotSupportedException ex) {
1✔
173
            throw new ConfigurationException(ex);
1✔
174
        } catch (SAXException ex) {
1✔
175
            throw new XMLUnitException(ex);
1✔
176
        } catch (java.io.IOException ex) {
1✔
177
            throw new XMLUnitException(ex);
1✔
178
        }
179
    }
180

181
    private static final String EXTERNAL_GENERAL_ENTITIES =
182
        "http://xml.org/sax/features/external-general-entities";
183
    private static final String EXTERNAL_PARAMETER_ENTITIES =
184
        "http://xml.org/sax/features/external-parameter-entities";
185

186
    /**
187
     * Stops the instance document from pulling in external general or
188
     * parameter entities while it is parsed for validation.
189
     *
190
     * <p>The DTD or schema to validate against is still resolved
191
     * through the {@link Handler}, only entities declared inside the
192
     * instance are affected.</p>
193
     */
194
    private static void restrictExternalEntities(SAXParserFactory factory) {
195
        setSafeFeature(factory, EXTERNAL_GENERAL_ENTITIES, false);
1✔
196
        setSafeFeature(factory, EXTERNAL_PARAMETER_ENTITIES, false);
1✔
197
    }
1✔
198

199
    private static void setSafeFeature(SAXParserFactory factory, String feature, boolean value) {
200
        try {
201
            factory.setFeature(feature, value);
1✔
NEW
202
        } catch (ParserConfigurationException ex) {
×
203
            // feature not supported by this parser, ignore
NEW
204
        } catch (SAXException ex) {
×
205
            // feature not supported by this parser, ignore
206
        }
1✔
207
    }
1✔
208

209
    private static class Properties {
210
        static final String SCHEMA_LANGUAGE =
211
            "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
212

213
        static final String SCHEMA_SOURCE =
214
            "http://java.sun.com/xml/jaxp/properties/schemaSource";
215

216
        private Properties() {}
217
    }
218

219
    private class Handler extends DefaultHandler {
1✔
220
        private final ValidationHandler v = new ValidationHandler();
1✔
221
        private String systemId;
222

223
        @Override public void error(SAXParseException e) {
224
            v.error(e);
1✔
225
        }
1✔
226

227
        @Override public void fatalError(SAXParseException e) {
228
            v.fatalError(e);
×
229
        }
×
230

231
        @Override public void warning(SAXParseException e) {
232
            v.warning(e);
×
233
        }
×
234

235
        private void setSchemaSystemId(String id) {
236
            systemId = id;
1✔
237
        }
1✔
238

239
        @Override public InputSource resolveEntity(String publicId,
240
                                                   String systemId)
241
            throws java.io.IOException, SAXException {
242
            if (this.systemId != null &&
1✔
243
                (getSchemaURI() == null || getSchemaURI().equals(publicId))
1!
244
                ) {
245
                return new InputSource(this.systemId);
1✔
246
            }
247
            return super.resolveEntity(publicId, systemId);
1✔
248
        }
249

250
        ValidationResult getResult() {
251
            return v.getResult();
1✔
252
        }
253
    }
254
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc