From ccab3e7e0f1c3dec0b06e378b69d83e0fabb8c23 Mon Sep 17 00:00:00 2001 From: "Grot (@grafanabot)" <43478413+grafanabot@users.noreply.github.com> Date: Mon, 23 May 2022 18:33:05 -0400 Subject: [PATCH] Parse template when validating it (#49282) (#49284) (cherry picked from commit e8b498fe8b7612d20cde106bc4d46c5aee93842d) Co-authored-by: Alexander Weaver --- .../definitions/provisioning_templates.go | 6 ++++ .../ngalert/provisioning/templates_test.go | 36 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/pkg/services/ngalert/api/tooling/definitions/provisioning_templates.go b/pkg/services/ngalert/api/tooling/definitions/provisioning_templates.go index 65ab1671a27..f4aee44bbe6 100644 --- a/pkg/services/ngalert/api/tooling/definitions/provisioning_templates.go +++ b/pkg/services/ngalert/api/tooling/definitions/provisioning_templates.go @@ -2,6 +2,7 @@ package definitions import ( "fmt" + "html/template" "regexp" "strings" @@ -74,6 +75,11 @@ func (t *MessageTemplate) Validate() error { return fmt.Errorf("template must have content") } + _, err := template.New("").Parse(t.Template) + if err != nil { + return fmt.Errorf("invalid template: %w", err) + } + content := strings.TrimSpace(t.Template) found, err := regexp.MatchString(`\{\{\s*define`, content) if err != nil { diff --git a/pkg/services/ngalert/provisioning/templates_test.go b/pkg/services/ngalert/provisioning/templates_test.go index 2016ec284e3..d0a3fe7b1b6 100644 --- a/pkg/services/ngalert/provisioning/templates_test.go +++ b/pkg/services/ngalert/provisioning/templates_test.go @@ -234,6 +234,42 @@ func TestTemplateService(t *testing.T) { require.Equal(t, tmpl.Template, result.Template) }) + + t.Run("rejects syntactically invalid template", func(t *testing.T) { + sut := createTemplateServiceSut() + tmpl := definitions.MessageTemplate{ + Name: "name", + Template: "{{ .MyField }", + } + sut.config.(*MockAMConfigStore).EXPECT(). + getsConfig(models.AlertConfiguration{ + AlertmanagerConfiguration: defaultConfig, + }) + sut.config.(*MockAMConfigStore).EXPECT().saveSucceeds() + sut.prov.(*MockProvisioningStore).EXPECT().saveSucceeds() + + _, err := sut.SetTemplate(context.Background(), 1, tmpl) + + require.ErrorIs(t, err, ErrValidation) + }) + + t.Run("does not reject template with unknown field", func(t *testing.T) { + sut := createTemplateServiceSut() + tmpl := definitions.MessageTemplate{ + Name: "name", + Template: "{{ .NotAField }}", + } + sut.config.(*MockAMConfigStore).EXPECT(). + getsConfig(models.AlertConfiguration{ + AlertmanagerConfiguration: defaultConfig, + }) + sut.config.(*MockAMConfigStore).EXPECT().saveSucceeds() + sut.prov.(*MockProvisioningStore).EXPECT().saveSucceeds() + + _, err := sut.SetTemplate(context.Background(), 1, tmpl) + + require.NoError(t, err) + }) }) t.Run("deleting templates", func(t *testing.T) {