diff --git a/alert.go b/alert.go
index 3a979a387..dd32e71a3 100644
--- a/alert.go
+++ b/alert.go
@@ -126,6 +126,9 @@ func newAlertNode(et *ExecutingTask, n *pipeline.AlertNode, d NodeDiagnostic) (a
return html.JS(tmpBuffer.String())
},
+ "safeHtml": func(v interface{}) html.HTML {
+ return html.HTML(v.(string))
+ },
}).Parse(n.Details)
if err != nil {
return nil, err
diff --git a/integrations/streamer_test.go b/integrations/streamer_test.go
index 0f5efcdc6..d18cbf366 100644
--- a/integrations/streamer_test.go
+++ b/integrations/streamer_test.go
@@ -9,6 +9,7 @@ import (
"html"
"io/ioutil"
"math/rand"
+ "mime/quotedprintable"
"net/http"
"net/http/httptest"
"net/mail"
@@ -16,6 +17,7 @@ import (
"path"
"path/filepath"
"reflect"
+ "strings"
"sync/atomic"
"testing"
"text/template"
@@ -10200,6 +10202,103 @@ Value: 10
}
}
+func TestStream_AlertEmail_HtmlEscape(t *testing.T) {
+ var script = `
+stream
+ |from()
+ .measurement('cpu')
+ .where(lambda: "host" == 'serverA')
+ .groupBy('host')
+ |window()
+ .period(10s)
+ .every(10s)
+ |count('value')
+ |eval(lambda: 'HTML field')
+ .keep()
+ .as('htmlFieldTest')
+ |eval(lambda: 'HTML tag')
+ .keep()
+ .as('htmlTagTest')
+ .tags('htmlTagTest')
+ |alert()
+ .id('')
+ .details('
+Field, escaped: {{ index .Fields "htmlFieldTest" }}
+Field, safeHtml: {{ index .Fields "htmlFieldTest" | safeHtml }}
+Tag, escaped: {{ index .Tags "htmlTagTest" }}
+Tag, safeHtml: {{ index .Tags "htmlTagTest" | safeHtml }}
+')
+ .crit(lambda: "count" > 8.0)
+ .email('user1@example.com')
+`
+ expField := "HTML field"
+ expTag := "HTML tag"
+ expDetails := fmt.Sprintf(
+ "\nField, escaped: %v\nField, safeHtml: %v\nTag, escaped: %v\nTag, safeHtml: %v\n",
+ html.EscapeString(expField),
+ expField,
+ html.EscapeString(expTag),
+ expTag)
+ var quotedPrintableDetails bytes.Buffer
+ quotedPrintableWriter := quotedprintable.NewWriter("edPrintableDetails)
+ _, _ = quotedPrintableWriter.Write([]byte(expDetails))
+ _ = quotedPrintableWriter.Close()
+
+ expMail := []*smtptest.Message{
+ {
+ Header: mail.Header{
+ "Mime-Version": []string{"1.0"},
+ "Content-Type": []string{"text/html; charset=UTF-8"},
+ "Content-Transfer-Encoding": []string{"quoted-printable"},
+ },
+ Body: strings.Replace(quotedPrintableDetails.String(), "\r", "", -1),
+ },
+ }
+
+ smtpServer, err := smtptest.NewServer()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer smtpServer.Close()
+ sc := smtp.Config{
+ Enabled: true,
+ Host: smtpServer.Host,
+ Port: smtpServer.Port,
+ From: "test@example.com",
+ }
+ smtpService := smtp.NewService(sc, diagService.NewSMTPHandler())
+ if err := smtpService.Open(); err != nil {
+ t.Fatal(err)
+ }
+ defer smtpService.Close()
+
+ tmInit := func(tm *kapacitor.TaskMaster) {
+ tm.SMTPService = smtpService
+ }
+
+ testStreamerNoOutput(t, "TestStream_Alert", script, 13*time.Second, tmInit)
+
+ // Close both client and server to ensure all message are processed
+ smtpService.Close()
+ smtpServer.Close()
+
+ errors := smtpServer.Errors()
+ if got, exp := len(errors), 0; got != exp {
+ t.Errorf("unexpected smtp server errors: %v", errors)
+ }
+
+ msgs := smtpServer.SentMessages()
+ if got, exp := len(msgs), len(expMail); got != exp {
+ t.Errorf("unexpected number of messages sent: got %d exp %d", got, exp)
+ }
+ for i, exp := range expMail {
+ got := msgs[i]
+ if err := exp.Compare(got); err != nil {
+ t.Errorf("%d %s", i, err)
+ }
+ }
+}
+
func TestStream_AlertSNMPTrap(t *testing.T) {
var script = `