[GEO] Fix for NPE enclosed in SearchParseException for a "geo_shape" filter or query
This fix adds better error handling for parsing multipoint, linestring, and polygon GeoJSONs. Current logic throws a NPE when parsing a multipoint, linestring, or polygon that does not comply with the GeoJSON specification. That is, if a user provides a single coordinate instead of an array of coordinates, or array of linestrings, the ShapeParser throws a NPE wrapped in a SearchParseException instead of a more useful error message. Closes #8432
This commit is contained in:
parent
f308049a90
commit
aa644e3ad7
|
@ -346,6 +346,10 @@ public abstract class ShapeBuilder implements ToXContent {
|
|||
this.coordinate = null;
|
||||
}
|
||||
|
||||
protected boolean isEmpty() {
|
||||
return (coordinate == null && (children == null || children.isEmpty()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
if (children == null) {
|
||||
|
@ -607,7 +611,19 @@ public abstract class ShapeBuilder implements ToXContent {
|
|||
}
|
||||
}
|
||||
|
||||
protected static void validatePointNode(CoordinateNode node) {
|
||||
if (node.isEmpty()) {
|
||||
throw new ElasticsearchParseException("Invalid number of points (0) provided when expecting a single coordinate "
|
||||
+ "([lat, lng])");
|
||||
} else if (node.coordinate == null) {
|
||||
if (node.children.isEmpty() == false) {
|
||||
throw new ElasticsearchParseException("multipoint data provided when single point data expected.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static PointBuilder parsePoint(CoordinateNode node) {
|
||||
validatePointNode(node);
|
||||
return newPoint(node.coordinate);
|
||||
}
|
||||
|
||||
|
@ -619,7 +635,24 @@ public abstract class ShapeBuilder implements ToXContent {
|
|||
return newEnvelope().topLeft(coordinates.children.get(0).coordinate).bottomRight(coordinates.children.get(1).coordinate);
|
||||
}
|
||||
|
||||
protected static void validateMultiPointNode(CoordinateNode coordinates) {
|
||||
if (coordinates.children == null || coordinates.children.isEmpty()) {
|
||||
if (coordinates.coordinate != null) {
|
||||
throw new ElasticsearchParseException("single coordinate found when expecting an array of " +
|
||||
"coordinates. change type to point or change data to an array of >0 coordinates");
|
||||
}
|
||||
throw new ElasticsearchParseException("No data provided for multipoint object when expecting " +
|
||||
">0 points (e.g., [[lat, lng]] or [[lat, lng], ...])");
|
||||
} else {
|
||||
for (CoordinateNode point : coordinates.children) {
|
||||
validatePointNode(point);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static MultiPointBuilder parseMultiPoint(CoordinateNode coordinates) {
|
||||
validateMultiPointNode(coordinates);
|
||||
|
||||
MultiPointBuilder points = new MultiPointBuilder();
|
||||
for (CoordinateNode node : coordinates.children) {
|
||||
points.point(node.coordinate);
|
||||
|
@ -671,6 +704,11 @@ public abstract class ShapeBuilder implements ToXContent {
|
|||
}
|
||||
|
||||
protected static PolygonBuilder parsePolygon(CoordinateNode coordinates) {
|
||||
if (coordinates.children == null || coordinates.children.isEmpty()) {
|
||||
throw new ElasticsearchParseException("Invalid LinearRing provided for type polygon. Linear ring must be an array of " +
|
||||
"coordinates");
|
||||
}
|
||||
|
||||
LineStringBuilder shell = parseLinearRing(coordinates.children.get(0));
|
||||
PolygonBuilder polygon = new PolygonBuilder(shell.points);
|
||||
for (int i = 1; i < coordinates.children.size(); i++) {
|
||||
|
|
|
@ -157,6 +157,58 @@ public class GeoJSONShapeParserTests extends ElasticsearchTestCase {
|
|||
assertGeometryEquals(jtsGeom(expected), polygonGeoJson);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParse_invalidPoint() throws IOException {
|
||||
// test case 1: create an invalid point object with multipoint data format
|
||||
String invalidPoint1 = XContentFactory.jsonBuilder().startObject().field("type", "point")
|
||||
.startArray("coordinates")
|
||||
.startArray().value(-74.011).value(40.753).endArray()
|
||||
.endArray()
|
||||
.endObject().string();
|
||||
XContentParser parser = JsonXContent.jsonXContent.createParser(invalidPoint1);
|
||||
parser.nextToken();
|
||||
ElasticsearchGeoAssertions.assertValidException(parser, ElasticsearchParseException.class);
|
||||
|
||||
// test case 2: create an invalid point object with an empty number of coordinates
|
||||
String invalidPoint2 = XContentFactory.jsonBuilder().startObject().field("type", "point")
|
||||
.startArray("coordinates")
|
||||
.endArray()
|
||||
.endObject().string();
|
||||
parser = JsonXContent.jsonXContent.createParser(invalidPoint2);
|
||||
parser.nextToken();
|
||||
ElasticsearchGeoAssertions.assertValidException(parser, ElasticsearchParseException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParse_invalidMultipoint() throws IOException {
|
||||
// test case 1: create an invalid multipoint object with single coordinate
|
||||
String invalidMultipoint1 = XContentFactory.jsonBuilder().startObject().field("type", "multipoint")
|
||||
.startArray("coordinates").value(-74.011).value(40.753).endArray()
|
||||
.endObject().string();
|
||||
XContentParser parser = JsonXContent.jsonXContent.createParser(invalidMultipoint1);
|
||||
parser.nextToken();
|
||||
ElasticsearchGeoAssertions.assertValidException(parser, ElasticsearchParseException.class);
|
||||
|
||||
// test case 2: create an invalid multipoint object with null coordinate
|
||||
String invalidMultipoint2 = XContentFactory.jsonBuilder().startObject().field("type", "multipoint")
|
||||
.startArray("coordinates")
|
||||
.endArray()
|
||||
.endObject().string();
|
||||
parser = JsonXContent.jsonXContent.createParser(invalidMultipoint2);
|
||||
parser.nextToken();
|
||||
ElasticsearchGeoAssertions.assertValidException(parser, ElasticsearchParseException.class);
|
||||
|
||||
// test case 3: create a valid formatted multipoint object with invalid number (0) of coordinates
|
||||
String invalidMultipoint3 = XContentFactory.jsonBuilder().startObject().field("type", "multipoint")
|
||||
.startArray("coordinates")
|
||||
.startArray().endArray()
|
||||
.endArray()
|
||||
.endObject().string();
|
||||
parser = JsonXContent.jsonXContent.createParser(invalidMultipoint3);
|
||||
parser.nextToken();
|
||||
ElasticsearchGeoAssertions.assertValidException(parser, ElasticsearchParseException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParse_invalidPolygon() throws IOException {
|
||||
/**
|
||||
|
@ -225,6 +277,15 @@ public class GeoJSONShapeParserTests extends ElasticsearchTestCase {
|
|||
parser = JsonXContent.jsonXContent.createParser(invalidPoly5);
|
||||
parser.nextToken();
|
||||
ElasticsearchGeoAssertions.assertValidException(parser, ElasticsearchIllegalArgumentException.class);
|
||||
|
||||
// test case 6: create an invalid polygon with 0 LinearRings
|
||||
String invalidPoly6 = XContentFactory.jsonBuilder().startObject().field("type", "polygon")
|
||||
.startArray("coordinates").endArray()
|
||||
.endObject().string();
|
||||
|
||||
parser = JsonXContent.jsonXContent.createParser(invalidPoly6);
|
||||
parser.nextToken();
|
||||
ElasticsearchGeoAssertions.assertValidException(parser, ElasticsearchParseException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
Loading…
Reference in New Issue