@marcel91200 / caddy Mirror · read-only / commits / d4165a4e
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, 03:30 UTC

caddyhttp: Reject repeated expression matchers in a matcher set (#8183)

* caddyhttp: Combine repeated expression matchers in a matcher set The Caddyfile parser groups repeated matchers of the same type and hands all their segments to the module, but the expression matcher only read the first segment, so every later `expression` line in a named matcher or `not` block was dropped without an error. Read every segment and AND the expressions, like the other matchers in a set. * caddyhttp: Put each joined expression on its own line A trailing // comment in one expression no longer swallows the closing parenthesis and the expressions joined after it. * caddyhttp: Reject repeated expression matchers instead of joining them A second expression line in a named matcher or a not block is now a Caddyfile error that points to combining them with && in one expression, instead of being dropped or joined with an implied operator.

Jay Hemnani committed Oct 11, 2026 at 02:20 UTC d4165a4eb1fdb129e8ce8390099021a00239ecf7
2 files changed +38 -15
modules/caddyhttp/celmatcher.go
+19 -15
@@ -243,24 +243,28 @@ func (m *MatchExpression) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
243 // CEL expression (e.g. strings) and we should retain that
244 if d.CountRemainingArgs() > 1 {
245 m.Expr = strings.Join(d.RemainingArgsRaw(), " ")
246 - return nil
247 - }
246 + } else {
247 + // there should at least be one arg
248 + if !d.NextArg() {
249 + return d.ArgErr()
250 + }
251
249 - // there should at least be one arg
250 - if !d.NextArg() {
251 - return d.ArgErr()
252 - }
252 + // if there's only one token, then we can safely grab the
253 + // cleaned token (no quotes) and use that as the expression
254 + // because there's no valid CEL expression that is only a
255 + // quoted string; commonly quotes are used in Caddyfile to
256 + // define the expression
257 + m.Expr = d.Val()
258
254 - // if there's only one token, then we can safely grab the
255 - // cleaned token (no quotes) and use that as the expression
256 - // because there's no valid CEL expression that is only a
257 - // quoted string; commonly quotes are used in Caddyfile to
258 - // define the expression
259 - m.Expr = d.Val()
259 + // use the named matcher's name, to fill regexp
260 + // matchers names by default
261 + m.Name = d.GetContextString(caddyfile.MatcherNameCtxKey)
262 + }
263
261 - // use the named matcher's name, to fill regexp
262 - // matchers names by default
263 - m.Name = d.GetContextString(caddyfile.MatcherNameCtxKey)
264 + // a matcher set holds only one expression matcher
265 + if d.Next() {
266 + return d.Err("repeated expression matchers are not supported, combine them into one expression with &&")
267 + }
268
269 return nil
270 }
modules/caddyhttp/celmatcher_test.go
+19
@@ -21,9 +21,11 @@ import (
21 "encoding/pem"
22 "net/http"
23 "net/http/httptest"
24 + "strings"
25 "testing"
26
27 "github.com/caddyserver/caddy/v2"
28 + "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
29 )
30
31 var (
@@ -605,3 +607,20 @@ func TestMatchExpressionProvision(t *testing.T) {
607 })
608 }
609 }
610 +
611 +func TestMatchExpressionUnmarshalCaddyfileRepeated(t *testing.T) {
612 + for _, input := range []string{`
613 + expression {http.request.method} == 'GET'
614 + expression {http.request.uri.path}.startsWith('/public/')
615 + `, `
616 + expression "{http.request.method} == 'GET'"
617 + expression {http.request.uri.path}.startsWith('/public/')
618 + `} {
619 + d := caddyfile.NewTestDispenser(input)
620 + m := new(MatchExpression)
621 + err := m.UnmarshalCaddyfile(d)
622 + if err == nil || !strings.Contains(err.Error(), "repeated expression matchers are not supported") {
623 + t.Errorf("UnmarshalCaddyfile() error = %v, want repeated expression error (expression: %s)", err, m.Expr)
624 + }
625 + }
626 +}