@marcel91200 / caddy Mirror · read-only / commits / f976451d
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 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