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
+}