Skip to content

Commit 8ab7fb6

Browse files
committed
prevent XXE vulnerabilities
1 parent d42c1d7 commit 8ab7fb6

File tree

10 files changed

+144
-64
lines changed

10 files changed

+144
-64
lines changed

build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ subprojects {
3535
maven {
3636
url 'https://citydb.jfrog.io/artifactory/maven'
3737
}
38+
maven {
39+
url 'https://oss.sonatype.org/content/repositories/snapshots/'
40+
}
3841
mavenCentral()
3942
}
4043

impexp-client-gui/src/main/java/org/citydb/gui/map/geocoder/service/GoogleGeocoder.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,12 @@
3232
import org.citydb.gui.map.geocoder.GeocoderResult;
3333
import org.citydb.gui.map.geocoder.Location;
3434
import org.citydb.gui.map.geocoder.LocationType;
35+
import org.citydb.util.xml.SecureXMLProcessors;
3536
import org.jdesktop.swingx.mapviewer.GeoPosition;
3637
import org.w3c.dom.Document;
3738
import org.w3c.dom.Node;
3839
import org.w3c.dom.NodeList;
3940

40-
import javax.xml.parsers.DocumentBuilderFactory;
4141
import javax.xml.xpath.XPath;
4242
import javax.xml.xpath.XPathConstants;
4343
import javax.xml.xpath.XPathFactory;
@@ -84,9 +84,11 @@ private GeocoderResult geocode(String operation, String requestString) throws Ge
8484
}
8585

