Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 11, 08:30 UTC
caddyhttp: fix escaped path matcher over-matching longer paths (#7828)
alhuda committed
Jul 7, 2026 at 23:59 UTC
08ad064160e0d29994a55db448be21a2f9f06734
2 files changed
+29
modules/caddyhttp/matchers.go
+14
@@ -640,6 +640,20 @@ func (MatchPath) matchPatternWithEscapeSequence(escapedPath, matchPath string) b
640
iPattern++
641
}
642
643
+ // if the pattern was fully consumed before the path, the remaining path
644
+ // bytes still have to be part of the string we match against; otherwise a
645
+ // pattern with no trailing wildcard (e.g. "/foo%2fbar") would behave like a
646
+ // prefix match and incorrectly match longer paths (e.g. "/foo%2fbarbaz").
647
+ // the pattern has no more escape hints here, so compare in normalised space.
648
+ if iPath < len(escapedPath) {
649
+ remaining := escapedPath[iPath:]
650
+ if unescaped, err := url.PathUnescape(remaining); err == nil {
651
+ sb.WriteString(unescaped)
652
+ } else {
653
+ sb.WriteString(remaining)
654
+ }
655
+ }
656
+
657
// we can now treat rawpath globs (%*) as regular globs (*)
658
matchPath = strings.ReplaceAll(matchPath, "%*", "*")
659
modules/caddyhttp/matchers_test.go
+15
@@ -422,6 +422,21 @@ func TestPathMatcher(t *testing.T) {
422
input: "/ADMIN%2fPaAzZLm123NEL",
423
expect: true,
424
},
425
+ {
426
+ match: MatchPath{"/foo%2fbar"},
427
+ input: "/foo%2fbarbaz",
428
+ expect: false,
429
+ },
430
+ {
431
+ match: MatchPath{"/foo%2f"},
432
+ input: "/foo%2fx",
433
+ expect: false,
434
+ },
435
+ {
436
+ match: MatchPath{"/admin%2fpanel"},
437
+ input: "/admin%2fpanelX",
438
+ expect: false,
439
+ },
440
} {
441
err := tc.match.Provision(caddy.Context{})
442
if err == nil && tc.provisionErr {