@marcel91200 / caddy Mirror · read-only / commits / 73d4a8ba
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 13, 14:15 UTC

map: Coerce val to string, fix #4987

Also prevent infinite recursion, and enforce placeholder syntax.

Matthew Holt committed Sep 1, 2022 at 21:15 UTC 73d4a8ba02292491fca289b324e89ef8c62fd435
5 files changed +38 -35
caddyconfig/caddyfile/dispenser.go
+4 -4
@@ -146,15 +146,15 @@ func (d *Dispenser) NextLine() bool {
146 //
147 // Proper use of this method looks like this:
148 //
149 -// for nesting := d.Nesting(); d.NextBlock(nesting); {
150 -// }
149 +// for nesting := d.Nesting(); d.NextBlock(nesting); {
150 +// }
151 //
152 // However, in simple cases where it is known that the
153 // Dispenser is new and has not already traversed state
154 // by a loop over NextBlock(), this will do:
155 //
156 -// for d.NextBlock(0) {
157 -// }
156 +// for d.NextBlock(0) {
157 +// }
158 //
159 // As with other token parsing logic, a loop over
160 // NextBlock() should be contained within a loop over
modules/caddyhttp/map/caddyfile.go
+5 -5
@@ -27,10 +27,10 @@ func init() {
27
28 // parseCaddyfile sets up the map handler from Caddyfile tokens. Syntax:
29 //
30 -// map [<matcher>] <source> <destinations...> {
31 -// [~]<input> <outputs...>
32 -// default <defaults...>
33 -// }
30 +// map [<matcher>] <source> <destinations...> {
31 +// [~]<input> <outputs...>
32 +// default <defaults...>
33 +// }
34 //
35 // If the input value is prefixed with a tilde (~), then the input will be parsed as a
36 // regular expression.
@@ -76,7 +76,7 @@ func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error)
76 continue
77 }
78
79 - // every other line maps one input to one or more outputs
79 + // every line maps an input value to one or more outputs
80 in := h.Val()
81 var outs []any
82 for h.NextArg() {
modules/caddyhttp/map/map.go
+20 -6
@@ -62,6 +62,9 @@ func (Handler) CaddyModule() caddy.ModuleInfo {
62 // Provision sets up h.
63 func (h *Handler) Provision(_ caddy.Context) error {
64 for j, dest := range h.Destinations {
65 + if strings.Count(dest, "{") != 1 || !strings.HasPrefix(dest, "{") {
66 + return fmt.Errorf("destination must be a placeholder and only a placeholder")
67 + }
68 h.Destinations[j] = strings.Trim(dest, "{}")
69 }
70
@@ -106,6 +109,16 @@ func (h *Handler) Validate() error {
109 }
110 seen[input] = i
111
112 + // prevent infinite recursion
113 + for _, out := range m.Outputs {
114 + for _, dest := range h.Destinations {
115 + if strings.Contains(caddy.ToString(out), dest) ||
116 + strings.Contains(m.Input, dest) {
117 + return fmt.Errorf("mapping %d requires value of {%s} to define value of {%s}: infinite recursion", i, dest, dest)
118 + }
119 + }
120 + }
121 +
122 // ensure mappings have 1:1 output-to-destination correspondence
123 nOut := len(m.Outputs)
124 if nOut != nDest {
@@ -135,21 +148,22 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhtt
148 if output == nil {
149 continue
150 }
151 + outputStr := caddy.ToString(output)
152 +
153 + // evaluate regular expression if configured
154 if m.re != nil {
155 var result []byte
156 matches := m.re.FindStringSubmatchIndex(input)
157 if matches == nil {
158 continue
159 }
144 - result = m.re.ExpandString(result, output.(string), input, matches)
160 + result = m.re.ExpandString(result, outputStr, input, matches)
161 return string(result), true
162 }
163 +
164 + // otherwise simple string comparison
165 if input == m.Input {
148 - if outputStr, ok := output.(string); ok {
149 - // NOTE: if the output has a placeholder that has the same key as the input, this is infinite recursion
150 - return repl.ReplaceAll(outputStr, ""), true
151 - }
152 - return output, true
166 + return repl.ReplaceAll(outputStr, ""), true
167 }
168 }
169
modules/caddyhttp/templates/tplcontext.go
+2 -15
@@ -305,7 +305,7 @@ func (TemplateContext) funcStripHTML(s string) string {
305 // funcMarkdown renders the markdown body as HTML. The resulting
306 // HTML is NOT escaped so that it can be rendered as HTML.
307 func (TemplateContext) funcMarkdown(input any) (string, error) {
308 - inputStr := toString(input)
308 + inputStr := caddy.ToString(input)
309
310 md := goldmark.New(
311 goldmark.WithExtensions(
@@ -341,7 +341,7 @@ func (TemplateContext) funcMarkdown(input any) (string, error) {
341 // and returns the separated key-value pairs and the body/content. input
342 // must be a "stringy" value.
343 func (TemplateContext) funcSplitFrontMatter(input any) (parsedMarkdownDoc, error) {
344 - meta, body, err := extractFrontMatter(toString(input))
344 + meta, body, err := extractFrontMatter(caddy.ToString(input))
345 if err != nil {
346 return parsedMarkdownDoc{}, err
347 }
@@ -465,19 +465,6 @@ func (h WrappedHeader) Del(field string) string {
465 return ""
466 }
467
468 -func toString(input any) string {
469 - switch v := input.(type) {
470 - case string:
471 - return v
472 - case fmt.Stringer:
473 - return v.String()
474 - case error:
475 - return v.Error()
476 - default:
477 - return fmt.Sprintf("%v", input)
478 - }
479 -}
480 -
468 var bufPool = sync.Pool{
469 New: func() any {
470 return new(bytes.Buffer)
replacer.go
+7 -5
@@ -78,11 +78,11 @@ func (r *Replacer) Get(variable string) (any, bool) {
78 return nil, false
79 }
80
81 -// GetString is the same as Get, but coerces the value to a
82 -// string representation.
81 +// GetString is the same as Get, but coerces the value to a
82 +// string representation as efficiently as possible.
83 func (r *Replacer) GetString(variable string) (string, bool) {
84 s, found := r.Get(variable)
85 - return toString(s), found
85 + return ToString(s), found
86 }
87
88 // Delete removes a variable with a static value
@@ -204,7 +204,7 @@ scan:
204 }
205
206 // convert val to a string as efficiently as possible
207 - valStr := toString(val)
207 + valStr := ToString(val)
208
209 // write the value; if it's empty, either return
210 // an error or write a default value
@@ -230,7 +230,9 @@ scan:
230 return sb.String(), nil
231 }
232
233 -func toString(val any) string {
233 +// ToString returns val as a string, as efficiently as possible.
234 +// EXPERIMENTAL: may be changed or removed later.
235 +func ToString(val any) string {
236 switch v := val.(type) {
237 case nil:
238 return ""