Skip to content

Commit

Permalink
Update ValidateMatcher in silence.go
Browse files Browse the repository at this point in the history
Signed-off-by: George Robinson <george.robinson@grafana.com>
  • Loading branch information
grobinson-grafana committed Nov 14, 2023
1 parent 16fb81c commit 3f30b33
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 3 deletions.
2 changes: 1 addition & 1 deletion cmd/alertmanager/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,8 @@ func run() int {
level.Error(logger).Log("msg", "error parsing the feature flag list", "err", err)
return 1
}

compat.InitFromFlags(logger, featureConfig)
silence.InitFromFlags(logger, featureConfig)

err = os.MkdirAll(*dataDir, 0o777)
if err != nil {
Expand Down
39 changes: 39 additions & 0 deletions silence/silence.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package silence
import (
"bytes"
"fmt"
"github.com/prometheus/alertmanager/featurecontrol"
"io"
"math/rand"
"os"
Expand All @@ -26,6 +27,7 @@ import (
"sort"
"sync"
"time"
"unicode/utf8"

"github.com/benbjohnson/clock"
"github.com/go-kit/log"
Expand Down Expand Up @@ -469,6 +471,20 @@ func (s *Silences) GC() (int, error) {

// ValidateMatcher runs validation on the matcher name, type, and pattern.
var ValidateMatcher = func(m *pb.Matcher) error {
return validateClassicMatcher(m)
}

// InitFromFlags initializes the validation function from the flagger.
func InitFromFlags(l log.Logger, f featurecontrol.Flagger) {
if !f.ClassicMatchersParsing() {
ValidateMatcher = func(m *pb.Matcher) error {
return validateUTF8Matcher(m)
}
}
}

// validateClassicMatcher validates the matcher against the classic rules.
func validateClassicMatcher(m *pb.Matcher) error {
if !model.LabelName(m.Name).IsValid() {
return fmt.Errorf("invalid label name %q", m.Name)
}
Expand All @@ -487,6 +503,29 @@ var ValidateMatcher = func(m *pb.Matcher) error {
return nil
}

// validateUTF8Matcher validates the matcher against the UTF-8 rules.
func validateUTF8Matcher(m *pb.Matcher) error {
if !utf8.ValidString(m.Name) {
return fmt.Errorf("invalid label name %q", m.Name)
}
switch m.Type {
case pb.Matcher_EQUAL, pb.Matcher_NOT_EQUAL:
if !utf8.ValidString(m.Pattern) {
return fmt.Errorf("invalid label value %q", m.Pattern)
}
case pb.Matcher_REGEXP, pb.Matcher_NOT_REGEXP:
if !utf8.ValidString(m.Pattern) {
return fmt.Errorf("invalid regular expression %q", m.Pattern)
}
if _, err := regexp.Compile(m.Pattern); err != nil {
return fmt.Errorf("invalid regular expression %q: %s", m.Pattern, err)
}
default:
return fmt.Errorf("unknown matcher type %q", m.Type)
}
return nil
}

func matchesEmpty(m *pb.Matcher) bool {
switch m.Type {
case pb.Matcher_EQUAL:
Expand Down
108 changes: 106 additions & 2 deletions silence/silence_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1114,7 +1114,7 @@ func TestSilencer(t *testing.T) {
require.True(t, s.Mutes(model.LabelSet{"foo": "bar"}), "expected alert silenced by activated second silence")
}

func TestValidateMatcher(t *testing.T) {
func TestValidateClassicMatcher(t *testing.T) {
cases := []struct {
m *pb.Matcher
err string
Expand Down Expand Up @@ -1154,6 +1154,103 @@ func TestValidateMatcher(t *testing.T) {
Type: pb.Matcher_EQUAL,
},
err: "invalid label name",
}, {
m: &pb.Matcher{
Name: "\xf0\x9f\x99\x82", // U+1F642
Pattern: "a",
Type: pb.Matcher_EQUAL,
},
err: "invalid label name",
}, {
m: &pb.Matcher{
Name: "a",
Pattern: "((",
Type: pb.Matcher_REGEXP,
},
err: "invalid regular expression",
}, {
m: &pb.Matcher{
Name: "a",
Pattern: "))",
Type: pb.Matcher_NOT_REGEXP,
},
err: "invalid regular expression",
}, {
m: &pb.Matcher{
Name: "a",
Pattern: "\xff",
Type: pb.Matcher_EQUAL,
},
err: "invalid label value",
}, {
m: &pb.Matcher{
Name: "a",
Pattern: "\xf0\x9f\x99\x82", // U+1F642
Type: pb.Matcher_EQUAL,
},
err: "",
}, {
m: &pb.Matcher{
Name: "a",
Pattern: "b",
Type: 333,
},
err: "unknown matcher type",
},
}

for _, c := range cases {
checkErr(t, c.err, validateClassicMatcher(c.m))
}
}

func TestValidateUTF8Matcher(t *testing.T) {
cases := []struct {
m *pb.Matcher
err string
}{
{
m: &pb.Matcher{
Name: "a",
Pattern: "b",
Type: pb.Matcher_EQUAL,
},
err: "",
}, {
m: &pb.Matcher{
Name: "a",
Pattern: "b",
Type: pb.Matcher_NOT_EQUAL,
},
err: "",
}, {
m: &pb.Matcher{
Name: "a",
Pattern: "b",
Type: pb.Matcher_REGEXP,
},
err: "",
}, {
m: &pb.Matcher{
Name: "a",
Pattern: "b",
Type: pb.Matcher_NOT_REGEXP,
},
err: "",
}, {
m: &pb.Matcher{
Name: "00",
Pattern: "a",
Type: pb.Matcher_EQUAL,
},
err: "",
}, {
m: &pb.Matcher{
Name: "\xf0\x9f\x99\x82", // U+1F642
Pattern: "a",
Type: pb.Matcher_EQUAL,
},
err: "",
}, {
m: &pb.Matcher{
Name: "a",
Expand All @@ -1175,6 +1272,13 @@ func TestValidateMatcher(t *testing.T) {
Type: pb.Matcher_EQUAL,
},
err: "invalid label value",
}, {
m: &pb.Matcher{
Name: "a",
Pattern: "\xf0\x9f\x99\x82", // U+1F642
Type: pb.Matcher_EQUAL,
},
err: "",
}, {
m: &pb.Matcher{
Name: "a",
Expand All @@ -1186,7 +1290,7 @@ func TestValidateMatcher(t *testing.T) {
}

for _, c := range cases {
checkErr(t, c.err, ValidateMatcher(c.m))
checkErr(t, c.err, validateUTF8Matcher(c.m))
}
}

Expand Down

0 comments on commit 3f30b33

Please sign in to comment.