Skip to content

Commit

Permalink
Avoid infinite loop in flat_object parsing
Browse files Browse the repository at this point in the history
We had logic in flat_object parsing that would:

1. Try parsing a flat object field that is not an object or null.
2. Would see an END_ARRAY token, ignore it, and not advance the parser.

Combined, this would create a scenario where passing an array of
strings for a flat_object would parse the string values, then loop
infinitely on the END_ARRAY token.

Signed-off-by: Michael Froh <froh@amazon.com>
  • Loading branch information
msfroh committed Sep 18, 2024
1 parent 4802d0d commit 1e1e7fe
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,17 @@ setup:
},
"required_matches": 1
}
# The following document is invalid.
- do:
catch: /parsing_exception/
index:
index: test
id: 3
body: {
"ISBN13": "V12154942123242",
"catalog": ["Arrays in Action"],
"required_matches": 1
}

# Do index refresh
- do:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
package org.opensearch.common.xcontent;

import org.opensearch.common.xcontent.json.JsonXContent;
import org.opensearch.core.common.ParsingException;
import org.opensearch.core.common.Strings;
import org.opensearch.core.common.bytes.BytesReference;
import org.opensearch.core.xcontent.AbstractXContentParser;
Expand Down Expand Up @@ -117,8 +118,6 @@ private boolean parseToken(Deque<String> path, String currentFieldName) throws I
isChildrenValueValid |= parseToken(path, currentFieldName);
}
this.parser.nextToken();
} else if (this.parser.currentToken() == Token.END_ARRAY) {
// skip
} else if (this.parser.currentToken() == Token.START_OBJECT) {
parser.nextToken();
while (this.parser.currentToken() != Token.END_OBJECT) {
Expand Down Expand Up @@ -172,7 +171,7 @@ private String parseValue() throws IOException {
return this.parser.textOrNull();
// Handle other token types as needed
default:
throw new IOException("Unsupported value token type [" + parser.currentToken() + "]");
throw new ParsingException(parser.getTokenLocation(), "Unexpected value token type [" + parser.currentToken() + "]");
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.opensearch.common.lucene.Lucene;
import org.opensearch.common.lucene.search.AutomatonQueries;
import org.opensearch.common.xcontent.JsonToStringXContentParser;
import org.opensearch.core.common.ParsingException;
import org.opensearch.core.xcontent.DeprecationHandler;
import org.opensearch.core.xcontent.NamedXContentRegistry;
import org.opensearch.core.xcontent.XContentParser;
Expand Down Expand Up @@ -568,31 +569,41 @@ protected void parseCreateField(ParseContext context) throws IOException {
if (context.externalValueSet()) {
String value = context.externalValue().toString();
parseValueAddFields(context, value, fieldType().name());
} else if (context.parser().currentToken() != XContentParser.Token.VALUE_NULL) {
JsonToStringXContentParser jsonToStringParser = new JsonToStringXContentParser(
NamedXContentRegistry.EMPTY,
DeprecationHandler.IGNORE_DEPRECATIONS,
context.parser(),
fieldType().name()
);
/*
JsonToStringParser is the main parser class to transform JSON into stringFields in a XContentParser
It reads the JSON object and parsed to a list of string
*/
XContentParser parser = jsonToStringParser.parseObject();

XContentParser.Token currentToken;
while ((currentToken = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
switch (currentToken) {
case FIELD_NAME:
fieldName = parser.currentName();
break;
case VALUE_STRING:
String value = parser.textOrNull();
parseValueAddFields(context, value, fieldName);
break;
} else {
XContentParser ctxParser = context.parser();
if (ctxParser.currentToken() != XContentParser.Token.VALUE_NULL) {
if (ctxParser.currentToken() != XContentParser.Token.START_OBJECT) {
throw new ParsingException(
ctxParser.getTokenLocation(),
"[" + this.name() + "] unexpected token [" + ctxParser.currentToken() + "] in flat_object field value"
);
}

JsonToStringXContentParser jsonToStringParser = new JsonToStringXContentParser(
NamedXContentRegistry.EMPTY,
DeprecationHandler.IGNORE_DEPRECATIONS,
ctxParser,
fieldType().name()
);
/*
JsonToStringParser is the main parser class to transform JSON into stringFields in a XContentParser
It reads the JSON object and parsed to a list of string
*/
XContentParser parser = jsonToStringParser.parseObject();

XContentParser.Token currentToken;
while ((currentToken = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
switch (currentToken) {
case FIELD_NAME:
fieldName = parser.currentName();
break;
case VALUE_STRING:
String value = parser.textOrNull();
parseValueAddFields(context, value, fieldName);
break;
}

}
}
}

Expand Down

0 comments on commit 1e1e7fe

Please sign in to comment.