Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2020 Google Inc.
* Copyright 2026 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -61,10 +61,12 @@
* XmlPullParser) and assigns specific elements read from the XmlPullParser to the container.
*/

/* package */ static final int MAX_CONTAINER_DEPTH = 20;

/* package */
static KmlContainer createContainer(XmlPullParser parser)
throws XmlPullParserException, IOException {
return assignPropertiesToContainer(parser);
return assignPropertiesToContainer(parser, MAX_CONTAINER_DEPTH);
}

/**
Expand All @@ -74,8 +76,12 @@ static KmlContainer createContainer(XmlPullParser parser)
* @param parser XmlPullParser object reading from a KML file
* @return KmlContainer object with properties read from the XmlPullParser
*/
private static KmlContainer assignPropertiesToContainer(XmlPullParser parser)
/* package */ static KmlContainer assignPropertiesToContainer(XmlPullParser parser, int maxDepth)
throws XmlPullParserException, IOException {
if (maxDepth < 0) {
KmlParser.skip(parser);
return null;
}
String startTag = parser.getName();
String containerId = null;
HashMap<String, String> containerProperties = new HashMap<String, String>();
Expand All @@ -97,7 +103,10 @@ private static KmlContainer assignPropertiesToContainer(XmlPullParser parser)
if (parser.getName().matches(UNSUPPORTED_REGEX)) {
KmlParser.skip(parser);
} else if (parser.getName().matches(CONTAINER_REGEX)) {
nestedContainers.add(assignPropertiesToContainer(parser));
KmlContainer container = assignPropertiesToContainer(parser, maxDepth - 1);
if (container != null) {
nestedContainers.add(container);
}
} else if (parser.getName().matches(PROPERTY_REGEX)) {
containerProperties.put(parser.getName(), parser.nextText());
} else if (parser.getName().equals(STYLE_MAP)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2020 Google Inc.
* Copyright 2026 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -182,8 +182,19 @@ private static String getImageUrl(XmlPullParser parser)
*
* @param geometryType Type of geometry object to create
*/
private static Geometry createGeometry(XmlPullParser parser, String geometryType)
/* package */ static final int MAX_GEOMETRY_DEPTH = 20;

/* package */ static Geometry createGeometry(XmlPullParser parser, String geometryType)
throws IOException, XmlPullParserException {
return createGeometry(parser, geometryType, MAX_GEOMETRY_DEPTH);
}

/* package */ static Geometry createGeometry(XmlPullParser parser, String geometryType, int maxDepth)
throws IOException, XmlPullParserException {
if (maxDepth < 0) {
KmlParser.skip(parser);
return null;
}
int eventType = parser.getEventType();
while (!(eventType == END_TAG && parser.getName().equals(geometryType))) {
if (eventType == START_TAG) {
Expand All @@ -196,7 +207,7 @@ private static Geometry createGeometry(XmlPullParser parser, String geometryType
} else if (parser.getName().equals("Polygon")) {
return createPolygon(parser);
} else if (parser.getName().equals("MultiGeometry")) {
return createMultiGeometry(parser);
return createMultiGeometry(parser, maxDepth);
} else if (parser.getName().equals("MultiTrack")) {
return createMultiTrack(parser);
}
Expand Down Expand Up @@ -349,14 +360,17 @@ private static KmlPolygon createPolygon(XmlPullParser parser)
*
* @return KmlMultiGeometry object
*/
private static KmlMultiGeometry createMultiGeometry(XmlPullParser parser)
private static KmlMultiGeometry createMultiGeometry(XmlPullParser parser, int maxDepth)
throws XmlPullParserException, IOException {
ArrayList<Geometry> geometries = new ArrayList<Geometry>();
// Get next otherwise have an infinite loop
int eventType = parser.next();
while (!(eventType == END_TAG && parser.getName().equals("MultiGeometry"))) {
if (eventType == START_TAG && parser.getName().matches(GEOMETRY_REGEX)) {
geometries.add(createGeometry(parser, parser.getName()));
Geometry geom = createGeometry(parser, parser.getName(), maxDepth - 1);
if (geom != null) {
geometries.add(geom);
}
}
eventType = parser.next();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2020 Google Inc.
* Copyright 2026 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -125,4 +125,52 @@ public void testSuitableCoordinates() throws Exception {
assertEquals(latLng.latitude, -43.60505741890396, 0.001);
assertEquals(latLng.longitude, 170.1435558771009, 0.001);
}

@Test
public void testDeeplyNestedMultiGeometry_doesNotThrowStackOverflow() throws Exception {
StringBuilder sb = new StringBuilder("<Placemark>");
for (int i = 0; i < 200; i++) {
sb.append("<MultiGeometry>");
}
sb.append("<Point><coordinates>0,0</coordinates></Point>");
for (int i = 0; i < 200; i++) {
sb.append("</MultiGeometry>");
}
sb.append("</Placemark>");
XmlPullParser parser = KmlTestUtil.createParserFromString(sb.toString());
parser.next();
KmlPlacemark placemark = KmlFeatureParser.createPlacemark(parser);
assertNotNull(placemark);
}

@Test
public void testGeometryExceedingMaxDepth_returnsNull() throws Exception {
XmlPullParser parser = KmlTestUtil.createParserFromString("<Point><coordinates>0,0</coordinates></Point>");
parser.next();
assertNull(KmlFeatureParser.createGeometry(parser, "Point", -1));
}

@Test
public void testDeeplyNestedFolder_doesNotThrowStackOverflow() throws Exception {
StringBuilder sb = new StringBuilder("<Folder>");
for (int i = 0; i < 200; i++) {
sb.append("<Folder>");
}
sb.append("<name>Deep Folder</name>");
for (int i = 0; i < 200; i++) {
sb.append("</Folder>");
}
sb.append("</Folder>");
XmlPullParser parser = KmlTestUtil.createParserFromString(sb.toString());
parser.next();
KmlContainer container = KmlContainerParser.assignPropertiesToContainer(parser, 20);
assertNotNull(container);
}

@Test
public void testContainerExceedingMaxDepth_returnsNull() throws Exception {
XmlPullParser parser = KmlTestUtil.createParserFromString("<Folder><name>Test</name></Folder>");
parser.next();
assertNull(KmlContainerParser.assignPropertiesToContainer(parser, -1));
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2020 Google Inc.
* Copyright 2026 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -43,4 +43,14 @@ static XmlPullParser createParser(String fileName) throws XmlPullParserException
parser.setInput(stream, null);
return parser;
}

static XmlPullParser createParserFromString(String xml) throws XmlPullParserException, IOException {
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setFeature(XmlPullParser.FEATURE_PROCESS_DOCDECL, false);
factory.setFeature(XmlPullParser.FEATURE_VALIDATION, false);
factory.setNamespaceAware(true);
XmlPullParser parser = factory.newPullParser();
parser.setInput(new java.io.StringReader(xml));
return parser;
}
}
Loading