Skip to content

Commit f784d0a

Browse files
[ML] Validate ML filter_id (#31535)
Like job and datafeed ids, the filter id should be validated with the same rules to avoid document ids that can be problematic.
1 parent 79cdcee commit f784d0a

File tree

5 files changed

+39
-6
lines changed

5 files changed

+39
-6
lines changed

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/config/MlFilter.java

+8-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
import org.elasticsearch.common.xcontent.ToXContentObject;
1616
import org.elasticsearch.common.xcontent.XContentBuilder;
1717
import org.elasticsearch.xpack.core.ml.MlMetaIndex;
18+
import org.elasticsearch.xpack.core.ml.job.messages.Messages;
19+
import org.elasticsearch.xpack.core.ml.utils.ExceptionsHelper;
20+
import org.elasticsearch.xpack.core.ml.utils.MlStrings;
1821

1922
import java.io.IOException;
2023
import java.util.Arrays;
@@ -57,7 +60,7 @@ private static ObjectParser<Builder, Void> createParser(boolean ignoreUnknownFie
5760
private final SortedSet<String> items;
5861

5962
private MlFilter(String id, String description, SortedSet<String> items) {
60-
this.id = Objects.requireNonNull(id, ID.getPreferredName() + " must not be null");
63+
this.id = Objects.requireNonNull(id);
6164
this.description = description;
6265
this.items = Objects.requireNonNull(items, ITEMS.getPreferredName() + " must not be null");
6366
}
@@ -178,6 +181,10 @@ public Builder setItems(String... items) {
178181
}
179182

180183
public MlFilter build() {
184+
ExceptionsHelper.requireNonNull(id, MlFilter.ID.getPreferredName());
185+
if (!MlStrings.isValidId(id)) {
186+
throw ExceptionsHelper.badRequestException(Messages.getMessage(Messages.INVALID_ID, ID.getPreferredName(), id));
187+
}
181188
return new MlFilter(id, description, items);
182189
}
183190
}

x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/action/PutFilterActionRequestTests.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
public class PutFilterActionRequestTests extends AbstractStreamableXContentTestCase<Request> {
1414

15-
private final String filterId = randomAlphaOfLengthBetween(1, 20);
15+
private final String filterId = MlFilterTests.randomValidFilterId();
1616

1717
@Override
1818
protected Request createTestInstance() {

x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/job/config/MlFilterTests.java

+17-4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
*/
66
package org.elasticsearch.xpack.core.ml.job.config;
77

8+
import com.carrotsearch.randomizedtesting.generators.CodepointSetGenerator;
9+
import org.elasticsearch.ElasticsearchStatusException;
810
import org.elasticsearch.common.io.stream.Writeable.Reader;
911
import org.elasticsearch.common.xcontent.XContentParser;
1012
import org.elasticsearch.common.xcontent.json.JsonXContent;
@@ -17,6 +19,7 @@
1719
import static org.hamcrest.Matchers.contains;
1820
import static org.hamcrest.Matchers.containsString;
1921
import static org.hamcrest.Matchers.equalTo;
22+
import static org.hamcrest.Matchers.startsWith;
2023

2124
public class MlFilterTests extends AbstractSerializingTestCase<MlFilter> {
2225

@@ -30,7 +33,12 @@ protected MlFilter createTestInstance() {
3033
}
3134

3235
public static MlFilter createRandom() {
33-
return createRandom(randomAlphaOfLengthBetween(1, 20));
36+
return createRandom(randomValidFilterId());
37+
}
38+
39+
public static String randomValidFilterId() {
40+
CodepointSetGenerator generator = new CodepointSetGenerator("abcdefghijklmnopqrstuvwxyz".toCharArray());
41+
return generator.ofCodePointsLength(random(), 10, 10);
3442
}
3543

3644
public static MlFilter createRandom(String filterId) {
@@ -58,13 +66,13 @@ protected MlFilter doParseInstance(XContentParser parser) {
5866
}
5967

6068
public void testNullId() {
61-
NullPointerException ex = expectThrows(NullPointerException.class, () -> MlFilter.builder(null).build());
62-
assertEquals(MlFilter.ID.getPreferredName() + " must not be null", ex.getMessage());
69+
Exception ex = expectThrows(IllegalArgumentException.class, () -> MlFilter.builder(null).build());
70+
assertEquals("[filter_id] must not be null.", ex.getMessage());
6371
}
6472

6573
public void testNullItems() {
6674
NullPointerException ex = expectThrows(NullPointerException.class,
67-
() -> MlFilter.builder(randomAlphaOfLength(20)).setItems((SortedSet<String>) null).build());
75+
() -> MlFilter.builder(randomValidFilterId()).setItems((SortedSet<String>) null).build());
6876
assertEquals(MlFilter.ITEMS.getPreferredName() + " must not be null", ex.getMessage());
6977
}
7078

@@ -89,6 +97,11 @@ public void testLenientParser() throws IOException {
8997
}
9098
}
9199

100+
public void testInvalidId() {
101+
ElasticsearchStatusException e = expectThrows(ElasticsearchStatusException.class, () -> MlFilter.builder("Invalid id").build());
102+
assertThat(e.getMessage(), startsWith("Invalid filter_id; 'Invalid id' can contain lowercase"));
103+
}
104+
92105
public void testItemsAreSorted() {
93106
MlFilter filter = MlFilter.builder("foo").setItems("c", "b", "a").build();
94107
assertThat(filter.getItems(), contains("a", "b", "c"));

x-pack/plugin/src/test/resources/rest-api-spec/test/ml/filter_crud.yml

+12
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,18 @@ setup:
109109
filter_id: "filter-foo"
110110
from: 0
111111
size: 1
112+
113+
---
114+
"Test create filter given invalid filter_id":
115+
- do:
116+
catch: bad_request
117+
xpack.ml.put_filter:
118+
filter_id: Invalid
119+
body: >
120+
{
121+
"description": "this id is invalid due to an upper case character"
122+
}
123+
112124
---
113125
"Test create filter api":
114126
- do:

x-pack/qa/smoke-test-ml-with-security/build.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ integTestRunner {
3939
'ml/delete_model_snapshot/Test delete snapshot missing job_id',
4040
'ml/delete_model_snapshot/Test delete with in-use model',
4141
'ml/filter_crud/Test create filter api with mismatching body ID',
42+
'ml/filter_crud/Test create filter given invalid filter_id',
4243
'ml/filter_crud/Test get filter API with bad ID',
4344
'ml/filter_crud/Test invalid param combinations',
4445
'ml/filter_crud/Test non-existing filter',

0 commit comments

Comments
 (0)