Repository: kestra-io/kestra
Issue: Dynamically generated webhook key doesn't work as expected
Labels: bug

In the last 0.11.0 release, we added the feature to fetch the webhook key from secrets. However, it didn't work for me. See in the video that when using the webhook key from the Secret, no Execution was triggered. After hardcoding the key, the event from EventBridge got passed to the webhook without any issues:

https://share.descript.com/view/kytVX5bnfp5 

### Environment Information

- Kestra Version: 0.12.0


Issue comments:
This is an issue due to the secret function that adds a new line at the end of the rendered value.

Pull request review comments:

Patch that resolved the issue:
diff --git a/build.gradle b/build.gradle
index 8754b1b083..663578a51a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -187,6 +187,7 @@ subprojects {
         systemProperty 'user.country', 'US'
 
         environment 'SECRET_MY_SECRET', "{\"secretKey\":\"secretValue\"}".bytes.encodeBase64().toString()
+        environment 'SECRET_WEBHOOK_KEY', "secretKey".bytes.encodeBase64().toString()
         environment 'SECRET_NON_B64_SECRET', "some secret value"
     }
 
diff --git a/webserver/src/main/java/io/kestra/webserver/controllers/ExecutionController.java b/webserver/src/main/java/io/kestra/webserver/controllers/ExecutionController.java
index 2a91c58c42..4031870cdf 100644
--- a/webserver/src/main/java/io/kestra/webserver/controllers/ExecutionController.java
+++ b/webserver/src/main/java/io/kestra/webserver/controllers/ExecutionController.java
@@ -391,7 +391,7 @@ private Execution webhook(
             .filter(w -> {
                 RunContext runContext = runContextFactory.of(flow, w);
                 try {
-                    String webhookKey = runContext.render(w.getKey());
+                    String webhookKey = runContext.render(w.getKey()).trim();
                     return webhookKey.equals(key);
                 } catch (IllegalVariableEvaluationException e) {
                     // be conservative, don't crash but filter the webhook


Test patch:
diff --git a/core/src/test/resources/flows/valids/webhook-secret-key.yaml b/core/src/test/resources/flows/valids/webhook-secret-key.yaml
new file mode 100644
index 0000000000..30329b26ba
--- /dev/null
+++ b/core/src/test/resources/flows/valids/webhook-secret-key.yaml
@@ -0,0 +1,13 @@
+id: webhook-secret-key
+namespace: io.kestra.tests
+
+tasks:
+  - id: out
+    type: io.kestra.core.tasks.debugs.Return
+    format: "{{trigger | json }}"
+
+
+triggers:
+  - id: webhook
+    type: io.kestra.core.models.triggers.types.Webhook
+    key: "{{ secret('WEBHOOK_KEY') }}"
diff --git a/webserver/src/test/java/io/kestra/webserver/controllers/ExecutionControllerTest.java b/webserver/src/test/java/io/kestra/webserver/controllers/ExecutionControllerTest.java
index 531f4b8d2a..62b185354d 100644
--- a/webserver/src/test/java/io/kestra/webserver/controllers/ExecutionControllerTest.java
+++ b/webserver/src/test/java/io/kestra/webserver/controllers/ExecutionControllerTest.java
@@ -33,6 +33,7 @@
 import jakarta.inject.Inject;
 import jakarta.inject.Named;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
 
 import java.io.File;
 import java.time.Duration;
@@ -526,6 +527,21 @@ void webhookDynamicKey() {
         assertThat(execution.getId(), notNullValue());
     }
 
+    @Test
+    @EnabledIfEnvironmentVariable(named = "SECRET_WEBHOOK_KEY", matches = ".*")
+    void webhookDynamicKeyFromASecret() {
+        Execution execution = client.toBlocking().retrieve(
+            HttpRequest
+                .GET(
+                    "/api/v1/executions/webhook/" + TESTS_FLOW_NS + "/webhook-secret-key/secretKey"
+                ),
+            Execution.class
+        );
+
+        assertThat(execution, notNullValue());
+        assertThat(execution.getId(), notNullValue());
+    }
+
     @Test
     void resumePaused() throws TimeoutException, InterruptedException {
         // Run execution until it is paused