From 9121ec1cc86f68aa9f40210ba6879bdf1d0d1575 Mon Sep 17 00:00:00 2001 From: TwiN Date: Thu, 20 Oct 2022 15:48:51 -0400 Subject: [PATCH] fix(alerting): Resolve Matrix issue with bad payload when condition has `"` in it --- alerting/provider/matrix/matrix.go | 38 ++++++++++++++----------- alerting/provider/matrix/matrix_test.go | 10 +++---- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/alerting/provider/matrix/matrix.go b/alerting/provider/matrix/matrix.go index ef2746eb..7fcc59c5 100644 --- a/alerting/provider/matrix/matrix.go +++ b/alerting/provider/matrix/matrix.go @@ -2,6 +2,7 @@ package matrix import ( "bytes" + "encoding/json" "fmt" "io" "math/rand" @@ -61,7 +62,7 @@ func (provider *AlertProvider) IsValid() bool { // Send an alert using the provider func (provider *AlertProvider) Send(endpoint *core.Endpoint, alert *alert.Alert, result *core.Result, resolved bool) error { - buffer := bytes.NewBuffer([]byte(provider.buildRequestBody(endpoint, alert, result, resolved))) + buffer := bytes.NewBuffer(provider.buildRequestBody(endpoint, alert, result, resolved)) config := provider.getConfigForGroup(endpoint.Group) if config.ServerURL == "" { config.ServerURL = defaultHomeserverURL @@ -94,17 +95,22 @@ func (provider *AlertProvider) Send(endpoint *core.Endpoint, alert *alert.Alert, return err } +type Body struct { + MsgType string `json:"msgtype"` + Format string `json:"format"` + Body string `json:"body"` + FormattedBody string `json:"formatted_body"` +} + // buildRequestBody builds the request body for the provider -func (provider *AlertProvider) buildRequestBody(endpoint *core.Endpoint, alert *alert.Alert, result *core.Result, resolved bool) string { - return fmt.Sprintf(`{ - "msgtype": "m.text", - "format": "org.matrix.custom.html", - "body": "%s", - "formatted_body": "%s" -}`, - buildPlaintextMessageBody(endpoint, alert, result, resolved), - buildHTMLMessageBody(endpoint, alert, result, resolved), - ) +func (provider *AlertProvider) buildRequestBody(endpoint *core.Endpoint, alert *alert.Alert, result *core.Result, resolved bool) []byte { + body, _ := json.Marshal(Body{ + MsgType: "m.text", + Format: "org.matrix.custom.html", + Body: buildPlaintextMessageBody(endpoint, alert, result, resolved), + FormattedBody: buildHTMLMessageBody(endpoint, alert, result, resolved), + }) + return body } // buildPlaintextMessageBody builds the message body in plaintext to include in request @@ -122,13 +128,13 @@ func buildPlaintextMessageBody(endpoint *core.Endpoint, alert *alert.Alert, resu } else { prefix = "✕" } - results += fmt.Sprintf("\\n%s - %s", prefix, conditionResult.Condition) + results += fmt.Sprintf("\n%s - %s", prefix, conditionResult.Condition) } var description string if alertDescription := alert.GetDescription(); len(alertDescription) > 0 { - description = "\\n" + alertDescription + description = "\n" + alertDescription } - return fmt.Sprintf("%s%s\\n%s", message, description, results) + return fmt.Sprintf("%s%s\n%s", message, description, results) } // buildHTMLMessageBody builds the message body in HTML to include in request @@ -150,9 +156,9 @@ func buildHTMLMessageBody(endpoint *core.Endpoint, alert *alert.Alert, result *c } var description string if alertDescription := alert.GetDescription(); len(alertDescription) > 0 { - description = fmt.Sprintf("\\n
%s
", alertDescription) + description = fmt.Sprintf("\n
%s
", alertDescription) } - return fmt.Sprintf("

%s

%s\\n
Condition results
", message, description, results) + return fmt.Sprintf("

%s

%s\n
Condition results
", message, description, results) } // getConfigForGroup returns the appropriate configuration for a given group diff --git a/alerting/provider/matrix/matrix_test.go b/alerting/provider/matrix/matrix_test.go index 12ba6042..7241d989 100644 --- a/alerting/provider/matrix/matrix_test.go +++ b/alerting/provider/matrix/matrix_test.go @@ -184,14 +184,14 @@ func TestAlertProvider_buildRequestBody(t *testing.T) { Provider: AlertProvider{}, Alert: alert.Alert{Description: &firstDescription, SuccessThreshold: 5, FailureThreshold: 3}, Resolved: false, - ExpectedBody: "{\n\t\"msgtype\": \"m.text\",\n\t\"format\": \"org.matrix.custom.html\",\n\t\"body\": \"An alert for `endpoint-name` has been triggered due to having failed 3 time(s) in a row\\ndescription-1\\n\\n✕ - [CONNECTED] == true\\n✕ - [STATUS] == 200\",\n\t\"formatted_body\": \"

An alert for endpoint-name has been triggered due to having failed 3 time(s) in a row

\\n
description-1
\\n
Condition results
\"\n}", + ExpectedBody: "{\"msgtype\":\"m.text\",\"format\":\"org.matrix.custom.html\",\"body\":\"An alert for `endpoint-name` has been triggered due to having failed 3 time(s) in a row\\ndescription-1\\n\\n✕ - [CONNECTED] == true\\n✕ - [STATUS] == 200\",\"formatted_body\":\"\\u003ch3\\u003eAn alert for \\u003ccode\\u003eendpoint-name\\u003c/code\\u003e has been triggered due to having failed 3 time(s) in a row\\u003c/h3\\u003e\\n\\u003cblockquote\\u003edescription-1\\u003c/blockquote\\u003e\\n\\u003ch5\\u003eCondition results\\u003c/h5\\u003e\\u003cul\\u003e\\u003cli\\u003e❌ - \\u003ccode\\u003e[CONNECTED] == true\\u003c/code\\u003e\\u003c/li\\u003e\\u003cli\\u003e❌ - \\u003ccode\\u003e[STATUS] == 200\\u003c/code\\u003e\\u003c/li\\u003e\\u003c/ul\\u003e\"}", }, { Name: "resolved", Provider: AlertProvider{}, Alert: alert.Alert{Description: &secondDescription, SuccessThreshold: 5, FailureThreshold: 3}, Resolved: true, - ExpectedBody: "{\n\t\"msgtype\": \"m.text\",\n\t\"format\": \"org.matrix.custom.html\",\n\t\"body\": \"An alert for `endpoint-name` has been resolved after passing successfully 5 time(s) in a row\\ndescription-2\\n\\n✓ - [CONNECTED] == true\\n✓ - [STATUS] == 200\",\n\t\"formatted_body\": \"

An alert for endpoint-name has been resolved after passing successfully 5 time(s) in a row

\\n
description-2
\\n
Condition results
\"\n}", + ExpectedBody: "{\"msgtype\":\"m.text\",\"format\":\"org.matrix.custom.html\",\"body\":\"An alert for `endpoint-name` has been resolved after passing successfully 5 time(s) in a row\\ndescription-2\\n\\n✓ - [CONNECTED] == true\\n✓ - [STATUS] == 200\",\"formatted_body\":\"\\u003ch3\\u003eAn alert for \\u003ccode\\u003eendpoint-name\\u003c/code\\u003e has been resolved after passing successfully 5 time(s) in a row\\u003c/h3\\u003e\\n\\u003cblockquote\\u003edescription-2\\u003c/blockquote\\u003e\\n\\u003ch5\\u003eCondition results\\u003c/h5\\u003e\\u003cul\\u003e\\u003cli\\u003e✅ - \\u003ccode\\u003e[CONNECTED] == true\\u003c/code\\u003e\\u003c/li\\u003e\\u003cli\\u003e✅ - \\u003ccode\\u003e[STATUS] == 200\\u003c/code\\u003e\\u003c/li\\u003e\\u003c/ul\\u003e\"}", }, } for _, scenario := range scenarios { @@ -207,11 +207,11 @@ func TestAlertProvider_buildRequestBody(t *testing.T) { }, scenario.Resolved, ) - if body != scenario.ExpectedBody { - t.Errorf("expected %s, got %s", scenario.ExpectedBody, body) + if string(body) != scenario.ExpectedBody { + t.Errorf("expected:\n%s\ngot:\n%s", scenario.ExpectedBody, body) } out := make(map[string]interface{}) - if err := json.Unmarshal([]byte(body), &out); err != nil { + if err := json.Unmarshal(body, &out); err != nil { t.Error("expected body to be valid JSON, got error:", err.Error()) } })