diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/watch/WatchParser.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/watch/WatchParser.java index 524913105823a..a825295c7c8a3 100644 --- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/watch/WatchParser.java +++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/watch/WatchParser.java @@ -5,6 +5,7 @@ */ package org.elasticsearch.xpack.watcher.watch; +import com.fasterxml.jackson.core.JsonParseException; import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.bytes.BytesReference; @@ -174,6 +175,14 @@ public Watch parse(String id, boolean includeStatus, WatcherXContentParser parse throw new ElasticsearchParseException("could not parse watch [{}]. unexpected field [{}]", id, currentFieldName); } } + + // Make sure we are at the end of the available input data -- certain types of JSON errors will not manifest + // until we try to consume additional tokens. + + if (parser.nextToken() != null) { + throw new ElasticsearchParseException("could not parse watch [{}]. expected end of payload, but received additional " + + "data at [line: {}, column: {}]", id, parser.getTokenLocation().lineNumber, parser.getTokenLocation().columnNumber); + } if (trigger == null) { throw new ElasticsearchParseException("could not parse watch [{}]. missing required field [{}]", id, WatchField.TRIGGER.getPreferredName()); diff --git a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/watch/WatchTests.java b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/watch/WatchTests.java index df96a802166e2..09e94ed916505 100644 --- a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/watch/WatchTests.java +++ b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/watch/WatchTests.java @@ -11,14 +11,18 @@ import org.elasticsearch.action.support.WriteRequest; import org.elasticsearch.client.Client; import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.common.xcontent.DeprecationHandler; import org.elasticsearch.common.xcontent.NamedXContentRegistry; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.common.xcontent.json.JsonXContent; +import org.elasticsearch.common.xcontent.json.JsonXContentParser; import org.elasticsearch.index.query.MatchAllQueryBuilder; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.ScriptQueryBuilder; @@ -121,6 +125,7 @@ import org.junit.Before; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.time.Clock; import java.time.Instant; import java.time.ZoneOffset; @@ -143,6 +148,8 @@ import static org.elasticsearch.xpack.watcher.input.InputBuilders.searchInput; import static org.elasticsearch.xpack.watcher.test.WatcherTestUtils.templateRequest; import static org.elasticsearch.xpack.watcher.trigger.TriggerBuilders.schedule; + +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.instanceOf; @@ -296,6 +303,47 @@ public void testParserBadActions() throws Exception { } } + public void testParserConsumesEntireDefinition() throws Exception { + WatchParser wp = createWatchparser(); + try (XContentBuilder builder = jsonBuilder()) { + builder.startObject(); + { + builder.startObject("trigger"); + { + builder.startObject("schedule"); + { + builder.field("interval", "10s"); + } + builder.endObject(); + } + builder.endObject(); + builder.startObject("input"); + { + builder.startObject("simple"); + { + } + builder.endObject(); + } + builder.endObject(); + builder.startObject("condition"); + { + builder.startObject("script"); + { + builder.field("source", "return false"); + } + builder.endObject(); + } + builder.endObject(); + } + builder.endObject(); + builder.generator().writeBinary(",".getBytes(StandardCharsets.UTF_8)); + ElasticsearchParseException e = expectThrows( + ElasticsearchParseException.class, + () -> wp.parseWithSecrets("failure", false, BytesReference.bytes(builder), new DateTime(), XContentType.JSON, true)); + assertThat(e.getMessage(), containsString("expected end of payload")); + } + } + public void testParserDefaults() throws Exception { Schedule schedule = randomSchedule(); ScheduleRegistry scheduleRegistry = registry(schedule);