Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 14, 05:15 UTC
Disallow unknown fields (strict unmarshal) when loading modules
This makes it faster and easier to detect broken configurations, but is a slight performance hit on config loads since we have to re-encode the decoded struct back into JSON without the module name's key
Matthew Holt committed
May 22, 2019 at 14:32 UTC
f976451d19aea6db42d08ff8cf6e760176f94acb
2 files changed
+29
-7
context.go
+2
-2
@@ -84,7 +84,7 @@ func (ctx Context) LoadModule(name string, rawMsg json.RawMessage) (interface{},
84
85
// fill in its config only if there is a config to fill in
86
if len(rawMsg) > 0 {
87
- err := json.Unmarshal(rawMsg, &val)
87
+ err := strictUnmarshalJSON(rawMsg, &val)
88
if err != nil {
89
return nil, fmt.Errorf("decoding module config: %s: %v", mod.Name, err)
90
}
@@ -127,7 +127,7 @@ func (ctx Context) LoadModule(name string, rawMsg json.RawMessage) (interface{},
127
// containing the module name is treated special/separate from all
128
// the other keys.
129
func (ctx Context) LoadModuleInline(moduleNameKey, moduleScope string, raw json.RawMessage) (interface{}, error) {
130
- moduleName, err := getModuleNameInline(moduleNameKey, raw)
130
+ moduleName, raw, err := getModuleNameInline(moduleNameKey, raw)
131
if err != nil {
132
return nil, err
133
}
modules.go
+27
-5
@@ -1,6 +1,7 @@
1
package caddy2
2
3
import (
4
+ "bytes"
5
"encoding/json"
6
"fmt"
7
"sort"
@@ -120,20 +121,31 @@ func Modules() []string {
121
}
122
123
// getModuleNameInline loads the string value from raw of moduleNameKey,
123
-// where raw must be a JSON encoding of a map.
124
-func getModuleNameInline(moduleNameKey string, raw json.RawMessage) (string, error) {
124
+// where raw must be a JSON encoding of a map. It returns that value,
125
+// along with the result of removing that key from raw.
126
+func getModuleNameInline(moduleNameKey string, raw json.RawMessage) (string, json.RawMessage, error) {
127
var tmp map[string]interface{}
128
err := json.Unmarshal(raw, &tmp)
129
if err != nil {
128
- return "", err
130
+ return "", nil, err
131
}
132
133
moduleName, ok := tmp[moduleNameKey].(string)
134
if !ok || moduleName == "" {
133
- return "", fmt.Errorf("module name not specified with key '%s' in %+v", moduleNameKey, tmp)
135
+ return "", nil, fmt.Errorf("module name not specified with key '%s' in %+v", moduleNameKey, tmp)
136
}
137
136
- return moduleName, nil
138
+ // remove key from the object, otherwise decoding it later
139
+ // will yield an error because the struct won't recognize it
140
+ // (this is only needed because we strictly enforce that
141
+ // all keys are recognized when loading modules)
142
+ delete(tmp, moduleNameKey)
143
+ result, err := json.Marshal(tmp)
144
+ if err != nil {
145
+ return "", nil, fmt.Errorf("re-encoding module configuration: %v", err)
146
+ }
147
+
148
+ return moduleName, result, nil
149
}
150
151
// Provisioner is implemented by modules which may need to perform
@@ -166,6 +178,16 @@ type CleanerUpper interface {
178
Cleanup() error
179
}
180
181
+// strictUnmarshalJSON is like json.Unmarshal but returns an error
182
+// if any of the fields are unrecognized. Useful when decoding
183
+// module configurations, where you want to be more sure they're
184
+// correct.
185
+func strictUnmarshalJSON(data []byte, v interface{}) error {
186
+ dec := json.NewDecoder(bytes.NewReader(data))
187
+ dec.DisallowUnknownFields()
188
+ return dec.Decode(v)
189
+}
190
+
191
var (
192
modules = make(map[string]Module)
193
modulesMu sync.Mutex