From e8e8e467376a58107106cb1cbe273d9e5eab6769 Mon Sep 17 00:00:00 2001 From: Zack King <91903901+kingzacko1@users.noreply.github.com> Date: Tue, 10 Sep 2024 08:43:09 -0500 Subject: [PATCH] Fix unescaped double quotes in nested event fields for TeamsNotificationV2 (#20389) * Fix unescaped double quotes in nested event fields for TeamsNotificationV2 * Changelog --- changelog/unreleased/pr-20389.toml | 4 + .../TeamsEventNotificationV2.java | 23 +--- .../TeamsEventNotificationV2Test.java | 128 +++++++++++++++++- 3 files changed, 133 insertions(+), 22 deletions(-) create mode 100644 changelog/unreleased/pr-20389.toml diff --git a/changelog/unreleased/pr-20389.toml b/changelog/unreleased/pr-20389.toml new file mode 100644 index 000000000000..5402d5f8e3e9 --- /dev/null +++ b/changelog/unreleased/pr-20389.toml @@ -0,0 +1,4 @@ +type = "f" +message = "Fixed issue where invalid JSON characters being unescaped broke TeamsNotificationV2 notifications." + +pulls = ["20389"] diff --git a/graylog2-server/src/main/java/org/graylog/integrations/notifications/types/microsoftteams/TeamsEventNotificationV2.java b/graylog2-server/src/main/java/org/graylog/integrations/notifications/types/microsoftteams/TeamsEventNotificationV2.java index e9cb93945a56..5ca11b40c5df 100644 --- a/graylog2-server/src/main/java/org/graylog/integrations/notifications/types/microsoftteams/TeamsEventNotificationV2.java +++ b/graylog2-server/src/main/java/org/graylog/integrations/notifications/types/microsoftteams/TeamsEventNotificationV2.java @@ -19,6 +19,7 @@ import com.floreysoft.jmte.Engine; import com.google.common.annotations.VisibleForTesting; import jakarta.inject.Inject; +import jakarta.inject.Named; import org.graylog.events.notifications.EventNotification; import org.graylog.events.notifications.EventNotificationContext; import org.graylog.events.notifications.EventNotificationException; @@ -40,7 +41,6 @@ import org.slf4j.LoggerFactory; import java.net.URI; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -51,7 +51,7 @@ public class TeamsEventNotificationV2 implements EventNotification { private static final Logger LOG = LoggerFactory.getLogger(TeamsEventNotificationV2.class); private final EventNotificationService notificationCallbackService; - private final Engine templateEngine; + private final Engine jsonTemplateEngine; private final NotificationService notificationService; private final ObjectMapperProvider objectMapperProvider; private final NodeId nodeId; @@ -61,13 +61,13 @@ public class TeamsEventNotificationV2 implements EventNotification { @Inject public TeamsEventNotificationV2(EventNotificationService notificationCallbackService, ObjectMapperProvider objectMapperProvider, - Engine templateEngine, + @Named("JsonSafe") Engine jsonTemplateEngine, NotificationService notificationService, NodeId nodeId, RequestClient requestClient, HttpConfiguration httpConfiguration) { this.notificationCallbackService = notificationCallbackService; this.objectMapperProvider = requireNonNull(objectMapperProvider); - this.templateEngine = requireNonNull(templateEngine); + this.jsonTemplateEngine = requireNonNull(jsonTemplateEngine); this.notificationService = requireNonNull(notificationService); this.nodeId = requireNonNull(nodeId); this.requestClient = requireNonNull(requestClient); @@ -121,7 +121,7 @@ String generateBody(EventNotificationContext ctx, TeamsEventNotificationConfigV2 final List backlog = getMessageBacklog(ctx, config); Map model = getCustomMessageModel(ctx, config.type(), backlog, config.timeZone()); try { - return templateEngine.transform(config.adaptiveCard(), model); + return jsonTemplateEngine.transform(config.adaptiveCard(), model); } catch (Exception e) { String error = "Invalid Custom Message template."; LOG.error("{} [{}]", error, e.toString()); @@ -146,17 +146,8 @@ Map getCustomMessageModel(EventNotificationContext ctx, String t final Map objectMap = objectMapperProvider.getForTimeZone(timeZone).convertValue(modelData, TypeReferences.MAP_STRING_OBJECT); objectMap.put("type", type); objectMap.put("http_external_uri", this.httpExternalUri); - final Map escapedModelMap = new HashMap<>(); - objectMap.forEach((k, v) -> { - if (v instanceof String str) { - escapedModelMap.put(k, str.replace("\"", "\\\"")); - } else { - escapedModelMap.put(k, v); - } - }); - LOG.debug("Finalized model map: {}", escapedModelMap); - - return escapedModelMap; + + return objectMap; } public interface Factory extends EventNotification.Factory { diff --git a/graylog2-server/src/test/java/org/graylog/integrations/notifications/types/microsoftteams/TeamsEventNotificationV2Test.java b/graylog2-server/src/test/java/org/graylog/integrations/notifications/types/microsoftteams/TeamsEventNotificationV2Test.java index 2a5fd7405766..3a73d15b37c7 100644 --- a/graylog2-server/src/test/java/org/graylog/integrations/notifications/types/microsoftteams/TeamsEventNotificationV2Test.java +++ b/graylog2-server/src/test/java/org/graylog/integrations/notifications/types/microsoftteams/TeamsEventNotificationV2Test.java @@ -16,7 +16,6 @@ */ package org.graylog.integrations.notifications.types.microsoftteams; -import com.floreysoft.jmte.Engine; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @@ -30,6 +29,7 @@ import org.graylog.events.notifications.TemporaryEventNotificationException; import org.graylog.events.processor.EventDefinitionDto; import org.graylog.integrations.notifications.types.util.RequestClient; +import org.graylog2.bindings.providers.JsonSafeEngineProvider; import org.graylog2.configuration.HttpConfiguration; import org.graylog2.notifications.NotificationImpl; import org.graylog2.notifications.NotificationService; @@ -67,6 +67,106 @@ public class TeamsEventNotificationV2Test { private final NodeId nodeId = new SimpleNodeId("12345"); private final MessageFactory messageFactory = new TestMessageFactory(); + private final String defaultTemplate = """ + { "type": "message", + "attachments": [ + { + "contentType": "application/vnd.microsoft.card.adaptive", + "content": { + "type": "AdaptiveCard", + "version": "1.6", + "msTeams": { "width": "full" }, + "body": [ + { + "type": "TextBlock", + "size": "Large", + "weight": "Bolder", + "text": "${event_definition_title} triggered", + "style": "heading", + "fontType": "Default" + }, + { + "type": "TextBlock", + "text": "${event_definition_description}", + "wrap": true + }, + { + "type": "TextBlock", + "text": "Event Details", + "wrap": true + }, + { + "type": "FactSet", + "facts": [ + { + "title": "Type", + "value": "${event_definition_type}" + }, + { + "title": "Timestamp", + "value": "${event.timestamp_processing}" + }, + { + "title": "Message", + "value": "${event.message}" + }, + { + "title": "Source", + "value": "${event.source}" + }, + { + "title": "Key", + "value": "${event.key}" + }, + { + "title": "Priority", + "value": "${event.priority}" + }, + { + "title": "Alert", + "value": "${event.alert}" + }, + { + "title": "Timerange Start", + "value": "${event.timerange_start}" + }, + { + "title": "Timerange End", + "value": "${event.timerange_end}" + } + ] + }${if event.fields}, + { + "type": "TextBlock", + "text": "Event Fields", + "weight": "bolder", + "size": "medium" + }, + { + "type": "FactSet", + "facts": [${foreach event.fields field} + { "title": "${field.key}", "value": "${field.value}" }${if last_field}${else},${end}${end} + ] + }${end}${if backlog}, + { + "type": "TextBlock", + "text": "Backlog", + "weight": "bolder", + "size": "medium" + }, + { + "type": "FactSet", + "facts": [${foreach backlog message} + { "title": "Message", "value": "${message.message}" }${if last_message}${else},${end}${end} + ] + }${end} + ], + "$schema": "[http://adaptivecards.io/schemas/adaptive-card.json](https://link.edgepilot.com/s/8e5962e4/2Jj9cedkLka5KIsBRuMOIg?u=http://adaptivecards.io/schemas/adaptive-card.json)", + "rtl": false + } + } + ] + }"""; @Mock NotificationService mockNotificationService; @@ -89,7 +189,7 @@ public void setUp() { teamsEventNotification = new TeamsEventNotificationV2(notificationCallbackService, new ObjectMapperProvider(), - Engine.createEngine(), + new JsonSafeEngineProvider().get(), mockNotificationService, nodeId, mockrequestClient, @@ -97,15 +197,22 @@ public void setUp() { } @Test - public void testEscapedQuotes() { + public void testEscapedQuotes() throws PermanentEventNotificationException { if (eventNotificationContext.eventDefinition().isPresent()) { EventDefinitionDto definition = eventNotificationContext.eventDefinition().get(); definition = definition.toBuilder().description("A Description with \"Double Quotes\"").build(); eventNotificationContext = eventNotificationContext.toBuilder().eventDefinition(definition).build(); } - List messageSummaries = generateMessageSummaries(50); - Map customMessageModel = teamsEventNotification.getCustomMessageModel(eventNotificationContext, notificationConfig.type(), messageSummaries, DateTimeZone.UTC); - assertThat(customMessageModel.get("event_definition_description")).isEqualTo("A Description with \\\"Double Quotes\\\""); + when(notificationCallbackService.getBacklogForEvent(any())).thenReturn(generateMessageSummariesWithDoubleQuotes(5)); + TeamsEventNotificationConfigV2 config = TeamsEventNotificationConfigV2.builder() + .adaptiveCard(defaultTemplate) + .backlogSize(5) + .timeZone(DateTimeZone.UTC) + .webhookUrl("http://localhost:12345") + .build(); + String body = teamsEventNotification.generateBody(eventNotificationContext, config); + assertThat(body).contains("A Description with \\\"Double Quotes\\\""); + assertThat(body).contains("Test message1 with \\\"Double Quotes\\\""); } @Test @@ -246,6 +353,15 @@ private ImmutableList generateMessageSummaries(int size) { return ImmutableList.copyOf(messageSummaries); } + private ImmutableList generateMessageSummariesWithDoubleQuotes(int size) { + List messageSummaries = new ArrayList<>(); + for (int i = 0; i < size; i++) { + MessageSummary summary = new MessageSummary("graylog_" + i, messageFactory.createMessage("Test message" + i + " with \"Double Quotes\"", "source" + i, new DateTime(2020, 9, 6, 17, 0, DateTimeZone.UTC))); + messageSummaries.add(summary); + } + return ImmutableList.copyOf(messageSummaries); + } + private TeamsEventNotificationConfigV2 buildInvalidTemplate() { TeamsEventNotificationConfigV2.Builder builder = TeamsEventNotificationConfigV2.builder(); builder.adaptiveCard("{${if backlog}\"invalid_json\": true }");