diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/datafeed/DatafeedConfig.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/datafeed/DatafeedConfig.java index 84deae61f8e62..624d5960c79ad 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/datafeed/DatafeedConfig.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/datafeed/DatafeedConfig.java @@ -286,11 +286,11 @@ public Builder(DatafeedConfig config) { this.jobId = config.jobId; this.queryDelay = config.queryDelay; this.frequency = config.frequency; - this.indices = config.indices; - this.types = config.types; + this.indices = config.indices == null ? null : new ArrayList<>(config.indices); + this.types = config.types == null ? null : new ArrayList<>(config.types); this.query = config.query; this.aggregations = config.aggregations; - this.scriptFields = config.scriptFields; + this.scriptFields = config.scriptFields == null ? null : new ArrayList<>(config.scriptFields); this.scrollSize = config.scrollSize; this.chunkingConfig = config.chunkingConfig; } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/job/config/Job.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/job/config/Job.java index f5d6711776ec4..9f5a8001c765a 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/job/config/Job.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ml/job/config/Job.java @@ -28,8 +28,10 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import java.io.IOException; +import java.util.ArrayList; import java.util.Collections; import java.util.Date; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -449,7 +451,7 @@ public Builder(String id) { public Builder(Job job) { this.id = job.getId(); this.jobType = job.getJobType(); - this.groups = job.getGroups(); + this.groups = new ArrayList<>(job.getGroups()); this.description = job.getDescription(); this.analysisConfig = job.getAnalysisConfig(); this.analysisLimits = job.getAnalysisLimits(); @@ -463,7 +465,7 @@ public Builder(Job job) { this.backgroundPersistInterval = job.getBackgroundPersistInterval(); this.modelSnapshotRetentionDays = job.getModelSnapshotRetentionDays(); this.resultsRetentionDays = job.getResultsRetentionDays(); - this.customSettings = job.getCustomSettings(); + this.customSettings = job.getCustomSettings() == null ? null : new HashMap<>(job.getCustomSettings()); this.modelSnapshotId = job.getModelSnapshotId(); this.resultsIndexName = job.getResultsIndexNameNoPrefix(); this.deleting = job.getDeleting(); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/datafeed/DatafeedConfig.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/datafeed/DatafeedConfig.java index afe38dc29bf7f..e12f472299e73 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/datafeed/DatafeedConfig.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/datafeed/DatafeedConfig.java @@ -38,6 +38,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -461,14 +462,14 @@ public Builder(DatafeedConfig config) { this.jobId = config.jobId; this.queryDelay = config.queryDelay; this.frequency = config.frequency; - this.indices = config.indices; - this.types = config.types; + this.indices = new ArrayList<>(config.indices); + this.types = new ArrayList<>(config.types); this.query = config.query; this.aggregations = config.aggregations; - this.scriptFields = config.scriptFields; + this.scriptFields = config.scriptFields == null ? null : new ArrayList<>(config.scriptFields); this.scrollSize = config.scrollSize; this.chunkingConfig = config.chunkingConfig; - this.headers = config.headers; + this.headers = new HashMap<>(config.headers); } public void setId(String datafeedId) { diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/config/Job.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/config/Job.java index 95ba05f4dc8e4..1d0bc90101343 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/config/Job.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/config/Job.java @@ -32,6 +32,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Date; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -664,7 +665,7 @@ public Builder(Job job) { this.id = job.getId(); this.jobType = job.getJobType(); this.jobVersion = job.getJobVersion(); - this.groups = job.getGroups(); + this.groups = new ArrayList<>(job.getGroups()); this.description = job.getDescription(); this.analysisConfig = job.getAnalysisConfig(); this.analysisLimits = job.getAnalysisLimits(); @@ -678,7 +679,7 @@ public Builder(Job job) { this.backgroundPersistInterval = job.getBackgroundPersistInterval(); this.modelSnapshotRetentionDays = job.getModelSnapshotRetentionDays(); this.resultsRetentionDays = job.getResultsRetentionDays(); - this.customSettings = job.getCustomSettings(); + this.customSettings = job.getCustomSettings() == null ? null : new HashMap<>(job.getCustomSettings()); this.modelSnapshotId = job.getModelSnapshotId(); this.resultsIndexName = job.getResultsIndexNameNoPrefix(); this.deleting = job.isDeleting(); diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/datafeed/DatafeedConfigTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/datafeed/DatafeedConfigTests.java index ffc13655d229c..a3cffe0dc8c4a 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/datafeed/DatafeedConfigTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/datafeed/DatafeedConfigTests.java @@ -6,7 +6,6 @@ package org.elasticsearch.xpack.core.ml.datafeed; import com.carrotsearch.randomizedtesting.generators.CodepointSetGenerator; - import org.elasticsearch.ElasticsearchException; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.io.stream.Writeable; @@ -490,6 +489,13 @@ public void testDefaultFrequency_GivenAggregationsWithHistogramInterval_1_Hour() assertEquals(TimeValue.timeValueHours(1), datafeed.defaultFrequency(TimeValue.timeValueHours(12))); } + public void testCopyingDatafeedDoesNotCauseStackOverflow() { + DatafeedConfig datafeed = createTestInstance(); + for (int i = 0; i < 100000; i++) { + datafeed = new DatafeedConfig.Builder(datafeed).build(); + } + } + public static String randomValidDatafeedId() { CodepointSetGenerator generator = new CodepointSetGenerator("abcdefghijklmnopqrstuvwxyz".toCharArray()); return generator.ofCodePointsLength(random(), 10, 10); diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/job/config/JobTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/job/config/JobTests.java index 4b4f20fe88615..b0b826cd54134 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/job/config/JobTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/job/config/JobTests.java @@ -580,6 +580,13 @@ public void testEarliestValidTimestamp_GivenDataCountsAndLatency() { assertThat(builder.build().earliestValidTimestamp(dataCounts), equalTo(123455789L)); } + public void testCopyingJobDoesNotCauseStackOverflow() { + Job job = createRandomizedJob(); + for (int i = 0; i < 100000; i++) { + job = new Job.Builder(job).build(); + } + } + public static Job.Builder buildJobBuilder(String id, Date date) { Job.Builder builder = new Job.Builder(id); builder.setCreateTime(date);