mirror of
https://github.com/caddyserver/caddy.git
synced 2026-05-13 09:06:41 +00: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
2 changed files with 20 additions and 2 deletions
|
|
@ -967,6 +967,7 @@ func TestVarREMatcher(t *testing.T) {
|
||||||
desc string
|
desc string
|
||||||
match MatchVarsRE
|
match MatchVarsRE
|
||||||
input VarsMiddleware
|
input VarsMiddleware
|
||||||
|
headers http.Header
|
||||||
expect bool
|
expect bool
|
||||||
expectRepl map[string]string
|
expectRepl map[string]string
|
||||||
}{
|
}{
|
||||||
|
|
@ -1001,6 +1002,14 @@ func TestVarREMatcher(t *testing.T) {
|
||||||
input: VarsMiddleware{"Var1": "var1Value"},
|
input: VarsMiddleware{"Var1": "var1Value"},
|
||||||
expect: true,
|
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.Run(tc.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
@ -1017,7 +1026,7 @@ func TestVarREMatcher(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// set up the fake request and its Replacer
|
// 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()
|
repl := caddy.NewReplacer()
|
||||||
ctx := context.WithValue(req.Context(), caddy.ReplacerCtxKey, repl)
|
ctx := context.WithValue(req.Context(), caddy.ReplacerCtxKey, repl)
|
||||||
ctx = context.WithValue(ctx, VarsCtxKey, make(map[string]any))
|
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)
|
repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
|
||||||
for key, val := range m {
|
for key, val := range m {
|
||||||
var varValue any
|
var varValue any
|
||||||
|
var fromPlaceholder bool
|
||||||
if strings.HasPrefix(key, "{") &&
|
if strings.HasPrefix(key, "{") &&
|
||||||
strings.HasSuffix(key, "}") &&
|
strings.HasSuffix(key, "}") &&
|
||||||
strings.Count(key, "{") == 1 {
|
strings.Count(key, "{") == 1 {
|
||||||
varValue, _ = repl.Get(strings.Trim(key, "{}"))
|
varValue, _ = repl.Get(strings.Trim(key, "{}"))
|
||||||
|
fromPlaceholder = true
|
||||||
} else {
|
} else {
|
||||||
varValue = vars[key]
|
varValue = vars[key]
|
||||||
}
|
}
|
||||||
|
|
@ -334,7 +336,14 @@ func (m MatchVarsRE) MatchWithError(r *http.Request) (bool, error) {
|
||||||
varStr = fmt.Sprintf("%v", vv)
|
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 {
|
if match := val.Match(valExpanded, repl); match {
|
||||||
return match, nil
|
return match, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue