@marcel91200 / caddy Mirror · read-only / commits / 08ad0641
Fork 0

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 {