diff --git a/provider/label/label.go b/provider/label/label.go index b0069eed3..8eb03291e 100644 --- a/provider/label/label.go +++ b/provider/label/label.go @@ -45,6 +45,21 @@ func GetStringValue(labels map[string]string, labelName string, defaultValue str return defaultValue } +// GetStringSafeValue get string value associated to a label and check if the content is "quotable". +func GetStringSafeValue(labels map[string]string, labelName string, defaultValue string) (string, error) { + value, ok := labels[labelName] + if !ok || len(value) <= 0 { + return defaultValue, nil + } + + _, err := strconv.Unquote(`"` + value + `"`) + if err != nil { + return value, err + } + + return value, nil +} + // GetBoolValue get bool value associated to a label func GetBoolValue(labels map[string]string, labelName string, defaultValue bool) bool { rawValue, ok := labels[labelName] diff --git a/provider/label/label_test.go b/provider/label/label_test.go index 13a4476d4..cd2ac7a91 100644 --- a/provider/label/label_test.go +++ b/provider/label/label_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestSplitAndTrimString(t *testing.T) { @@ -106,6 +107,78 @@ func TestGetStringValue(t *testing.T) { } } +func TestGetStringSafeValue(t *testing.T) { + testCases := []struct { + desc string + labels map[string]string + labelName string + defaultValue string + expected string + expectedError bool + }{ + { + desc: "empty labels map", + labelName: "foo", + defaultValue: "default", + expected: "default", + }, + { + desc: "unescaped value", + labels: map[string]string{ + "foo": `^https?://(test\.beta\.redacted\.org|redacted\.com)/(.*)`, + }, + labelName: "foo", + defaultValue: "default", + expected: `^https?://(test\.beta\.redacted\.org|redacted\.com)/(.*)`, + expectedError: true, + }, + { + desc: "escaped value", + labels: map[string]string{ + "foo": `^https?://(test\\.beta\\.redacted\\.org|redacted\\.com)/(.*)`, + }, + labelName: "foo", + defaultValue: "default", + expected: `^https?://(test\\.beta\\.redacted\\.org|redacted\\.com)/(.*)`, + }, + { + desc: "empty value", + labels: map[string]string{ + "foo": "", + }, + labelName: "foo", + defaultValue: "default", + expected: "default", + }, + { + desc: "non existing label", + labels: map[string]string{ + "foo": "bar", + }, + labelName: "fii", + defaultValue: "default", + expected: "default", + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + got, err := GetStringSafeValue(test.labels, test.labelName, test.defaultValue) + + if test.expectedError { + assert.Error(t, err) + } else { + require.NoError(t, err) + } + + assert.Equal(t, test.expected, got) + }) + } +} + func TestGetBoolValue(t *testing.T) { testCases := []struct { desc string diff --git a/provider/label/partial.go b/provider/label/partial.go index 8f0ad8b08..c7c4f4bc5 100644 --- a/provider/label/partial.go +++ b/provider/label/partial.go @@ -50,8 +50,13 @@ func GetRedirect(labels map[string]string) *types.Redirect { if Has(labels, TraefikFrontendRedirectRegex) && Has(labels, TraefikFrontendRedirectReplacement) { + value, err := GetStringSafeValue(labels, TraefikFrontendRedirectRegex, "") + if err != nil { + log.Errorf("Invalid regex syntax: %s", value) + return nil + } return &types.Redirect{ - Regex: GetStringValue(labels, TraefikFrontendRedirectRegex, ""), + Regex: value, Replacement: GetStringValue(labels, TraefikFrontendRedirectReplacement, ""), Permanent: permanent, } diff --git a/provider/label/partial_test.go b/provider/label/partial_test.go index 86f20b57e..306a9d7a1 100644 --- a/provider/label/partial_test.go +++ b/provider/label/partial_test.go @@ -449,12 +449,19 @@ func TestGetRedirect(t *testing.T) { labels map[string]string expected *types.Redirect }{ - { desc: "should return nil when no redirect labels", labels: map[string]string{}, expected: nil, }, + { + desc: "should return nil when regex syntax is invalid", + labels: map[string]string{ + TraefikFrontendRedirectRegex: `^https?://(test\.beta\.redacted\.org|redacted\.com)/(.*)`, + TraefikFrontendRedirectReplacement: "$1", + }, + expected: nil, + }, { desc: "should use only entry point tag when mix regex redirect and entry point redirect", labels: map[string]string{ @@ -805,6 +812,7 @@ func TestGetAuth(t *testing.T) { }) } } + func TestGetPassTLSClientCert(t *testing.T) { testCases := []struct { desc string