From ca3f793eb1ecce7738f25c01a8a611d7c52e0268 Mon Sep 17 00:00:00 2001 From: andig Date: Sun, 31 Oct 2021 10:58:03 +0100 Subject: [PATCH] Add basic form of log redaction for user names and passwords (#1810) --- charger/easee.go | 2 +- charger/go-e.go | 2 +- meter/discovergy.go | 3 ++- meter/fritzdect.go | 1 + meter/sma.go | 2 +- meter/tesla.go | 2 +- util/fritzdect/fritzdect.go | 2 +- util/log.go | 18 ++++++++++++---- util/redactor.go | 42 +++++++++++++++++++++++++++++++++++++ vehicle/audi.go | 2 +- vehicle/bmw.go | 2 +- vehicle/carwings.go | 12 +++++------ vehicle/enyaq.go | 2 +- vehicle/fiat.go | 2 +- vehicle/ford.go | 2 +- vehicle/hyundai.go | 3 ++- vehicle/id.go | 2 +- vehicle/kia.go | 3 ++- vehicle/nissan.go | 2 +- vehicle/niu.go | 2 +- vehicle/ovms.go | 2 +- vehicle/porsche.go | 3 +-- vehicle/psa.go | 1 + vehicle/renault.go | 2 +- vehicle/seat.go | 2 +- vehicle/skoda.go | 2 +- vehicle/tesla.go | 2 +- vehicle/tronity.go | 2 +- vehicle/volvo.go | 2 +- vehicle/vw.go | 2 +- 30 files changed, 92 insertions(+), 36 deletions(-) create mode 100644 util/redactor.go diff --git a/charger/easee.go b/charger/easee.go index d286406f1..fb757de75 100644 --- a/charger/easee.go +++ b/charger/easee.go @@ -75,7 +75,7 @@ func NewEaseeFromConfig(other map[string]interface{}) (api.Charger, error) { // NewEasee creates Easee charger func NewEasee(user, password, charger string, circuit int, cache time.Duration) (*Easee, error) { - log := util.NewLogger("easee") + log := util.NewLogger("easee").Redact(user, password) if !sponsor.IsAuthorized() { return nil, api.ErrSponsorRequired diff --git a/charger/go-e.go b/charger/go-e.go index e9cebea23..8fbe1065f 100644 --- a/charger/go-e.go +++ b/charger/go-e.go @@ -69,7 +69,7 @@ func NewGoEFromConfig(other map[string]interface{}) (api.Charger, error) { func NewGoE(uri, token string, cache time.Duration) (api.Charger, error) { c := &GoE{} - log := util.NewLogger("go-e") + log := util.NewLogger("go-e").Redact(token) if token != "" { c.api = goe.NewCloud(log, token, cache) diff --git a/meter/discovergy.go b/meter/discovergy.go index 52417eead..daa428c66 100644 --- a/meter/discovergy.go +++ b/meter/discovergy.go @@ -39,7 +39,8 @@ func NewDiscovergyFromConfig(other map[string]interface{}) (api.Meter, error) { return nil, err } - log := util.NewLogger("discgy") + log := util.NewLogger("discgy").Redact(cc.User, cc.Password, cc.Meter) + client := request.NewHelper(log) client.Transport = basicauth.NewTransport(cc.User, cc.Password, client.Transport) diff --git a/meter/fritzdect.go b/meter/fritzdect.go index afc0ef997..b700f3217 100644 --- a/meter/fritzdect.go +++ b/meter/fritzdect.go @@ -19,5 +19,6 @@ func NewFritzDECTFromConfig(other map[string]interface{}) (api.Meter, error) { if err := util.DecodeOther(other, &cc); err != nil { return nil, err } + return fritzdect.NewConnection(cc.URI, cc.AIN, cc.User, cc.Password) } diff --git a/meter/sma.go b/meter/sma.go index a0c16bcbf..98b59c18f 100644 --- a/meter/sma.go +++ b/meter/sma.go @@ -48,7 +48,7 @@ func NewSMAFromConfig(other map[string]interface{}) (api.Meter, error) { // NewSMA creates a SMA Meter func NewSMA(uri, password, iface string, serial uint32, scale float64) (api.Meter, error) { sm := &SMA{ - log: util.NewLogger("sma"), + log: util.NewLogger("sma").Redact(password), uri: uri, scale: scale, } diff --git a/meter/tesla.go b/meter/tesla.go index e3d5e6ee1..2f93c5bfa 100644 --- a/meter/tesla.go +++ b/meter/tesla.go @@ -64,7 +64,7 @@ func NewTeslaFromConfig(other map[string]interface{}) (api.Meter, error) { // NewTesla creates a Tesla Meter func NewTesla(uri, usage, password string) (api.Meter, error) { - log := util.NewLogger("tesla") + log := util.NewLogger("tesla").Redact(password) m := &Tesla{ Helper: request.NewHelper(log), diff --git a/util/fritzdect/fritzdect.go b/util/fritzdect/fritzdect.go index a44c391e3..12401432c 100644 --- a/util/fritzdect/fritzdect.go +++ b/util/fritzdect/fritzdect.go @@ -63,7 +63,7 @@ func NewConnection(uri, ain, user, password string) (*Connection, error) { Password: password, } - log := util.NewLogger("fritzdect") + log := util.NewLogger("fritzdect").Redact(password) fritzdect := &Connection{ Helper: request.NewHelper(log), diff --git a/util/log.go b/util/log.go index ffe5b5462..9bc47021a 100644 --- a/util/log.go +++ b/util/log.go @@ -3,7 +3,6 @@ package util import ( "io" "log" - "os" "regexp" "strconv" "strings" @@ -32,6 +31,7 @@ var LogAreaPadding = 6 type Logger struct { *jww.Notepad name string + *Redactor } // NewLogger creates a logger with the given log area and adds it to the registry @@ -49,13 +49,17 @@ func NewLogger(area string) *Logger { } level := LogLevelForArea(area) - notepad := jww.NewNotepad(level, level, os.Stdout, io.Discard, padded, log.Ldate|log.Ltime) + redactor := new(Redactor) + notepad := jww.NewNotepad(level, level, redactor, io.Discard, padded, log.Ldate|log.Ltime) logger := &Logger{ - Notepad: notepad, - name: area, + Notepad: notepad, + Redactor: redactor, + name: area, } + loggers[area] = logger + return logger } @@ -64,6 +68,12 @@ func (l *Logger) Name() string { return l.name } +// Redact adds items for redaction +func (l *Logger) Redact(items ...string) *Logger { + l.Redactor.Redact(items...) + return l +} + // Loggers invokes callback for each configured logger func Loggers(cb func(string, *Logger)) { for name, logger := range loggers { diff --git a/util/redactor.go b/util/redactor.go new file mode 100644 index 000000000..b4e8e3d4e --- /dev/null +++ b/util/redactor.go @@ -0,0 +1,42 @@ +package util + +import ( + "bytes" + "net/url" + "os" +) + +var ( + // RedactReplacement is the default replacement string + RedactReplacement = "***" + + // RedactHook is the hook for expanding different representations of + // redaction items. Setting to nil will disable redaction. + RedactHook = RedactDefaultHook +) + +// Redactor implements a redacting io.Writer +type Redactor struct { + redact []string +} + +// Redact adds items for redaction +func (l *Redactor) Redact(redact ...string) { + for _, s := range redact { + if RedactHook != nil && len(s) > 0 { + l.redact = append(l.redact, RedactHook(s)...) + } + } +} + +func (l *Redactor) Write(p []byte) (n int, err error) { + for _, s := range l.redact { + p = bytes.ReplaceAll(p, []byte(s), []byte(RedactReplacement)) + } + return os.Stdout.Write(p) +} + +// RedactDefaultHook expands a redaction item to include URL encoding +func RedactDefaultHook(s string) []string { + return []string{s, url.QueryEscape(s)} +} diff --git a/vehicle/audi.go b/vehicle/audi.go index 591f2fb1c..2fb7ee948 100644 --- a/vehicle/audi.go +++ b/vehicle/audi.go @@ -46,7 +46,7 @@ func NewAudiFromConfig(other map[string]interface{}) (api.Vehicle, error) { embed: &cc.embed, } - log := util.NewLogger("audi") + log := util.NewLogger("audi").Redact(cc.User, cc.Password, cc.VIN) identity := vw.NewIdentity(log) query := url.Values(map[string][]string{ diff --git a/vehicle/bmw.go b/vehicle/bmw.go index 53628c992..608d33ed1 100644 --- a/vehicle/bmw.go +++ b/vehicle/bmw.go @@ -38,7 +38,7 @@ func NewBMWFromConfig(other map[string]interface{}) (api.Vehicle, error) { embed: &cc.embed, } - log := util.NewLogger("bmw") + log := util.NewLogger("bmw").Redact(cc.User, cc.Password, cc.VIN) identity := bmw.NewIdentity(log) if err := identity.Login(cc.User, cc.Password); err != nil { diff --git a/vehicle/carwings.go b/vehicle/carwings.go index 58126f542..375309e01 100644 --- a/vehicle/carwings.go +++ b/vehicle/carwings.go @@ -16,6 +16,7 @@ import ( ) const ( + carwingsRequestTimeout = 90 * time.Second carwingsStatusExpiry = 5 * time.Minute // if returned status value is older, evcc will init refresh carwingsRefreshTimeout = 2 * time.Minute // timeout to get status after refresh ) @@ -54,17 +55,16 @@ func NewCarWingsFromConfig(other map[string]interface{}) (api.Vehicle, error) { return nil, errors.New("missing credentials") } - // http client with high dial/handshake timeout - const timeout = 90 * time.Second - log := util.NewLogger("carwings") + log := util.NewLogger("carwings").Redact(cc.User, cc.Password, cc.VIN) + // http client with high dial/handshake timeout transport := request.NewTripper(log, &http.Transport{ Proxy: http.ProxyFromEnvironment, // default DialContext: (&net.Dialer{ - Timeout: timeout, + Timeout: carwingsRequestTimeout, KeepAlive: 30 * time.Second, // default }).DialContext, - TLSHandshakeTimeout: timeout, + TLSHandshakeTimeout: carwingsRequestTimeout, ForceAttemptHTTP2: true, // default MaxIdleConns: 100, // default IdleConnTimeout: 90 * time.Second, // default @@ -72,7 +72,7 @@ func NewCarWingsFromConfig(other map[string]interface{}) (api.Vehicle, error) { }) carwings.Client = &http.Client{ - Timeout: timeout, + Timeout: carwingsRequestTimeout, Transport: transport, } diff --git a/vehicle/enyaq.go b/vehicle/enyaq.go index 435e40e4c..91031cf2b 100644 --- a/vehicle/enyaq.go +++ b/vehicle/enyaq.go @@ -46,7 +46,7 @@ func NewEnyaqFromConfig(other map[string]interface{}) (api.Vehicle, error) { } var err error - log := util.NewLogger("enyaq") + log := util.NewLogger("enyaq").Redact(cc.User, cc.Password, cc.VIN) if cc.VIN == "" { identity := vw.NewIdentity(log) diff --git a/vehicle/fiat.go b/vehicle/fiat.go index 2a5b7c26a..f11c0b495 100644 --- a/vehicle/fiat.go +++ b/vehicle/fiat.go @@ -47,7 +47,7 @@ func NewFiatFromConfig(other map[string]interface{}) (api.Vehicle, error) { embed: &cc.embed, } - log := util.NewLogger("fiat") + log := util.NewLogger("fiat").Redact(cc.User, cc.Password, cc.VIN) identity := fiat.NewIdentity(log, cc.User, cc.Password) err := identity.Login() diff --git a/vehicle/ford.go b/vehicle/ford.go index bf38f7049..e35afa0c8 100644 --- a/vehicle/ford.go +++ b/vehicle/ford.go @@ -59,7 +59,7 @@ func NewFordFromConfig(other map[string]interface{}) (api.Vehicle, error) { return nil, errors.New("missing credentials") } - log := util.NewLogger("ford") + log := util.NewLogger("ford").Redact(cc.User, cc.Password, cc.VIN) v := &Ford{ embed: &cc.embed, diff --git a/vehicle/hyundai.go b/vehicle/hyundai.go index c98362344..596700952 100644 --- a/vehicle/hyundai.go +++ b/vehicle/hyundai.go @@ -40,7 +40,8 @@ func NewHyundaiFromConfig(other map[string]interface{}) (api.Vehicle, error) { return nil, errors.New("missing credentials") } - log := util.NewLogger("hyundai") + log := util.NewLogger("hyundai").Redact(cc.User, cc.Password, cc.VIN) + settings := bluelink.Config{ URI: "https://prd.eu-ccapi.hyundai.com:8080", BasicToken: "NmQ0NzdjMzgtM2NhNC00Y2YzLTk1NTctMmExOTI5YTk0NjU0OktVeTQ5WHhQekxwTHVvSzB4aEJDNzdXNlZYaG10UVI5aVFobUlGampvWTRJcHhzVg==", diff --git a/vehicle/id.go b/vehicle/id.go index 06f302ca9..cbad62027 100644 --- a/vehicle/id.go +++ b/vehicle/id.go @@ -45,7 +45,7 @@ func NewIDFromConfig(other map[string]interface{}) (api.Vehicle, error) { embed: &cc.embed, } - log := util.NewLogger("id") + log := util.NewLogger("id").Redact(cc.User, cc.Password, cc.VIN) identity := vw.NewIdentity(log) query := url.Values(map[string][]string{ diff --git a/vehicle/kia.go b/vehicle/kia.go index d2d3ed954..e6e5c6d0a 100644 --- a/vehicle/kia.go +++ b/vehicle/kia.go @@ -40,7 +40,8 @@ func NewKiaFromConfig(other map[string]interface{}) (api.Vehicle, error) { return nil, errors.New("missing credentials") } - log := util.NewLogger("kia") + log := util.NewLogger("kia").Redact(cc.User, cc.Password, cc.VIN) + settings := bluelink.Config{ URI: "https://prd.eu-ccapi.kia.com:8080", BasicToken: "ZmRjODVjMDAtMGEyZi00YzY0LWJjYjQtMmNmYjE1MDA3MzBhOnNlY3JldA==", diff --git a/vehicle/nissan.go b/vehicle/nissan.go index 18e49c19c..83e3bdd8d 100644 --- a/vehicle/nissan.go +++ b/vehicle/nissan.go @@ -48,7 +48,7 @@ func NewNissanFromConfig(other map[string]interface{}) (api.Vehicle, error) { embed: &cc.embed, } - log := util.NewLogger("nissan") + log := util.NewLogger("nissan").Redact(cc.User, cc.Password, cc.VIN) identity := nissan.NewIdentity(log) if err := identity.Login(cc.User, cc.Password); err != nil { diff --git a/vehicle/niu.go b/vehicle/niu.go index f77d93573..978115602 100644 --- a/vehicle/niu.go +++ b/vehicle/niu.go @@ -48,7 +48,7 @@ func NewNiuFromConfig(other map[string]interface{}) (api.Vehicle, error) { return nil, errors.New("missing user, password or serial") } - log := util.NewLogger("niu") + log := util.NewLogger("niu").Redact(cc.User, cc.Password) v := &Niu{ embed: &cc.embed, diff --git a/vehicle/ovms.go b/vehicle/ovms.go index db454046a..4732b5630 100644 --- a/vehicle/ovms.go +++ b/vehicle/ovms.go @@ -60,7 +60,7 @@ func NewOvmsFromConfig(other map[string]interface{}) (api.Vehicle, error) { return nil, err } - log := util.NewLogger("ovms") + log := util.NewLogger("ovms").Redact(cc.User, cc.Password, cc.VehicleID) v := &Ovms{ embed: &cc.embed, diff --git a/vehicle/porsche.go b/vehicle/porsche.go index 3f6e98f2a..b32499954 100644 --- a/vehicle/porsche.go +++ b/vehicle/porsche.go @@ -38,12 +38,11 @@ func NewPorscheFromConfig(other map[string]interface{}) (api.Vehicle, error) { Cache: interval, } - log := util.NewLogger("porsche") - if err := util.DecodeOther(other, &cc); err != nil { return nil, err } + log := util.NewLogger("porsche").Redact(cc.User, cc.Password, cc.VIN) identity := porsche.NewIdentity(log, cc.User, cc.Password) accessTokens, err := identity.Login() diff --git a/vehicle/psa.go b/vehicle/psa.go index b9c6bdb1a..a52828d54 100644 --- a/vehicle/psa.go +++ b/vehicle/psa.go @@ -85,6 +85,7 @@ func newPSA(log *util.Logger, brand, realm, id, secret string, other map[string] embed: &cc.embed, } + log.Redact(cc.User, cc.Password, cc.VIN) identity := psa.NewIdentity(log, brand, cc.Credentials.ID, cc.Credentials.Secret) if err := identity.Login(cc.User, cc.Password); err != nil { diff --git a/vehicle/renault.go b/vehicle/renault.go index 8d49ad6d2..1700173e5 100644 --- a/vehicle/renault.go +++ b/vehicle/renault.go @@ -128,7 +128,7 @@ func NewRenaultFromConfig(other map[string]interface{}) (api.Vehicle, error) { return nil, err } - log := util.NewLogger("renault") + log := util.NewLogger("renault").Redact(cc.User, cc.Password, cc.VIN) v := &Renault{ embed: &cc.embed, diff --git a/vehicle/seat.go b/vehicle/seat.go index 52c016eb4..8cfb7dccc 100644 --- a/vehicle/seat.go +++ b/vehicle/seat.go @@ -45,7 +45,7 @@ func NewSeatFromConfig(other map[string]interface{}) (api.Vehicle, error) { embed: &cc.embed, } - log := util.NewLogger("seat") + log := util.NewLogger("seat").Redact(cc.User, cc.Password, cc.VIN) identity := vw.NewIdentity(log) query := url.Values(map[string][]string{ diff --git a/vehicle/skoda.go b/vehicle/skoda.go index 817e85eb0..4b509f29a 100644 --- a/vehicle/skoda.go +++ b/vehicle/skoda.go @@ -45,7 +45,7 @@ func NewSkodaFromConfig(other map[string]interface{}) (api.Vehicle, error) { embed: &cc.embed, } - log := util.NewLogger("skoda") + log := util.NewLogger("skoda").Redact(cc.User, cc.Password, cc.VIN) identity := vw.NewIdentity(log) query := url.Values(map[string][]string{ diff --git a/vehicle/tesla.go b/vehicle/tesla.go index 05d0371ea..d529b6a75 100644 --- a/vehicle/tesla.go +++ b/vehicle/tesla.go @@ -50,7 +50,7 @@ func NewTeslaFromConfig(other map[string]interface{}) (api.Vehicle, error) { } // authenticated http client with logging injected to the Tesla client - log := util.NewLogger("tesla") + log := util.NewLogger("tesla").Redact(cc.Tokens.Access, cc.Tokens.Refresh) ctx := context.WithValue(context.Background(), oauth2.HTTPClient, request.NewHelper(log).Client) options := []tesla.ClientOption{tesla.WithToken(&oauth2.Token{ diff --git a/vehicle/tronity.go b/vehicle/tronity.go index b9b671312..a1df4fa3f 100644 --- a/vehicle/tronity.go +++ b/vehicle/tronity.go @@ -77,7 +77,7 @@ func NewTronityFromConfig(other map[string]interface{}) (api.Vehicle, error) { } // authenticated http client with logging injected to the tronity client - log := util.NewLogger("tronity") + log := util.NewLogger("tronity").Redact(cc.Credentials.ID, cc.Credentials.Secret) oc, err := tronity.OAuth2Config(cc.Credentials.ID, cc.Credentials.Secret) if err != nil { diff --git a/vehicle/volvo.go b/vehicle/volvo.go index 510c15722..336d7d87d 100644 --- a/vehicle/volvo.go +++ b/vehicle/volvo.go @@ -108,7 +108,7 @@ func NewVolvoFromConfig(other map[string]interface{}) (api.Vehicle, error) { return nil, err } - log := util.NewLogger("volvo") + log := util.NewLogger("volvo").Redact(cc.User, cc.Password, cc.VIN) v := &Volvo{ embed: &cc.embed, diff --git a/vehicle/vw.go b/vehicle/vw.go index 95fe5c927..5d3017fd6 100644 --- a/vehicle/vw.go +++ b/vehicle/vw.go @@ -45,7 +45,7 @@ func NewVWFromConfig(other map[string]interface{}) (api.Vehicle, error) { embed: &cc.embed, } - log := util.NewLogger("vw") + log := util.NewLogger("vw").Redact(cc.User, cc.Password, cc.VIN) identity := vw.NewIdentity(log) query := url.Values(map[string][]string{