@marcel91200 / caddy Mirror · read-only / commits / 7dcc041e
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, 12:15 UTC

vars: Add matcher placeholder handling tests (#7640)

* vars: add matcher placeholder handling tests * vars: add query placeholder matcher coverage

Zen Dodd committed Apr 11, 2026 at 08:27 UTC 7dcc041eec7e6221e99b8f13225b7afba6cacb8b
1 file changed +159
modules/caddyhttp/vars_test.go new
+159
@@ -0,0 +1,159 @@
1 +// Copyright 2015 Matthew Holt and The Caddy Authors
2 +//
3 +// Licensed under the Apache License, Version 2.0 (the "License");
4 +// you may not use this file except in compliance with the License.
5 +// You may obtain a copy of the License at
6 +//
7 +// http://www.apache.org/licenses/LICENSE-2.0
8 +//
9 +// Unless required by applicable law or agreed to in writing, software
10 +// distributed under the License is distributed on an "AS IS" BASIS,
11 +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 +// See the License for the specific language governing permissions and
13 +// limitations under the License.
14 +
15 +package caddyhttp
16 +
17 +import (
18 + "context"
19 + "net/http"
20 + "net/http/httptest"
21 + "testing"
22 +
23 + "github.com/caddyserver/caddy/v2"
24 +)
25 +
26 +func newVarsTestRequest(t *testing.T, target string, headers http.Header, vars map[string]any) (*http.Request, *caddy.Replacer) {
27 + t.Helper()
28 +
29 + if target == "" {
30 + target = "https://example.com/test"
31 + }
32 +
33 + req := httptest.NewRequest(http.MethodGet, target, nil)
34 + req.Header = headers
35 +
36 + repl := caddy.NewReplacer()
37 + ctx := context.WithValue(req.Context(), caddy.ReplacerCtxKey, repl)
38 + if vars == nil {
39 + vars = make(map[string]any)
40 + }
41 + // Inject vars directly so these tests exercise matcher-side handling of
42 + // already-resolved values, not VarsMiddleware placeholder expansion.
43 + ctx = context.WithValue(ctx, VarsCtxKey, vars)
44 + req = req.WithContext(ctx)
45 +
46 + addHTTPVarsToReplacer(repl, req, httptest.NewRecorder())
47 +
48 + return req, repl
49 +}
50 +
51 +func TestVarsMatcherDoesNotExpandResolvedValues(t *testing.T) {
52 + t.Setenv("CADDY_VARS_TEST_SECRET", "topsecret")
53 +
54 + for _, tc := range []struct {
55 + name string
56 + target string
57 + match VarsMatcher
58 + headers http.Header
59 + vars map[string]any
60 + expect bool
61 + }{
62 + {
63 + name: "literal variable value containing placeholder syntax is not re-expanded",
64 + match: VarsMatcher{"secret": []string{"topsecret"}},
65 + vars: map[string]any{"secret": "{env.CADDY_VARS_TEST_SECRET}"},
66 + expect: false,
67 + },
68 + {
69 + name: "placeholder key value containing placeholder syntax is not re-expanded",
70 + match: VarsMatcher{"{http.request.header.X-Input}": []string{"topsecret"}},
71 + headers: http.Header{"X-Input": []string{"{env.CADDY_VARS_TEST_SECRET}"}},
72 + expect: false,
73 + },
74 + {
75 + name: "query placeholder value containing placeholder syntax is not re-expanded",
76 + target: "https://example.com/test?foo=%7Benv.CADDY_VARS_TEST_SECRET%7D",
77 + match: VarsMatcher{"{http.request.uri.query.foo}": []string{"topsecret"}},
78 + expect: false,
79 + },
80 + {
81 + name: "matcher values still expand placeholders",
82 + match: VarsMatcher{"secret": []string{"{env.CADDY_VARS_TEST_SECRET}"}},
83 + vars: map[string]any{"secret": "topsecret"},
84 + expect: true,
85 + },
86 + } {
87 + t.Run(tc.name, func(t *testing.T) {
88 + t.Parallel()
89 +
90 + req, _ := newVarsTestRequest(t, tc.target, tc.headers, tc.vars)
91 +
92 + actual, err := tc.match.MatchWithError(req)
93 + if err != nil {
94 + t.Fatalf("MatchWithError() error = %v", err)
95 + }
96 +
97 + if actual != tc.expect {
98 + t.Fatalf("MatchWithError() = %t, want %t", actual, tc.expect)
99 + }
100 + })
101 + }
102 +}
103 +
104 +func TestMatchVarsREDoesNotExpandResolvedValues(t *testing.T) {
105 + t.Setenv("CADDY_VARS_TEST_SECRET", "topsecret")
106 +
107 + for _, tc := range []struct {
108 + name string
109 + target string
110 + match MatchVarsRE
111 + headers http.Header
112 + vars map[string]any
113 + expect bool
114 + }{
115 + {
116 + name: "literal variable value containing placeholder syntax is not re-expanded",
117 + match: MatchVarsRE{"secret": &MatchRegexp{Pattern: "^topsecret$"}},
118 + vars: map[string]any{"secret": "{env.CADDY_VARS_TEST_SECRET}"},
119 + expect: false,
120 + },
121 + {
122 + name: "placeholder key value containing placeholder syntax is not re-expanded",
123 + match: MatchVarsRE{"{http.request.header.X-Input}": &MatchRegexp{Pattern: "^topsecret$"}},
124 + headers: http.Header{"X-Input": []string{"{env.CADDY_VARS_TEST_SECRET}"}},
125 + expect: false,
126 + },
127 + {
128 + name: "query placeholder value containing placeholder syntax is not re-expanded",
129 + target: "https://example.com/test?foo=%7Benv.CADDY_VARS_TEST_SECRET%7D",
130 + match: MatchVarsRE{"{http.request.uri.query.foo}": &MatchRegexp{Pattern: "^topsecret$"}},
131 + expect: false,
132 + },
133 + } {
134 + t.Run(tc.name, func(t *testing.T) {
135 + t.Parallel()
136 +
137 + err := tc.match.Provision(caddy.Context{})
138 + if err != nil {
139 + t.Fatalf("Provision() error = %v", err)
140 + }
141 +
142 + err = tc.match.Validate()
143 + if err != nil {
144 + t.Fatalf("Validate() error = %v", err)
145 + }
146 +
147 + req, _ := newVarsTestRequest(t, tc.target, tc.headers, tc.vars)
148 +
149 + actual, err := tc.match.MatchWithError(req)
150 + if err != nil {
151 + t.Fatalf("MatchWithError() error = %v", err)
152 + }
153 +
154 + if actual != tc.expect {
155 + t.Fatalf("MatchWithError() = %t, want %t", actual, tc.expect)
156 + }
157 + })
158 + }
159 +}