Repository: kestra-io/kestra
Issue: Triple dash in attribute value can lead to incorrect validation
Labels: bug

### Expected Behavior

Wrong regex leading to parsing --- everywhere instead of on a new line only

### Actual Behaviour

_No response_

### Steps To Reproduce

_No response_

### Environment Information

- Kestra Version:
- Operating System (OS / Docker / Kubernetes):
- Java Version (If not docker):


### Example flow

_No response_

Issue comments:
See this https://kestra-io.slack.com/archives/C03FQKXRK3K/p1687858331341749

Pull request review comments:
Directly returns the list

however, lgtm
Ups, was for testing forgot it :+1: 

Patch that resolved the issue:
diff --git a/core/src/main/java/io/kestra/core/models/validations/ValidateConstraintViolation.java b/core/src/main/java/io/kestra/core/models/validations/ValidateConstraintViolation.java
index 175d901471..891981bd5c 100644
--- a/core/src/main/java/io/kestra/core/models/validations/ValidateConstraintViolation.java
+++ b/core/src/main/java/io/kestra/core/models/validations/ValidateConstraintViolation.java
@@ -1,5 +1,6 @@
 package io.kestra.core.models.validations;
 
+import com.fasterxml.jackson.annotation.JsonIgnore;
 import io.micronaut.core.annotation.Introspected;
 import lombok.AllArgsConstructor;
 import lombok.Getter;
@@ -31,14 +32,17 @@ public class ValidateConstraintViolation {
 
     private String constraints;
 
+    @JsonIgnore
     public String getIdentity(){
         return flow != null & namespace != null ? getFlowId() : flow != null ? flow : String.valueOf(index);
     }
 
+    @JsonIgnore
     public String getIdentity(Path directory) throws IOException {
         return flow != null & namespace != null ? getFlowId() : flow != null ? flow : String.valueOf(Files.walk(directory).collect(Collectors.toList()).get(index));
     }
 
+    @JsonIgnore
     public String getFlowId(){
         return namespace+"."+flow;
     }
diff --git a/webserver/src/main/java/io/kestra/webserver/controllers/FlowController.java b/webserver/src/main/java/io/kestra/webserver/controllers/FlowController.java
index c9221ed3d7..4fc46e515e 100644
--- a/webserver/src/main/java/io/kestra/webserver/controllers/FlowController.java
+++ b/webserver/src/main/java/io/kestra/webserver/controllers/FlowController.java
@@ -446,7 +446,7 @@ public List<ValidateConstraintViolation> validateFlows(
     ) {
         AtomicInteger index = new AtomicInteger(0);
         return Stream
-            .of(flows.split("---"))
+            .of(flows.split("\\n+---\\n*?"))
             .map(flow -> {
                 ValidateConstraintViolation.ValidateConstraintViolationBuilder<?, ?> validateConstraintViolationBuilder = ValidateConstraintViolation.builder();
                 validateConstraintViolationBuilder.index(index.getAndIncrement());


Test patch:
diff --git a/webserver/src/test/java/io/kestra/webserver/controllers/FlowControllerTest.java b/webserver/src/test/java/io/kestra/webserver/controllers/FlowControllerTest.java
index cf7246c3bb..596630f4b4 100644
--- a/webserver/src/test/java/io/kestra/webserver/controllers/FlowControllerTest.java
+++ b/webserver/src/test/java/io/kestra/webserver/controllers/FlowControllerTest.java
@@ -9,6 +9,7 @@
 import io.kestra.core.models.flows.input.StringInput;
 import io.kestra.core.models.hierarchies.FlowGraph;
 import io.kestra.core.models.tasks.Task;
+import io.kestra.core.models.validations.ValidateConstraintViolation;
 import io.kestra.core.runners.AbstractMemoryRunnerTest;
 import io.kestra.core.serializers.YamlFlowParser;
 import io.kestra.core.tasks.debugs.Return;
@@ -30,6 +31,7 @@
 import io.micronaut.http.hateoas.JsonError;
 import io.micronaut.rxjava2.http.client.RxHttpClient;
 import jakarta.inject.Inject;
+import org.hamcrest.Matchers;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
@@ -40,10 +42,7 @@
 import java.nio.charset.Charset;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.zip.ZipFile;
 
 import static io.micronaut.http.HttpRequest.*;
@@ -664,6 +663,30 @@ void deleteFlowsByIds(){
         assertThat(e.getStatus(), is(HttpStatus.NOT_FOUND));
     }
 
+    @Test
+    void validateFlows() throws IOException {
+        URL resource = TestsUtils.class.getClassLoader().getResource("flows/validateMultipleValidFlows.yaml");
+        String flow = Files.readString(Path.of(Objects.requireNonNull(resource).getPath()), Charset.defaultCharset());
+
+        HttpResponse<List<ValidateConstraintViolation>> response = client.toBlocking().exchange(POST("/api/v1/flows/validate", flow).contentType(MediaType.APPLICATION_YAML), Argument.listOf(ValidateConstraintViolation.class));
+
+        List<ValidateConstraintViolation> body = response.body();
+        assertThat(body.size(), is(2));
+        assertThat(body, everyItem(
+            Matchers.hasProperty("constraints", is(nullValue()))
+        ));
+
+        resource = TestsUtils.class.getClassLoader().getResource("flows/validateMultipleInvalidFlows.yaml");
+        flow = Files.readString(Path.of(Objects.requireNonNull(resource).getPath()), Charset.defaultCharset());
+
+        response = client.toBlocking().exchange(POST("/api/v1/flows/validate", flow).contentType(MediaType.APPLICATION_YAML), Argument.listOf(ValidateConstraintViolation.class));
+
+        body = response.body();
+        assertThat(body.size(), is(2));
+        assertThat(body.get(0).getConstraints(), containsString("Unrecognized field \"unknownProp\""));
+        assertThat(body.get(1).getConstraints(), containsString("Invalid type: io.kestra.core.tasks.debugs.UnknownTask"));
+    }
+
     private Flow generateFlow(String namespace, String inputName) {
         return generateFlow(IdUtils.create(), namespace, inputName);
     }
diff --git a/webserver/src/test/resources/flows/validateMultipleInvalidFlows.yaml b/webserver/src/test/resources/flows/validateMultipleInvalidFlows.yaml
new file mode 100644
index 0000000000..7a5c502450
--- /dev/null
+++ b/webserver/src/test/resources/flows/validateMultipleInvalidFlows.yaml
@@ -0,0 +1,19 @@
+id: "first_flow"
+namespace: "validation"
+tasks:
+  - id: task_one
+    type: io.kestra.core.tasks.scripts.Bash
+    commands:
+      - 'echo strange---string'
+    unknownProp: unknownValue
+
+---
+
+id: "second_flow"
+namespace: "validation"
+tasks:
+  - id: task-two
+    type: io.kestra.core.tasks.debugs.UnknownTask
+  - id: task-three
+    type: io.kestra.core.tasks.debugs.Return
+    format: strangestring---
\ No newline at end of file
diff --git a/webserver/src/test/resources/flows/validateMultipleValidFlows.yaml b/webserver/src/test/resources/flows/validateMultipleValidFlows.yaml
new file mode 100644
index 0000000000..55513373cc
--- /dev/null
+++ b/webserver/src/test/resources/flows/validateMultipleValidFlows.yaml
@@ -0,0 +1,19 @@
+id: "first_flow"
+namespace: "validation"
+tasks:
+  - id: task_one
+    type: io.kestra.core.tasks.scripts.Bash
+    commands:
+      - 'echo strange---string'
+
+---
+
+id: "second_flow"
+namespace: "validation"
+tasks:
+  - id: task-two
+    type: io.kestra.core.tasks.debugs.Return
+    format: strangestring---
+  - id: task-three
+    type: io.kestra.core.tasks.debugs.Return
+    format: ---strangestring
\ No newline at end of file