Repository: caddyserver/caddy (language: go)

Issue:
`vars_regexp` matcher does not work with placeholders
I'm trying to introduce a simple WAF on Caddy, and my configuration is as follows.

**Caddyfile**
```
:80 {
	root * /caddy/web_root
	import rewrite_waf.part

	file_server {
		browse
	}
}
```

**rewrite_waf.part**
```
@disallowed_query_string_for_illegal_characters {
    vars_regexp {query} (<|>|'|%0A|%0D|%27|%3C|%3E|%00)
}
respond @disallowed_query_string_for_illegal_characters "waf" 403
```

Then, I always get a WAF error whether or not a query string contains "<" (less-than sign) or ">" (greater-than sign). 

```
> curl localhost/?a=a
waf
> curl localhost/?a=\>
waf
```

Is there any good way to solve this?

**Caddy version**
v2.6.2 h1:wKoFIxpmOJLGl3QXoo6PNbYvGW4xLEgo32GPBEjWL8o=



Discussion / hints:
Ah, I see a bug with `vars_regexp` when used with placeholders. It doesn't work as it should. I have a fix for it.

But as a workaround for now, you can use the `expression` matcher instead to do it.

```
@disallowed `{query}.matches("(<|>|'|%0A|%0D|%27|%3C|%3E|%00)")`
respond @disallowed "waf" 403
```

Fix patch:
diff --git a/modules/caddyhttp/vars.go b/modules/caddyhttp/vars.go
index b4e1d897517..d2d7f62c5b1 100644
--- a/modules/caddyhttp/vars.go
+++ b/modules/caddyhttp/vars.go
@@ -255,9 +255,18 @@ func (m MatchVarsRE) Provision(ctx caddy.Context) error {
 func (m MatchVarsRE) Match(r *http.Request) bool {
 	vars := r.Context().Value(VarsCtxKey).(map[string]any)
 	repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
-	for k, rm := range m {
+	for key, val := range m {
+		var varValue any
+		if strings.HasPrefix(key, "{") &&
+			strings.HasSuffix(key, "}") &&
+			strings.Count(key, "{") == 1 {
+			varValue, _ = repl.Get(strings.Trim(key, "{}"))
+		} else {
+			varValue = vars[key]
+		}
+
 		var varStr string
-		switch vv := vars[k].(type) {
+		switch vv := varValue.(type) {
 		case string:
 			varStr = vv
 		case fmt.Stringer:
@@ -267,13 +276,9 @@ func (m MatchVarsRE) Match(r *http.Request) bool {
 		default:
 			varStr = fmt.Sprintf("%v", vv)
 		}
-		valExpanded := repl.ReplaceAll(varStr, "")
-		if match := rm.Match(valExpanded, repl); match {
-			return match
-		}
 
-		replacedVal := repl.ReplaceAll(k, "")
-		if match := rm.Match(replacedVal, repl); match {
+		valExpanded := repl.ReplaceAll(varStr, "")
+		if match := val.Match(valExpanded, repl); match {
 			return match
 		}
 	}


Test patch: