66 "regexp"
77 "strings"
88
9+ "github.com/dlclark/regexp2"
910 k8svalidation "k8s.io/apimachinery/pkg/util/validation"
1011)
1112
@@ -15,7 +16,7 @@ const (
1516)
1617
1718var (
18- pathRegexp = regexp .MustCompile ("^" + pathFmt + "$" )
19+ pathRegexp = regexp2 .MustCompile ("^" + pathFmt + "$" , 0 )
1920 pathExamples = []string {"/" , "/path" , "/path/subpath-123" }
2021)
2122
@@ -91,7 +92,9 @@ func validatePath(path string) error {
9192 return nil
9293 }
9394
94- if ! pathRegexp .MatchString (path ) {
95+ if valid , err := pathRegexp .MatchString (path ); err != nil {
96+ return fmt .Errorf ("failed to validate path %q: %w" , path , err )
97+ } else if ! valid {
9598 msg := k8svalidation .RegexError (pathErrMsg , pathFmt , pathExamples ... )
9699 return errors .New (msg )
97100 }
@@ -108,56 +111,39 @@ func validatePathInMatch(path string) error {
108111 if path == "" {
109112 return errors .New ("cannot be empty" )
110113 }
111-
112- if ! pathRegexp .MatchString (path ) {
114+ if valid , err := pathRegexp .MatchString (path ); err != nil {
115+ return fmt .Errorf ("failed to validate path in match %q: %w" , path , err )
116+ } else if ! valid {
113117 msg := k8svalidation .RegexError (pathErrMsg , pathFmt , pathExamples ... )
114118 return errors .New (msg )
115119 }
116120
117121 return nil
118122}
119123
120- // validatePathInRegexMatch a path used in a regex location directive.
121- // 1. Must be non-empty and start with '/'
122- // 2. Forbidden characters in NGINX location context: {}, ;, whitespace
123- // 3. Must compile under Go's regexp (RE2)
124- // 4. Disallow unescaped '$' (NGINX variables / PCRE backrefs)
125- // 5. Disallow lookahead/lookbehind (unsupported in RE2)
126- // 6. Disallow backreferences like \1, \2 (RE2 unsupported).
124+ // validatePathInRegexMatch validates a path used in a regex location directive.
125+ //
126+ // It uses Perl5 compatible regexp2 package along with RE2 compatibility.
127+ //
128+ // Checks:
129+ // 1. Non-empty.
130+ // 2. Satisfies NGINX location path shape.
131+ // 3. Compiles as a regexp2 regular expression with RE2 option to support named capturing group.
132+ // No extra bans on backrefs, lookarounds, '$'.
127133func validatePathInRegexMatch (path string ) error {
128134 if path == "" {
129135 return errors .New ("cannot be empty" )
130136 }
131137
132- if ! pathRegexp .MatchString (path ) {
133- return errors .New (k8svalidation .RegexError (pathErrMsg , pathFmt , pathExamples ... ))
134- }
135-
136- if _ , err := regexp .Compile (path ); err != nil {
137- return fmt .Errorf ("invalid RE2 regex for path '%s': %w" , path , err )
138- }
139-
140- for i := range len (path ) {
141- if path [i ] == '$' && (i == 0 || path [i - 1 ] != '\\' ) {
142- return fmt .Errorf ("invalid unescaped `$` at position %d in path '%s'" , i , path )
143- }
144- }
145-
146- lookarounds := []string {"(?=" , "(?!" , "(?<=" , "(?<!" }
147- for _ , la := range lookarounds {
148- if strings .Contains (path , la ) {
149- return fmt .Errorf ("lookahead/lookbehind '%s' found in path '%s' which is not supported in RE2" , la , path )
150- }
138+ if valid , err := pathRegexp .MatchString (path ); err != nil {
139+ return fmt .Errorf ("failed to validate path %q: %w" , path , err )
140+ } else if ! valid {
141+ msg := k8svalidation .RegexError (pathErrMsg , pathFmt , pathExamples ... )
142+ return errors .New (msg )
151143 }
152144
153- backref := regexp .MustCompile (`\\[0-9]+` )
154- matches := backref .FindAllStringIndex (path , - 1 )
155- if len (matches ) > 0 {
156- var positions []string
157- for _ , m := range matches {
158- positions = append (positions , fmt .Sprintf ("[%d-%d]" , m [0 ], m [1 ]))
159- }
160- return fmt .Errorf ("backreference(s) %v found in path '%s' which are not supported in RE2" , positions , path )
145+ if _ , err := regexp2 .Compile (path , regexp2 .RE2 ); err != nil {
146+ return fmt .Errorf ("invalid regex for path %q: %w" , path , err )
161147 }
162148
163149 return nil
0 commit comments