From e2ec8f622264df567dbf1a4b36356ee8cac90e1d Mon Sep 17 00:00:00 2001 From: Mashhur Date: Wed, 24 Sep 2025 10:44:00 -0700 Subject: [PATCH 1/3] Validate Logstash pipeline ID when creating. --- .../test/rest/LogstashSystemIndexIT.java | 53 +++++++++++++++++++ .../logstash/rest/RestPutPipelineAction.java | 27 ++++++++++ 2 files changed, 80 insertions(+) diff --git a/x-pack/plugin/logstash/src/javaRestTest/java/org/elasticsearch/xpack/test/rest/LogstashSystemIndexIT.java b/x-pack/plugin/logstash/src/javaRestTest/java/org/elasticsearch/xpack/test/rest/LogstashSystemIndexIT.java index 1ef4cbc3f2820..6e9ae486df298 100644 --- a/x-pack/plugin/logstash/src/javaRestTest/java/org/elasticsearch/xpack/test/rest/LogstashSystemIndexIT.java +++ b/x-pack/plugin/logstash/src/javaRestTest/java/org/elasticsearch/xpack/test/rest/LogstashSystemIndexIT.java @@ -150,6 +150,59 @@ public void testMultiplePipelines() throws IOException { assertThat(listResponseMap.size(), is(ids.size())); } + public void testValidPipelineIds() throws IOException { + final String pipelineJson = getPipelineJson(); + final List validIds = List.of( + "main", + "_internal", + "my_pipeline", + "my-pipeline", + "pipeline123", + "A1", + "_pipeline_1", + "MyPipeline-123", + "main_pipeline_v2" + ); + + for (String id : validIds) { + createPipeline(id, pipelineJson); + } + + refreshAllIndices(); + + // fetch all pipeline IDs + Request listAll = new Request("GET", "/_logstash/pipeline"); + Response listAllResponse = client().performRequest(listAll); + assertThat(listAllResponse.getStatusLine().getStatusCode(), is(200)); + Map listResponseMap = XContentHelper.convertToMap( + XContentType.JSON.xContent(), + EntityUtils.toString(listAllResponse.getEntity()), + false + ); + for (String id : validIds) { + assertTrue(listResponseMap.containsKey(id)); + } + assertThat(listResponseMap.size(), is(validIds.size())); + } + + public void testInvalidPipelineIds() throws IOException { + final String pipelineJson = getPipelineJson(); + final List invalidPipelineIds = List.of("123pipeline", "-pipeline", "*-pipeline"); + + for (String id : invalidPipelineIds) { + Request putRequest = new Request("PUT", "/_logstash/pipeline/" + id); + putRequest.setJsonEntity(pipelineJson); + + ResponseException exception = expectThrows(ResponseException.class, () -> client().performRequest(putRequest)); + + Response response = exception.getResponse(); + assertThat(response.getStatusLine().getStatusCode(), is(400)); + + String responseBody = EntityUtils.toString(response.getEntity()); + assertThat(responseBody, containsString("Invalid pipeline [" + id + "] ID received")); + } + } + private void createPipeline(String id, String json) throws IOException { Request putRequest = new Request("PUT", "/_logstash/pipeline/" + id); putRequest.setJsonEntity(json); diff --git a/x-pack/plugin/logstash/src/main/java/org/elasticsearch/xpack/logstash/rest/RestPutPipelineAction.java b/x-pack/plugin/logstash/src/main/java/org/elasticsearch/xpack/logstash/rest/RestPutPipelineAction.java index 5255ec97a7ccf..0bdae6eada0ae 100644 --- a/x-pack/plugin/logstash/src/main/java/org/elasticsearch/xpack/logstash/rest/RestPutPipelineAction.java +++ b/x-pack/plugin/logstash/src/main/java/org/elasticsearch/xpack/logstash/rest/RestPutPipelineAction.java @@ -8,6 +8,7 @@ package org.elasticsearch.xpack.logstash.rest; import org.elasticsearch.client.internal.node.NodeClient; +import org.elasticsearch.common.Strings; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.rest.BaseRestHandler; import org.elasticsearch.rest.RestRequest; @@ -23,12 +24,17 @@ import java.io.IOException; import java.util.List; +import java.util.regex.Pattern; import static org.elasticsearch.rest.RestRequest.Method.PUT; @ServerlessScope(Scope.PUBLIC) public class RestPutPipelineAction extends BaseRestHandler { + // A pipeline ID pattern to validate. + // Reference: https://www.elastic.co/docs/reference/logstash/configuring-centralized-pipelines#wildcard-in-pipeline-id + private static final Pattern PIPELINE_ID_PATTERN = Pattern.compile("[a-zA-Z_][a-zA-Z0-9_-]*"); + @Override public String getName() { return "logstash_put_pipeline"; @@ -39,9 +45,30 @@ public List routes() { return List.of(new Route(PUT, "/_logstash/pipeline/{id}")); } + /** + * Validates pipeline ID for: + * - must begin with a letter or underscore + * - can contain only letters, underscores, dashes, and numbers + */ + private static void validatePipelineId(String id) { + if (Strings.isEmpty(id)) { + throw new IllegalArgumentException("Pipeline ID cannot be null or empty"); + } + + if (PIPELINE_ID_PATTERN.matcher(id).matches() == false) { + throw new IllegalArgumentException( + "Invalid pipeline [" + + id + + "] ID received. Pipeline ID must begin with a letter or underscore and can contain only letters, underscores, dashes, hyphens and numbers" + ); + } + } + @Override protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException { final String id = request.param("id"); + validatePipelineId(id); + try (XContentParser parser = request.contentParser()) { // parse pipeline for validation Pipeline.PARSER.apply(parser, id); From 96bb4380b99cabf0f06554a0a664a0f02cdf2854 Mon Sep 17 00:00:00 2001 From: Mashhur Date: Wed, 24 Sep 2025 11:43:08 -0700 Subject: [PATCH 2/3] Checkstyle issue fix. --- .../xpack/logstash/rest/RestPutPipelineAction.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugin/logstash/src/main/java/org/elasticsearch/xpack/logstash/rest/RestPutPipelineAction.java b/x-pack/plugin/logstash/src/main/java/org/elasticsearch/xpack/logstash/rest/RestPutPipelineAction.java index 0bdae6eada0ae..4506e70a6877e 100644 --- a/x-pack/plugin/logstash/src/main/java/org/elasticsearch/xpack/logstash/rest/RestPutPipelineAction.java +++ b/x-pack/plugin/logstash/src/main/java/org/elasticsearch/xpack/logstash/rest/RestPutPipelineAction.java @@ -59,7 +59,8 @@ private static void validatePipelineId(String id) { throw new IllegalArgumentException( "Invalid pipeline [" + id - + "] ID received. Pipeline ID must begin with a letter or underscore and can contain only letters, underscores, dashes, hyphens and numbers" + + "] ID received. Pipeline ID must begin with a letter or underscore and can contain only letters, " + + "underscores, dashes, hyphens and numbers" ); } } From d71b9e23964eecd6acb2b5d9a2c5d605af31d59f Mon Sep 17 00:00:00 2001 From: Mashhur Date: Thu, 25 Sep 2025 14:06:59 -0700 Subject: [PATCH 3/3] Apply Exford comma. --- .../xpack/logstash/rest/RestPutPipelineAction.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugin/logstash/src/main/java/org/elasticsearch/xpack/logstash/rest/RestPutPipelineAction.java b/x-pack/plugin/logstash/src/main/java/org/elasticsearch/xpack/logstash/rest/RestPutPipelineAction.java index 4506e70a6877e..3bbd3d7717799 100644 --- a/x-pack/plugin/logstash/src/main/java/org/elasticsearch/xpack/logstash/rest/RestPutPipelineAction.java +++ b/x-pack/plugin/logstash/src/main/java/org/elasticsearch/xpack/logstash/rest/RestPutPipelineAction.java @@ -60,7 +60,7 @@ private static void validatePipelineId(String id) { "Invalid pipeline [" + id + "] ID received. Pipeline ID must begin with a letter or underscore and can contain only letters, " - + "underscores, dashes, hyphens and numbers" + + "underscores, dashes, hyphens, and numbers" ); } }