44 "fmt"
55 "regexp"
66 "strings"
7+ "time"
78
89 "github.com/dlclark/regexp2"
910 "github.com/grafana/grafana-plugin-sdk-go/backend"
@@ -65,6 +66,78 @@ func splitKeyParams(paramStr string) []string {
6566 return params
6667}
6768
69+ // isPathologicalRegex detects potentially dangerous regex patterns that could cause ReDoS
70+ func isPathologicalRegex (pattern string ) bool {
71+ // Check for consecutive quantifiers
72+ consecutiveQuantifiers := []string {`\*\*` , `\+\+` , `\*\+` , `\+\*` }
73+ for _ , q := range consecutiveQuantifiers {
74+ if matched , _ := regexp .MatchString (q , pattern ); matched {
75+ return true
76+ }
77+ }
78+
79+ // Check for nested quantifiers
80+ nestedQuantifiers := []string {
81+ `\([^)]*\+[^)]*\)\+` , // (a+)+
82+ `\([^)]*\*[^)]*\)\*` , // (a*)*
83+ `\([^)]*\+[^)]*\)\*` , // (a+)*
84+ `\([^)]*\*[^)]*\)\+` , // (a*)+
85+ }
86+ for _ , nested := range nestedQuantifiers {
87+ if matched , _ := regexp .MatchString (nested , pattern ); matched {
88+ return true
89+ }
90+ }
91+
92+ // Check for specific catastrophic patterns
93+ catastrophicPatterns := []string {
94+ `\(\.\*\)\*` , // (.*)*
95+ `\(\.\+\)\+` , // (.+)+
96+ `\(\.\*\)\+` , // (.*)+
97+ `\(\.\+\)\*` , // (.+)*
98+ }
99+ for _ , catastrophic := range catastrophicPatterns {
100+ if matched , _ := regexp .MatchString (catastrophic , pattern ); matched {
101+ return true
102+ }
103+ }
104+
105+ // Check for obvious overlapping alternation (manual check for exact duplicates)
106+ if strings .Contains (pattern , "(a|a)" ) ||
107+ strings .Contains (pattern , "(1|1)" ) ||
108+ strings .Contains (pattern , "(.*|.*)" ) {
109+ return true
110+ }
111+
112+ return false
113+ }
114+
115+ // safeRegexpCompile compiles a regex with timeout protection
116+ func safeRegexpCompile (pattern string ) (* regexp2.Regexp , error ) {
117+ // Channel to receive compilation result
118+ resultCh := make (chan struct {
119+ regex * regexp2.Regexp
120+ err error
121+ }, 1 )
122+
123+ // Compile regex in goroutine with timeout
124+ go func () {
125+ regex , err := regexp2 .Compile (pattern , regexp2 .RE2 )
126+ resultCh <- struct {
127+ regex * regexp2.Regexp
128+ err error
129+ }{regex , err }
130+ }()
131+
132+ // Wait for compilation or timeout
133+ select {
134+ case result := <- resultCh :
135+ return result .regex , result .err
136+ case <- time .After (5 * time .Second ):
137+ return nil , fmt .Errorf ("regex compilation timeout (5s) - pattern may be too complex" )
138+ }
139+ }
140+
68141func parseFilter (filter string ) (* regexp2.Regexp , error ) {
69142 vaildREModifiers := "imncsxrde"
70143 regex := regexp .MustCompile (`^/(.+)/([imncsxrde]*)$` )
@@ -75,17 +148,35 @@ func parseFilter(filter string) (*regexp2.Regexp, error) {
75148 return nil , nil
76149 }
77150
151+ regexPattern := matches [1 ]
152+
153+ // Security: Check for pathological regex patterns
154+ if isPathologicalRegex (regexPattern ) {
155+ return nil , backend .DownstreamErrorf ("error parsing regexp: potentially dangerous regex pattern detected" )
156+ }
157+
158+ // Security: Limit regex pattern length
159+ if len (regexPattern ) > 1000 {
160+ return nil , backend .DownstreamErrorf ("error parsing regexp: pattern too long (max 1000 characters)" )
161+ }
162+
78163 pattern := ""
79164 if matches [2 ] != "" {
80165 if flagRE .MatchString (matches [2 ]) {
81166 pattern += "(?" + matches [2 ] + ")"
82167 } else {
83- return nil , backend .DownstreamError ( fmt . Errorf ( "error parsing regexp: unsupported flags `%s` (expected [%s])" , matches [2 ], vaildREModifiers ) )
168+ return nil , backend .DownstreamErrorf ( "error parsing regexp: unsupported flags `%s` (expected [%s])" , matches [2 ], vaildREModifiers )
84169 }
85170 }
86- pattern += matches [1 ]
171+ pattern += regexPattern
172+
173+ // Security: Test compilation with timeout
174+ compiled , err := safeRegexpCompile (pattern )
175+ if err != nil {
176+ return nil , backend .DownstreamErrorf ("error parsing regexp: %v" , err )
177+ }
87178
88- return regexp2 . Compile ( pattern , regexp2 . RE2 )
179+ return compiled , nil
89180}
90181
91182func isRegex (filter string ) bool {
0 commit comments