Skip to content

Commit e77e142

Browse files
authored
Artifact tag matcher: Curb complexity when parsing regexps from user input (#3836)
* Mitigate ReDos in artifact tag matcher This commit adds a limit to the lengrh of regular expressions we take from user input to set a length limit and curve the complexity Minder handles when compiling them. Signed-off-by: Adolfo García Veytia (Puerco) <[email protected]> * Add test for artifact.BuildFilter Signed-off-by: Adolfo García Veytia (Puerco) <[email protected]> --------- Signed-off-by: Adolfo García Veytia (Puerco) <[email protected]>
1 parent 87416ec commit e77e142

File tree

2 files changed

+78
-0
lines changed

2 files changed

+78
-0
lines changed

internal/providers/artifact/versionsfilter.go

+3
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ func BuildFilter(tags []string, tagRegex string) (*filter, error) {
4747

4848
// no tags specified, but a regex was, compile it
4949
if tagRegex != "" {
50+
if len(tagRegex) > 512 {
51+
return nil, fmt.Errorf("tag regular expressions are limited to 512 characters")
52+
}
5053
re, err := regexp.Compile(tagRegex)
5154
if err != nil {
5255
return nil, fmt.Errorf("error compiling tag regex: %w", err)

internal/providers/artifact/versionsfilter_test.go

+75
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,89 @@
1616
package artifact
1717

1818
import (
19+
"regexp"
20+
"strings"
1921
"testing"
2022
"time"
2123

2224
"github.com/stretchr/testify/assert"
25+
"github.com/stretchr/testify/require"
2326

2427
provifv1 "github.com/stacklok/minder/pkg/providers/v1"
2528
)
2629

30+
func TestBuildFilter(t *testing.T) {
31+
t.Parallel()
32+
simpleRegex := `^simpleregex$`
33+
compiledSimpleRegex := regexp.MustCompile(simpleRegex)
34+
for _, tc := range []struct {
35+
name string
36+
tags []string
37+
regex string
38+
expected *filter
39+
mustErr bool
40+
}{
41+
{
42+
name: "tags",
43+
tags: []string{"hello", "bye"},
44+
mustErr: false,
45+
expected: &filter{
46+
tagMatcher: &tagListMatcher{tags: []string{"hello", "bye"}},
47+
retentionPeriod: time.Time{},
48+
},
49+
},
50+
{
51+
name: "empty-tag",
52+
tags: []string{"hello", ""},
53+
mustErr: true,
54+
},
55+
{
56+
name: "regex",
57+
tags: []string{},
58+
regex: simpleRegex,
59+
mustErr: false,
60+
expected: &filter{
61+
tagMatcher: &tagRegexMatcher{
62+
re: compiledSimpleRegex,
63+
},
64+
},
65+
},
66+
{
67+
name: "invalidregexp",
68+
tags: []string{},
69+
regex: `$(invalid^`,
70+
mustErr: true,
71+
},
72+
{
73+
name: "valid-long-regexp",
74+
tags: []string{},
75+
regex: `^` + strings.Repeat("A", 1000) + `$`,
76+
mustErr: true,
77+
},
78+
{
79+
name: "no-tags",
80+
tags: []string{},
81+
mustErr: false,
82+
expected: &filter{
83+
tagMatcher: &tagAllMatcher{},
84+
},
85+
},
86+
} {
87+
tc := tc
88+
t.Run(tc.name, func(t *testing.T) {
89+
t.Parallel()
90+
f, err := BuildFilter(tc.tags, tc.regex)
91+
if tc.mustErr {
92+
require.Error(t, err)
93+
return
94+
}
95+
require.NoError(t, err)
96+
require.NotNil(t, f.tagMatcher)
97+
require.Equal(t, tc.expected.tagMatcher, f.tagMatcher)
98+
})
99+
}
100+
}
101+
27102
func Test_filter_IsSkippable(t *testing.T) {
28103
t.Parallel()
29104

0 commit comments

Comments
 (0)