Invalid JSON request body caused endless loop (#26680)
Request bodys that only consists of a String value can lead to endless loops in the parser of several rest requests like e.g. `_count`. Up to 5.2 this seems to have been caught in the logic guessing the content type of the request, but since then it causes the node to block. This change introduces checks for receiving a valid xContent object before starting the parsing in RestActions#parseTopLevelQueryBuilder(). Closes #26083
This commit is contained in:
parent
6c46a67dd6
commit
2db3bccd37
|
@ -243,6 +243,15 @@ public class RestActions {
|
||||||
private static QueryBuilder parseTopLevelQueryBuilder(XContentParser parser) {
|
private static QueryBuilder parseTopLevelQueryBuilder(XContentParser parser) {
|
||||||
try {
|
try {
|
||||||
QueryBuilder queryBuilder = null;
|
QueryBuilder queryBuilder = null;
|
||||||
|
XContentParser.Token first = parser.nextToken();
|
||||||
|
if (first == null) {
|
||||||
|
return null;
|
||||||
|
} else if (first != XContentParser.Token.START_OBJECT) {
|
||||||
|
throw new ParsingException(
|
||||||
|
parser.getTokenLocation(), "Expected [" + XContentParser.Token.START_OBJECT +
|
||||||
|
"] but found [" + first + "]", parser.getTokenLocation()
|
||||||
|
);
|
||||||
|
}
|
||||||
for (XContentParser.Token token = parser.nextToken(); token != XContentParser.Token.END_OBJECT; token = parser.nextToken()) {
|
for (XContentParser.Token token = parser.nextToken(); token != XContentParser.Token.END_OBJECT; token = parser.nextToken()) {
|
||||||
if (token == XContentParser.Token.FIELD_NAME) {
|
if (token == XContentParser.Token.FIELD_NAME) {
|
||||||
String fieldName = parser.currentName();
|
String fieldName = parser.currentName();
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
|
|
||||||
package org.elasticsearch.rest.action;
|
package org.elasticsearch.rest.action;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.io.JsonEOFException;
|
||||||
|
import java.util.Arrays;
|
||||||
import org.elasticsearch.common.ParsingException;
|
import org.elasticsearch.common.ParsingException;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
||||||
|
@ -59,10 +61,32 @@ public class RestActionsTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testParseTopLevelBuilderEmptyObject() throws IOException {
|
public void testParseTopLevelBuilderEmptyObject() throws IOException {
|
||||||
String requestBody = "{}";
|
for (String requestBody : Arrays.asList("{}", "")) {
|
||||||
try (XContentParser parser = createParser(JsonXContent.jsonXContent, requestBody)) {
|
try (XContentParser parser = createParser(JsonXContent.jsonXContent, requestBody)) {
|
||||||
QueryBuilder query = RestActions.getQueryContent(parser);
|
QueryBuilder query = RestActions.getQueryContent(parser);
|
||||||
assertNull(query);
|
assertNull(query);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testParseTopLevelBuilderMalformedJson() throws IOException {
|
||||||
|
for (String requestBody : Arrays.asList("\"\"", "\"someString\"", "\"{\"")) {
|
||||||
|
try (XContentParser parser = createParser(JsonXContent.jsonXContent, requestBody)) {
|
||||||
|
ParsingException exception =
|
||||||
|
expectThrows(ParsingException.class, () -> RestActions.getQueryContent(parser));
|
||||||
|
assertEquals("Expected [START_OBJECT] but found [VALUE_STRING]", exception.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testParseTopLevelBuilderIncompleteJson() throws IOException {
|
||||||
|
for (String requestBody : Arrays.asList("{", "{ \"query\" :")) {
|
||||||
|
try (XContentParser parser = createParser(JsonXContent.jsonXContent, requestBody)) {
|
||||||
|
ParsingException exception =
|
||||||
|
expectThrows(ParsingException.class, () -> RestActions.getQueryContent(parser));
|
||||||
|
assertEquals("Failed to parse", exception.getMessage());
|
||||||
|
assertEquals(JsonEOFException.class, exception.getRootCause().getClass());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue