@marcel91200 / caddy Mirror · read-only / commits / 2cac3c54
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 12, 22:00 UTC

httpcaddyfile: fix placeholder shorthands in named routes (#5791)

Co-authored-by: Francis Lavoie <lavofr@gmail.com>

Đỗ Trọng Hải committed Sep 9, 2023 at 01:38 UTC 2cac3c5491e6428441ecf668cc4f5a86e67ed9b3
3 files changed +109 -71
.github/workflows/lint.yml
+2
@@ -56,3 +56,5 @@ jobs:
56 steps:
57 - name: govulncheck
58 uses: golang/govulncheck-action@v1
59 + with:
60 + check-latest: true
caddyconfig/httpcaddyfile/httptype.go
+15 -71
@@ -18,7 +18,6 @@ import (
18 "encoding/json"
19 "fmt"
20 "reflect"
21 - "regexp"
21 "sort"
22 "strconv"
23 "strings"
@@ -82,46 +81,18 @@ func (st ServerType) Setup(
81 return nil, warnings, err
82 }
83
85 - originalServerBlocks, err = st.extractNamedRoutes(originalServerBlocks, options, &warnings)
84 + // this will replace both static and user-defined placeholder shorthands
85 + // with actual identifiers used by Caddy
86 + replacer := NewShorthandReplacer()
87 +
88 + originalServerBlocks, err = st.extractNamedRoutes(originalServerBlocks, options, &warnings, replacer)
89 if err != nil {
90 return nil, warnings, err
91 }
92
90 - // replace shorthand placeholders (which are convenient
91 - // when writing a Caddyfile) with their actual placeholder
92 - // identifiers or variable names
93 - replacer := strings.NewReplacer(placeholderShorthands()...)
94 -
95 - // these are placeholders that allow a user-defined final
96 - // parameters, but we still want to provide a shorthand
97 - // for those, so we use a regexp to replace
98 - regexpReplacements := []struct {
99 - search *regexp.Regexp
100 - replace string
101 - }{
102 - {regexp.MustCompile(`{header\.([\w-]*)}`), "{http.request.header.$1}"},
103 - {regexp.MustCompile(`{cookie\.([\w-]*)}`), "{http.request.cookie.$1}"},
104 - {regexp.MustCompile(`{labels\.([\w-]*)}`), "{http.request.host.labels.$1}"},
105 - {regexp.MustCompile(`{path\.([\w-]*)}`), "{http.request.uri.path.$1}"},
106 - {regexp.MustCompile(`{file\.([\w-]*)}`), "{http.request.uri.path.file.$1}"},
107 - {regexp.MustCompile(`{query\.([\w-]*)}`), "{http.request.uri.query.$1}"},
108 - {regexp.MustCompile(`{re\.([\w-]*)\.([\w-]*)}`), "{http.regexp.$1.$2}"},
109 - {regexp.MustCompile(`{vars\.([\w-]*)}`), "{http.vars.$1}"},
110 - {regexp.MustCompile(`{rp\.([\w-\.]*)}`), "{http.reverse_proxy.$1}"},
111 - {regexp.MustCompile(`{err\.([\w-\.]*)}`), "{http.error.$1}"},
112 - {regexp.MustCompile(`{file_match\.([\w-]*)}`), "{http.matchers.file.$1}"},
113 - }
114 -
93 for _, sb := range originalServerBlocks {
116 - for _, segment := range sb.block.Segments {
117 - for i := 0; i < len(segment); i++ {
118 - // simple string replacements
119 - segment[i].Text = replacer.Replace(segment[i].Text)
120 - // complex regexp replacements
121 - for _, r := range regexpReplacements {
122 - segment[i].Text = r.search.ReplaceAllString(segment[i].Text, r.replace)
123 - }
124 - }
94 + for i := range sb.block.Segments {
95 + replacer.ApplyToSegment(&sb.block.Segments[i])
96 }
97
98 if len(sb.block.Keys) == 0 {
@@ -452,6 +423,7 @@ func (ServerType) extractNamedRoutes(
423 serverBlocks []serverBlock,
424 options map[string]any,
425 warnings *[]caddyconfig.Warning,
426 + replacer ShorthandReplacer,
427 ) ([]serverBlock, error) {
428 namedRoutes := map[string]*caddyhttp.Route{}
429
@@ -477,11 +449,14 @@ func (ServerType) extractNamedRoutes(
449 continue
450 }
451
480 - // zip up all the segments since ParseSegmentAsSubroute
481 - // was designed to take a directive+
452 wholeSegment := caddyfile.Segment{}
483 - for _, segment := range sb.block.Segments {
484 - wholeSegment = append(wholeSegment, segment...)
453 + for i := range sb.block.Segments {
454 + // replace user-defined placeholder shorthands in extracted named routes
455 + replacer.ApplyToSegment(&sb.block.Segments[i])
456 +
457 + // zip up all the segments since ParseSegmentAsSubroute
458 + // was designed to take a directive+
459 + wholeSegment = append(wholeSegment, sb.block.Segments[i]...)
460 }
461
462 h := Helper{
@@ -1449,37 +1424,6 @@ func encodeMatcherSet(matchers map[string]caddyhttp.RequestMatcher) (caddy.Modul
1424 return msEncoded, nil
1425 }
1426
1452 -// placeholderShorthands returns a slice of old-new string pairs,
1453 -// where the left of the pair is a placeholder shorthand that may
1454 -// be used in the Caddyfile, and the right is the replacement.
1455 -func placeholderShorthands() []string {
1456 - return []string{
1457 - "{dir}", "{http.request.uri.path.dir}",
1458 - "{file}", "{http.request.uri.path.file}",
1459 - "{host}", "{http.request.host}",
1460 - "{hostport}", "{http.request.hostport}",
1461 - "{port}", "{http.request.port}",
1462 - "{method}", "{http.request.method}",
1463 - "{path}", "{http.request.uri.path}",
1464 - "{query}", "{http.request.uri.query}",
1465 - "{remote}", "{http.request.remote}",
1466 - "{remote_host}", "{http.request.remote.host}",
1467 - "{remote_port}", "{http.request.remote.port}",
1468 - "{scheme}", "{http.request.scheme}",
1469 - "{uri}", "{http.request.uri}",
1470 - "{tls_cipher}", "{http.request.tls.cipher_suite}",
1471 - "{tls_version}", "{http.request.tls.version}",
1472 - "{tls_client_fingerprint}", "{http.request.tls.client.fingerprint}",
1473 - "{tls_client_issuer}", "{http.request.tls.client.issuer}",
1474 - "{tls_client_serial}", "{http.request.tls.client.serial}",
1475 - "{tls_client_subject}", "{http.request.tls.client.subject}",
1476 - "{tls_client_certificate_pem}", "{http.request.tls.client.certificate_pem}",
1477 - "{tls_client_certificate_der_base64}", "{http.request.tls.client.certificate_der_base64}",
1478 - "{upstream_hostport}", "{http.reverse_proxy.upstream.hostport}",
1479 - "{client_ip}", "{http.vars.client_ip}",
1480 - }
1481 -}
1482 -
1427 // WasReplacedPlaceholderShorthand checks if a token string was
1428 // likely a replaced shorthand of the known Caddyfile placeholder
1429 // replacement outputs. Useful to prevent some user-defined map
caddyconfig/httpcaddyfile/shorthands.go new
+92
@@ -0,0 +1,92 @@
1 +package httpcaddyfile
2 +
3 +import (
4 + "regexp"
5 + "strings"
6 +
7 + "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
8 +)
9 +
10 +type ComplexShorthandReplacer struct {
11 + search *regexp.Regexp
12 + replace string
13 +}
14 +
15 +type ShorthandReplacer struct {
16 + complex []ComplexShorthandReplacer
17 + simple *strings.Replacer
18 +}
19 +
20 +func NewShorthandReplacer() ShorthandReplacer {
21 + // replace shorthand placeholders (which are convenient
22 + // when writing a Caddyfile) with their actual placeholder
23 + // identifiers or variable names
24 + replacer := strings.NewReplacer(placeholderShorthands()...)
25 +
26 + // these are placeholders that allow a user-defined final
27 + // parameters, but we still want to provide a shorthand
28 + // for those, so we use a regexp to replace
29 + regexpReplacements := []ComplexShorthandReplacer{
30 + {regexp.MustCompile(`{header\.([\w-]*)}`), "{http.request.header.$1}"},
31 + {regexp.MustCompile(`{cookie\.([\w-]*)}`), "{http.request.cookie.$1}"},
32 + {regexp.MustCompile(`{labels\.([\w-]*)}`), "{http.request.host.labels.$1}"},
33 + {regexp.MustCompile(`{path\.([\w-]*)}`), "{http.request.uri.path.$1}"},
34 + {regexp.MustCompile(`{file\.([\w-]*)}`), "{http.request.uri.path.file.$1}"},
35 + {regexp.MustCompile(`{query\.([\w-]*)}`), "{http.request.uri.query.$1}"},
36 + {regexp.MustCompile(`{re\.([\w-]*)\.([\w-]*)}`), "{http.regexp.$1.$2}"},
37 + {regexp.MustCompile(`{vars\.([\w-]*)}`), "{http.vars.$1}"},
38 + {regexp.MustCompile(`{rp\.([\w-\.]*)}`), "{http.reverse_proxy.$1}"},
39 + {regexp.MustCompile(`{err\.([\w-\.]*)}`), "{http.error.$1}"},
40 + {regexp.MustCompile(`{file_match\.([\w-]*)}`), "{http.matchers.file.$1}"},
41 + }
42 +
43 + return ShorthandReplacer{
44 + complex: regexpReplacements,
45 + simple: replacer,
46 + }
47 +}
48 +
49 +// placeholderShorthands returns a slice of old-new string pairs,
50 +// where the left of the pair is a placeholder shorthand that may
51 +// be used in the Caddyfile, and the right is the replacement.
52 +func placeholderShorthands() []string {
53 + return []string{
54 + "{dir}", "{http.request.uri.path.dir}",
55 + "{file}", "{http.request.uri.path.file}",
56 + "{host}", "{http.request.host}",
57 + "{hostport}", "{http.request.hostport}",
58 + "{port}", "{http.request.port}",
59 + "{method}", "{http.request.method}",
60 + "{path}", "{http.request.uri.path}",
61 + "{query}", "{http.request.uri.query}",
62 + "{remote}", "{http.request.remote}",
63 + "{remote_host}", "{http.request.remote.host}",
64 + "{remote_port}", "{http.request.remote.port}",
65 + "{scheme}", "{http.request.scheme}",
66 + "{uri}", "{http.request.uri}",
67 + "{tls_cipher}", "{http.request.tls.cipher_suite}",
68 + "{tls_version}", "{http.request.tls.version}",
69 + "{tls_client_fingerprint}", "{http.request.tls.client.fingerprint}",
70 + "{tls_client_issuer}", "{http.request.tls.client.issuer}",
71 + "{tls_client_serial}", "{http.request.tls.client.serial}",
72 + "{tls_client_subject}", "{http.request.tls.client.subject}",
73 + "{tls_client_certificate_pem}", "{http.request.tls.client.certificate_pem}",
74 + "{tls_client_certificate_der_base64}", "{http.request.tls.client.certificate_der_base64}",
75 + "{upstream_hostport}", "{http.reverse_proxy.upstream.hostport}",
76 + "{client_ip}", "{http.vars.client_ip}",
77 + }
78 +}
79 +
80 +// ApplyToSegment replaces shorthand placeholder to its full placeholder, understandable by Caddy.
81 +func (s ShorthandReplacer) ApplyToSegment(segment *caddyfile.Segment) {
82 + if segment != nil {
83 + for i := 0; i < len(*segment); i++ {
84 + // simple string replacements
85 + (*segment)[i].Text = s.simple.Replace((*segment)[i].Text)
86 + // complex regexp replacements
87 + for _, r := range s.complex {
88 + (*segment)[i].Text = r.search.ReplaceAllString((*segment)[i].Text, r.replace)
89 + }
90 + }
91 + }
92 +}