diff --git a/modules/caddyhttp/vars_test.go b/modules/caddyhttp/vars_test.go new file mode 100644 index 000000000..7cbe583e5 --- /dev/null +++ b/modules/caddyhttp/vars_test.go @@ -0,0 +1,159 @@ +// Copyright 2015 Matthew Holt and The Caddy Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package caddyhttp + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" + + "github.com/caddyserver/caddy/v2" +) + +func newVarsTestRequest(t *testing.T, target string, headers http.Header, vars map[string]any) (*http.Request, *caddy.Replacer) { + t.Helper() + + if target == "" { + target = "https://example.com/test" + } + + req := httptest.NewRequest(http.MethodGet, target, nil) + req.Header = headers + + repl := caddy.NewReplacer() + ctx := context.WithValue(req.Context(), caddy.ReplacerCtxKey, repl) + if vars == nil { + vars = make(map[string]any) + } + // Inject vars directly so these tests exercise matcher-side handling of + // already-resolved values, not VarsMiddleware placeholder expansion. + ctx = context.WithValue(ctx, VarsCtxKey, vars) + req = req.WithContext(ctx) + + addHTTPVarsToReplacer(repl, req, httptest.NewRecorder()) + + return req, repl +} + +func TestVarsMatcherDoesNotExpandResolvedValues(t *testing.T) { + t.Setenv("CADDY_VARS_TEST_SECRET", "topsecret") + + for _, tc := range []struct { + name string + target string + match VarsMatcher + headers http.Header + vars map[string]any + expect bool + }{ + { + name: "literal variable value containing placeholder syntax is not re-expanded", + match: VarsMatcher{"secret": []string{"topsecret"}}, + vars: map[string]any{"secret": "{env.CADDY_VARS_TEST_SECRET}"}, + expect: false, + }, + { + name: "placeholder key value containing placeholder syntax is not re-expanded", + match: VarsMatcher{"{http.request.header.X-Input}": []string{"topsecret"}}, + headers: http.Header{"X-Input": []string{"{env.CADDY_VARS_TEST_SECRET}"}}, + expect: false, + }, + { + name: "query placeholder value containing placeholder syntax is not re-expanded", + target: "https://example.com/test?foo=%7Benv.CADDY_VARS_TEST_SECRET%7D", + match: VarsMatcher{"{http.request.uri.query.foo}": []string{"topsecret"}}, + expect: false, + }, + { + name: "matcher values still expand placeholders", + match: VarsMatcher{"secret": []string{"{env.CADDY_VARS_TEST_SECRET}"}}, + vars: map[string]any{"secret": "topsecret"}, + expect: true, + }, + } { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + req, _ := newVarsTestRequest(t, tc.target, tc.headers, tc.vars) + + actual, err := tc.match.MatchWithError(req) + if err != nil { + t.Fatalf("MatchWithError() error = %v", err) + } + + if actual != tc.expect { + t.Fatalf("MatchWithError() = %t, want %t", actual, tc.expect) + } + }) + } +} + +func TestMatchVarsREDoesNotExpandResolvedValues(t *testing.T) { + t.Setenv("CADDY_VARS_TEST_SECRET", "topsecret") + + for _, tc := range []struct { + name string + target string + match MatchVarsRE + headers http.Header + vars map[string]any + expect bool + }{ + { + name: "literal variable value containing placeholder syntax is not re-expanded", + match: MatchVarsRE{"secret": &MatchRegexp{Pattern: "^topsecret$"}}, + vars: map[string]any{"secret": "{env.CADDY_VARS_TEST_SECRET}"}, + expect: false, + }, + { + name: "placeholder key value containing placeholder syntax is not re-expanded", + match: MatchVarsRE{"{http.request.header.X-Input}": &MatchRegexp{Pattern: "^topsecret$"}}, + headers: http.Header{"X-Input": []string{"{env.CADDY_VARS_TEST_SECRET}"}}, + expect: false, + }, + { + name: "query placeholder value containing placeholder syntax is not re-expanded", + target: "https://example.com/test?foo=%7Benv.CADDY_VARS_TEST_SECRET%7D", + match: MatchVarsRE{"{http.request.uri.query.foo}": &MatchRegexp{Pattern: "^topsecret$"}}, + expect: false, + }, + } { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + err := tc.match.Provision(caddy.Context{}) + if err != nil { + t.Fatalf("Provision() error = %v", err) + } + + err = tc.match.Validate() + if err != nil { + t.Fatalf("Validate() error = %v", err) + } + + req, _ := newVarsTestRequest(t, tc.target, tc.headers, tc.vars) + + actual, err := tc.match.MatchWithError(req) + if err != nil { + t.Fatalf("MatchWithError() error = %v", err) + } + + if actual != tc.expect { + t.Fatalf("MatchWithError() = %t, want %t", actual, tc.expect) + } + }) + } +}