mirror of
https://github.com/caddyserver/caddy.git
synced 2026-03-07 09:35:34 -05:00
Merge commit from fork
Only apply repl.ReplaceAll() on values from literal variable names
(e.g. map outputs), not on values resolved from placeholder keys
(e.g. {http.request.header.*}). The placeholder path already resolves
the value via repl.Get(), so a second expansion allows user-controlled
input containing {env.*} or {file.*} to be evaluated, leaking
environment variables and file contents.
Add regression test to verify placeholder-sourced values are not
re-expanded.
This commit is contained in:
parent
2dbcdefbbe
commit
7e83775e3a
@ -967,6 +967,7 @@ func TestVarREMatcher(t *testing.T) {
|
||||
desc string
|
||||
match MatchVarsRE
|
||||
input VarsMiddleware
|
||||
headers http.Header
|
||||
expect bool
|
||||
expectRepl map[string]string
|
||||
}{
|
||||
@ -1001,6 +1002,14 @@ func TestVarREMatcher(t *testing.T) {
|
||||
input: VarsMiddleware{"Var1": "var1Value"},
|
||||
expect: true,
|
||||
},
|
||||
{
|
||||
desc: "placeholder key value containing braces is not double-expanded",
|
||||
match: MatchVarsRE{"{http.request.header.X-Input}": &MatchRegexp{Pattern: ".+", Name: "val"}},
|
||||
input: VarsMiddleware{},
|
||||
headers: http.Header{"X-Input": []string{"{env.HOME}"}},
|
||||
expect: true,
|
||||
expectRepl: map[string]string{"val.0": "{env.HOME}"},
|
||||
},
|
||||
} {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
@ -1017,7 +1026,7 @@ func TestVarREMatcher(t *testing.T) {
|
||||
}
|
||||
|
||||
// set up the fake request and its Replacer
|
||||
req := &http.Request{URL: new(url.URL), Method: http.MethodGet}
|
||||
req := &http.Request{URL: new(url.URL), Method: http.MethodGet, Header: tc.headers}
|
||||
repl := caddy.NewReplacer()
|
||||
ctx := context.WithValue(req.Context(), caddy.ReplacerCtxKey, repl)
|
||||
ctx = context.WithValue(ctx, VarsCtxKey, make(map[string]any))
|
||||
|
||||
@ -312,10 +312,12 @@ func (m MatchVarsRE) MatchWithError(r *http.Request) (bool, error) {
|
||||
repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
|
||||
for key, val := range m {
|
||||
var varValue any
|
||||
var fromPlaceholder bool
|
||||
if strings.HasPrefix(key, "{") &&
|
||||
strings.HasSuffix(key, "}") &&
|
||||
strings.Count(key, "{") == 1 {
|
||||
varValue, _ = repl.Get(strings.Trim(key, "{}"))
|
||||
fromPlaceholder = true
|
||||
} else {
|
||||
varValue = vars[key]
|
||||
}
|
||||
@ -334,7 +336,14 @@ func (m MatchVarsRE) MatchWithError(r *http.Request) (bool, error) {
|
||||
varStr = fmt.Sprintf("%v", vv)
|
||||
}
|
||||
|
||||
valExpanded := repl.ReplaceAll(varStr, "")
|
||||
// Only expand placeholders in values from literal variable names
|
||||
// (e.g. map outputs). Values resolved from placeholder keys are
|
||||
// already final and must not be re-expanded, as that would allow
|
||||
// user input like {env.SECRET} to be evaluated.
|
||||
valExpanded := varStr
|
||||
if !fromPlaceholder {
|
||||
valExpanded = repl.ReplaceAll(varStr, "")
|
||||
}
|
||||
if match := val.Match(valExpanded, repl); match {
|
||||
return match, nil
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user