package config import ( "encoding/json" "errors" "fmt" ) // encoding/json accepts repeated object keys. Inspect the token stream before // typed decoding so deployment settings cannot silently override earlier values. func uniqueJSONKeys(decoder *json.Decoder, depth int) error { if depth > 16 { return errors.New("configuration nesting is too deep") } token, err := decoder.Token() if err != nil { return err } delimiter, compound := token.(json.Delim) if !compound { return nil } switch delimiter { case '{': seen := make(map[string]bool) for decoder.More() { keyToken, err := decoder.Token() if err != nil { return err } key, ok := keyToken.(string) if !ok { return errors.New("configuration object key must be a string") } if seen[key] { return fmt.Errorf("duplicate configuration field %q", key) } seen[key] = true if err := uniqueJSONKeys(decoder, depth+1); err != nil { return err } } case '[': for decoder.More() { if err := uniqueJSONKeys(decoder, depth+1); err != nil { return err } } default: return errors.New("unexpected configuration JSON delimiter") } _, err = decoder.Token() return err }