8686
try (InputStream stream = new URL(serviceCall).openStream()) {
87-
Document response = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(stream);
88-
XPath xpath = XPathFactory.newInstance().newXPath();
87+
Document response = SecureXMLProcessors.newDocumentBuilderFactory()
88+
.newDocumentBuilder()
89+
.parse(stream);
8990

91+
XPath xpath = XPathFactory.newInstance().newXPath();
9092
GeocoderResult geocodingResult = new GeocoderResult();
9193

9294
// check the response status

impexp-config/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
dependencies {
2-
api 'org.citygml4j:citygml4j:2.12.0'
2+
api 'org.citygml4j:citygml4j:2.12.1-SNAPSHOT'
33
}

impexp-core/src/main/java/org/citydb/core/database/schema/util/SchemaMappingUtil.java

Lines changed: 47 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,21 @@
2828
package org.citydb.core.database.schema.util;
2929

3030
import org.citydb.core.database.schema.mapping.*;
31+
import org.citydb.util.xml.SecureXMLProcessors;
32+
import org.xml.sax.InputSource;
3133
import org.xml.sax.SAXException;
34+
import org.xml.sax.XMLReader;
3235

3336
import javax.xml.XMLConstants;
3437
import javax.xml.bind.*;
38+
import javax.xml.parsers.ParserConfigurationException;
39+
import javax.xml.parsers.SAXParserFactory;
3540
import javax.xml.validation.Schema;
3641
import javax.xml.validation.SchemaFactory;
3742
import java.io.*;
3843
import java.net.URL;
3944
import java.nio.charset.StandardCharsets;
45+
import java.nio.file.Files;
4046

4147
public class SchemaMappingUtil {
4248
private static SchemaMappingUtil instance;
@@ -53,56 +59,53 @@ public static synchronized SchemaMappingUtil getInstance() throws JAXBException
5359
return instance;
5460
}
5561

56-
private SchemaMapping unmarshal(SchemaMapping schemaMapping, Object input) throws SchemaMappingException, SchemaMappingValidationException, JAXBException {
57-
Unmarshaller um = context.createUnmarshaller();
58-
um.setAdapter(new AppSchemaAdapter(schemaMapping));
59-
um.setAdapter(new ComplexAttributeTypeAdapter(schemaMapping));
60-
um.setAdapter(new ComplexTypeAdapter(schemaMapping));
61-
um.setAdapter(new FeatureTypeAdapter(schemaMapping));
62-
um.setAdapter(new ObjectTypeAdapter(schemaMapping));
63-
um.setListener(new UnmarshalListener());
62+
private SchemaMapping unmarshal(SchemaMapping schemaMapping, InputSource input) throws SchemaMappingException, SchemaMappingValidationException, JAXBException {
63+
Unmarshaller unmarshaller = context.createUnmarshaller();
64+
unmarshaller.setAdapter(new AppSchemaAdapter(schemaMapping));
65+
unmarshaller.setAdapter(new ComplexAttributeTypeAdapter(schemaMapping));
66+
unmarshaller.setAdapter(new ComplexTypeAdapter(schemaMapping));
67+
unmarshaller.setAdapter(new FeatureTypeAdapter(schemaMapping));
68+
unmarshaller.setAdapter(new ObjectTypeAdapter(schemaMapping));
69+
unmarshaller.setListener(new UnmarshalListener());
6470

6571
// validate schema mapping
6672
ValidationEvent[] events = new ValidationEvent[1];
67-
um.setSchema(readSchema());
68-
um.setEventHandler(new ValidationEventHandler() {
69-
public boolean handleEvent(ValidationEvent event) {
70-
events[0] = event;
71-
return false;
72-
}
73+
unmarshaller.setSchema(readSchema());
74+
unmarshaller.setEventHandler(event -> {
75+
events[0] = event;
76+
return false;
7377
});
7478

75-
// unmarshal schema mapping
76-
Object result = null;
77-
try {
78-
if (input instanceof InputStream)
79-
result = um.unmarshal((InputStream) input);
80-
else if (input instanceof Reader)
81-
result = um.unmarshal((Reader) input);
79+
UnmarshallerHandler handler = unmarshaller.getUnmarshallerHandler();
8280

83-
if (!(result instanceof SchemaMapping))
84-
throw new SchemaMappingException("Failed to unmarshal input resource into a schema mapping.");
85-
86-
return (SchemaMapping)result;
87-
} catch (JAXBException e) {
88-
if (events[0] != null)
81+
try {
82+
SAXParserFactory saxParserFactory = SecureXMLProcessors.newSAXParserFactory();
83+
saxParserFactory.setNamespaceAware(true);
84+
XMLReader reader = saxParserFactory.newSAXParser().getXMLReader();
85+
reader.setContentHandler(handler);
86+
reader.parse(input);
87+
} catch (SAXException | ParserConfigurationException | IOException e) {
88+
if (events[0] != null) {
8989
throw new SchemaMappingValidationException(events[0].getMessage());
90+
} else {
91+
throw new SchemaMappingException("Failed to parse the schema mapping.", e);
92+
}
93+
}
9094

91-
throw e;
92-
} catch (Throwable e) {
93-
if (e.getCause() instanceof SchemaMappingException)
94-
throw (SchemaMappingException)e.getCause();
95+
// unmarshal schema mapping
96+
Object result = handler.getResult();
97+
if (!(result instanceof SchemaMapping))
98+
throw new SchemaMappingException("Failed to unmarshal input resource into a schema mapping.");
9599

96-
throw e;
97-
}
100+
return (SchemaMapping) result;
98101
}
99102

100103
public SchemaMapping unmarshal(SchemaMapping schemaMapping, InputStream inputStream) throws SchemaMappingException, SchemaMappingValidationException, JAXBException {
101-
return unmarshal(schemaMapping, (Object) inputStream);
104+
return unmarshal(schemaMapping, new InputSource(inputStream));
102105
}
103106

104107
public SchemaMapping unmarshal(InputStream inputStream) throws SchemaMappingException, SchemaMappingValidationException, JAXBException {
105-
return unmarshal(null, (Object) inputStream);
108+
return unmarshal(null, new InputSource(inputStream));
106109
}
107110

108111
public SchemaMapping unmarshal(SchemaMapping schemaMapping, URL resource) throws SchemaMappingException, SchemaMappingValidationException, JAXBException {
@@ -118,7 +121,7 @@ public SchemaMapping unmarshal(URL resource) throws SchemaMappingException, Sche
118121
}
119122

120123
public SchemaMapping unmarshal(SchemaMapping schemaMapping, File file) throws SchemaMappingException, SchemaMappingValidationException, JAXBException {
121-
try (InputStream inputStream = new FileInputStream(file)) {
124+
try (InputStream inputStream = Files.newInputStream(file.toPath())) {
122125
return unmarshal(schemaMapping, inputStream);
123126
} catch (IOException e) {
124127
throw new JAXBException("Failed to open schema mapping resource from file '" + file.getAbsolutePath() + "'.");
@@ -130,11 +133,7 @@ public SchemaMapping unmarshal(File file) throws SchemaMappingException, SchemaM
130133
}
131134

132135
public SchemaMapping unmarshal(SchemaMapping schemaMapping, String xml) throws SchemaMappingException, SchemaMappingValidationException, JAXBException {
133-
try (Reader reader = new StringReader(xml)) {
134-
return unmarshal(schemaMapping, reader);
135-
} catch (IOException e) {
136-
throw new JAXBException("Failed to open schema mapping resource from XML string.");
137-
}
136+
return unmarshal(schemaMapping, new InputSource(new StringReader(xml)));
138137
}
139138

140139
public SchemaMapping unmarshal(String xml) throws SchemaMappingException, SchemaMappingValidationException, JAXBException {
@@ -151,11 +150,9 @@ public void marshal(SchemaMapping schemaMapping, Writer writer, boolean prettyPr
151150
// validate schema mapping
152151
ValidationEvent[] events = new ValidationEvent[1];
153152
m.setSchema(readSchema());
154-
m.setEventHandler(new ValidationEventHandler() {
155-
public boolean handleEvent(ValidationEvent event) {
156-
events[0] = event;
157-
return false;
158-
}
153+
m.setEventHandler(event -> {
154+
events[0] = event;
155+
return false;
159156
});
160157

161158
try {
@@ -178,7 +175,7 @@ public void marshal(SchemaMapping schemaMapping, Writer writer) throws SchemaMap
178175
}
179176

180177
public void marshal(SchemaMapping schemaMapping, File file) throws SchemaMappingException, SchemaMappingValidationException, JAXBException {
181-
try (Writer writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8))) {
178+
try (Writer writer = new BufferedWriter(new OutputStreamWriter(Files.newOutputStream(file.toPath()), StandardCharsets.UTF_8))) {
182179
marshal(schemaMapping, writer);
183180
} catch (UnsupportedEncodingException e) {
184181
throw new JAXBException("Failed to marshal schema mapping.", e);
@@ -189,8 +186,9 @@ public void marshal(SchemaMapping schemaMapping, File file) throws SchemaMapping
189186

190187
private Schema readSchema() throws JAXBException {
191188
try {
192-
return SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI)
193-
.newSchema(SchemaMappingUtil.class.getResource("/org/citydb/core/database/schema/3dcitydb-schema.xsd"));
189+
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
190+
schemaFactory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
191+
return schemaFactory.newSchema(SchemaMappingUtil.class.getResource("/org/citydb/core/database/schema/3dcitydb-schema.xsd"));
194192
} catch (SAXException e) {
195193
throw new JAXBException("Failed to parse the schema mapping XSD schema. " +
196194
"Could not find '/org/citydb/core/database/schema/3dcitydb-schema.xsd' on the classpath.", e);

impexp-core/src/main/java/org/citydb/core/operation/exporter/writer/citygml/CityGMLWriterFactory.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import org.citydb.core.query.Query;
4242
import org.citydb.core.query.filter.type.FeatureTypeFilter;
4343
import org.citydb.util.log.Logger;
44+
import org.citydb.util.xml.SecureXMLProcessors;
4445
import org.citygml4j.model.module.Module;
4546
import org.citygml4j.model.module.ModuleContext;
4647
import org.citygml4j.model.module.Modules;
@@ -55,7 +56,6 @@
5556
import javax.xml.XMLConstants;
5657
import javax.xml.transform.Templates;
5758
import javax.xml.transform.TransformerConfigurationException;
58-
import javax.xml.transform.TransformerFactory;
5959
import javax.xml.transform.sax.SAXTransformerFactory;
6060
import javax.xml.transform.stream.StreamSource;
6161
import java.io.File;
@@ -94,7 +94,7 @@ public CityGMLWriterFactory(Query query, SchemaMapping schemaMapping, Config con
9494
log.info("Applying XSL transformations on export data.");
9595

9696
List<String> stylesheets = cityGMLOptions.getXSLTransformation().getStylesheets();
97-
SAXTransformerFactory factory = (SAXTransformerFactory) TransformerFactory.newInstance();
97+
SAXTransformerFactory factory = (SAXTransformerFactory) SecureXMLProcessors.newTransformerFactory();
9898
Templates[] templates = new Templates[stylesheets.size()];
9999

100100
for (int i = 0; i < stylesheets.size(); i++) {

impexp-core/src/main/java/org/citydb/core/operation/importer/reader/citygml/CityGMLReaderFactory.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.citydb.core.operation.importer.reader.FeatureReaderFactory;
3838
import org.citydb.core.registry.ObjectRegistry;
3939
import org.citydb.util.log.Logger;
40+
import org.citydb.util.xml.SecureXMLProcessors;
4041
import org.citygml4j.builder.jaxb.CityGMLBuilderException;
4142
import org.citygml4j.model.module.Module;
4243
import org.citygml4j.model.module.Modules;
@@ -49,7 +50,6 @@
4950
import javax.xml.namespace.QName;
5051
import javax.xml.transform.Templates;
5152
import javax.xml.transform.TransformerConfigurationException;
52-
import javax.xml.transform.TransformerFactory;
5353
import javax.xml.transform.sax.SAXTransformerFactory;
5454
import javax.xml.transform.stream.StreamSource;
5555
import java.io.File;
@@ -101,7 +101,7 @@ public void initializeContext(CityGMLFilter filter, Config config) throws Featur
101101
log.info("Applying XSL transformations to CityGML input features.");
102102

103103
List<String> stylesheets = cityGMLOptions.getXSLTransformation().getStylesheets();
104-
SAXTransformerFactory factory = (SAXTransformerFactory) TransformerFactory.newInstance();
104+
SAXTransformerFactory factory = (SAXTransformerFactory) SecureXMLProcessors.newTransformerFactory();
105105
Templates[] templates = new Templates[stylesheets.size()];
106106

107107
for (int i = 0; i < stylesheets.size(); i++) {

impexp-core/src/main/java/org/citydb/core/operation/validator/reader/citygml/CityGMLValidatorFactory.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public void initializeContext(Config config) throws ValidationException {
4848
try {
4949
SchemaHandler schemaHandler = SchemaHandler.newInstance();
5050
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
51+
schemaFactory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
5152
Schema schema = schemaFactory.newSchema(schemaHandler.getSchemaSources());
5253
validator = schema.newValidator();
5354
} catch (SAXException e) {

impexp-util/src/main/java/org/citydb/util/config/ConfigUtil.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,9 @@
2929

3030
import org.citydb.config.ProjectConfig;
3131
import org.citydb.config.gui.GuiConfig;
32-
import org.citydb.config.util.ConfigNamespaceFilter;
3332
import org.citydb.config.project.query.QueryWrapper;
33+
import org.citydb.config.util.ConfigNamespaceFilter;
34+
import org.citydb.util.xml.SecureXMLProcessors;
3435
import org.xml.sax.InputSource;
3536
import org.xml.sax.SAXException;
3637
import org.xml.sax.XMLReader;
@@ -95,19 +96,19 @@ public Object unmarshal(File file) throws JAXBException, IOException {
9596
public Object unmarshal(InputStream inputStream) throws JAXBException, IOException {
9697
Unmarshaller unmarshaller = createJAXBContext().context.createUnmarshaller();
9798
UnmarshallerHandler handler = unmarshaller.getUnmarshallerHandler();
98-
SAXParserFactory factory = SAXParserFactory.newInstance();
99-
factory.setNamespaceAware(true);
10099

101100
ConfigNamespaceFilter namespaceFilter;
102101
try {
102+
SAXParserFactory factory = SecureXMLProcessors.newSAXParserFactory();
103+
factory.setNamespaceAware(true);
103104
XMLReader reader = factory.newSAXParser().getXMLReader();
104105
namespaceFilter = new ConfigNamespaceFilter(reader);
105106
namespaceFilter.setContentHandler(handler);
106107
namespaceFilter.parse(new InputSource(inputStream));
107108
} catch (SAXException e) {
108109
throw new JAXBException(e.getMessage());
109110
} catch (ParserConfigurationException e) {
110-
throw new IOException(e.getMessage());
111+
throw new IOException("Failed to create secure XML reader.", e);
111112
}
112113

113114
Object result = handler.getResult();
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* 3D City Database - The Open Source CityGML Database
3+
* https://www.3dcitydb.org/
4+
*
5+
* Copyright 2013 - 2022
6+
* Chair of Geoinformatics
7+
* Technical University of Munich, Germany
8+
* https://www.lrg.tum.de/gis/
9+
*
10+
* The 3D City Database is jointly developed with the following
11+
* cooperation partners:
12+
*
13+
* Virtual City Systems, Berlin <https://vc.systems/>
14+
* M.O.S.S. Computer Grafik Systeme GmbH, Taufkirchen <http://www.moss.de/>
15+
*
16+
* Licensed under the Apache License, Version 2.0 (the "License");
17+
* you may not use this file except in compliance with the License.
18+
* You may obtain a copy of the License at
19+
*
20+
* http://www.apache.org/licenses/LICENSE-2.0
21+
*
22+
* Unless required by applicable law or agreed to in writing, software
23+
* distributed under the License is distributed on an "AS IS" BASIS,
24+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
25+
* See the License for the specific language governing permissions and
26+
* limitations under the License.
27+
*/
28+
29+
package org.citydb.util.xml;
30+
31+
import org.xml.sax.SAXNotRecognizedException;
32+
import org.xml.sax.SAXNotSupportedException;
33+
34+
import javax.xml.XMLConstants;
35+
import javax.xml.parsers.DocumentBuilderFactory;
36+
import javax.xml.parsers.ParserConfigurationException;
37+
import javax.xml.parsers.SAXParserFactory;
38+
import javax.xml.transform.TransformerConfigurationException;
39+
import javax.xml.transform.TransformerFactory;
40+
41+
public class SecureXMLProcessors {
42+
43+
private SecureXMLProcessors() {
44+
}
45+
46+
public static SAXParserFactory newSAXParserFactory() throws SAXNotSupportedException, SAXNotRecognizedException, ParserConfigurationException {
47+
SAXParserFactory factory = SAXParserFactory.newInstance();
48+
factory.setXIncludeAware(false);
49+
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
50+
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
51+
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
52+
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
53+
return factory;
54+
}
55+
56+
public static DocumentBuilderFactory newDocumentBuilderFactory() throws ParserConfigurationException {
57+
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
58+
factory.setXIncludeAware(false);
59+
factory.setExpandEntityReferences(false);
60+
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
61+
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
62+
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
63+
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
64+
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
65+
return factory;
66+
}
67+
68+
public static TransformerFactory newTransformerFactory() throws TransformerConfigurationException {
69+
TransformerFactory factory = TransformerFactory.newInstance();
70+
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
71+
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
72+
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
73+
return factory;
74+
}
75+
}

0 commit comments

Comments
 (0)