From 1bfa9ed9eb24e1b4ad2e88c665b8a67aa4b8da94 Mon Sep 17 00:00:00 2001 From: Abhishek Patro Date: Thu, 23 Jan 2025 13:39:49 -0800 Subject: [PATCH] add linters --- .golangci.yml | 61 ++++ Makefile | 15 +- cmd/tesla-control/commands.go | 315 +++++++++++---------- cmd/tesla-control/main.go | 9 +- cmd/tesla-http-proxy/main.go | 18 +- cmd/tesla-http-proxy/main_test.go | 12 +- cmd/tesla-jws/main.go | 2 +- cmd/tesla-keygen/main.go | 9 +- internal/authentication/crypto.go | 2 +- internal/authentication/dispatcher.go | 8 +- internal/authentication/dispatcher_test.go | 4 +- internal/authentication/ecdh_test.go | 6 +- internal/authentication/example_test.go | 6 +- internal/authentication/jwt_test.go | 2 +- internal/authentication/native.go | 36 +-- internal/authentication/peer.go | 26 +- internal/authentication/signer_test.go | 8 +- internal/authentication/verifier.go | 5 +- internal/authentication/window_test.go | 14 +- internal/dispatcher/dispatcher_test.go | 4 +- internal/schnorr/schnorr.go | 3 +- pkg/account/account.go | 6 +- pkg/cache/cache.go | 9 +- pkg/cache/cache_test.go | 16 +- pkg/cache/example_test.go | 4 +- pkg/cli/config.go | 2 +- pkg/cli/keyring.go | 3 +- pkg/connector/ble/ble.go | 6 +- pkg/connector/inet/inet.go | 16 +- pkg/connector/inet/inet_test.go | 2 +- pkg/protocol/key.go | 4 +- pkg/proxy/command.go | 2 +- pkg/proxy/command_test.go | 2 +- pkg/proxy/proxy.go | 22 +- pkg/vehicle/climate.go | 2 +- pkg/vehicle/state.go | 24 +- pkg/vehicle/vcsec.go | 2 +- pkg/vehicle/vehicle.go | 3 +- pkg/vehicle/vehicle_mock_test.go | 2 +- pkg/vehicle/vehicle_test.go | 44 +-- 40 files changed, 413 insertions(+), 323 deletions(-) create mode 100644 .golangci.yml diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 00000000..f7c72867 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,61 @@ +--- +run: + timeout: 4m + +issues: + exclude-dirs: + - temp + exclude: + - "Error return value of `w.Write` is not checked" + - "Error return value of `fmt.Fprintf` is not checked" + - "Error return value of `fmt.Fprintln` is not checked" + - "Error return value of `file.Close` is not checked" + - "Error return value of `os.Setenv` is not checked" + +linters: + enable: + - errcheck + - gofmt + - gosimple + - govet + - misspell + - revive + - unused + disable: + # Remove from disable when all issues are fixed + - staticcheck + +linters-settings: + revive: + rules: + - name: blank-imports + - name: context-as-argument + - name: context-keys-type + - name: dot-imports + arguments: + - allowedPackages: + - "github.com/onsi/ginkgo" + - "github.com/onsi/ginkgo/v2" + - "github.com/onsi/gomega" + - name: empty-block + - name: error-naming + - name: error-return + - name: error-strings + - name: errorf + # Uncomment when all issues are fixed + # - name: exported + - name: increment-decrement + - name: indent-error-flow + - name: package-comments + disabled: true + - name: range + - name: receiver-naming + - name: redefines-builtin-id + - name: superfluous-else + - name: time-naming + # Uncomment when all issues are fixed + # - name: unexported-return + - name: unreachable-code + - name: unused-parameter + - name: var-declaration + - name: var-naming diff --git a/Makefile b/Makefile index 557f89c8..509870c9 100644 --- a/Makefile +++ b/Makefile @@ -1,22 +1,31 @@ +LINTER = golangci-lint run -v $(LINTER_FLAGS) --exclude-use-default=false --timeout $(LINTER_DEADLINE) +LINTER_DEADLINE = 30s +LINTER_FLAGS ?= + all: build +linters: + @echo "** Running linters...**" + $(LINTER) + @echo "** SUCCESS **" + set-version: if TAG=$$(git describe --tags --abbrev=0); then echo "$${TAG}" | sed 's/v//' > pkg/account/version.txt; fi format: set-version go fmt ./... -test: +test: install go test -cover ./... go vet ./... build: set-version test go build ./... -install: test +install: go install ./cmd/... doc-images: docker run -v ./:/data plantuml/plantuml "doc" -.PHONY: install build test format set-version doc-images +.PHONY: install build linters test format set-version doc-images diff --git a/cmd/tesla-control/commands.go b/cmd/tesla-control/commands.go index ff454e49..20bc399e 100644 --- a/cmd/tesla-control/commands.go +++ b/cmd/tesla-control/commands.go @@ -76,7 +76,7 @@ var categoriesByName = map[string]vehicle.StateCategory{ func categoryNames() []string { var names []string - for name, _ := range categoriesByName { + for name := range categoriesByName { names = append(names, name) } return names @@ -273,73 +273,73 @@ func (c *Command) Usage(name string) { } var commands = map[string]*Command{ - "valet-mode-on": &Command{ + "valet-mode-on": { help: "Enable valet mode", requiresAuth: true, requiresFleetAPI: false, args: []Argument{ - Argument{name: "PIN", help: "Valet mode PIN"}, + {name: "PIN", help: "Valet mode PIN"}, }, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, args map[string]string) error { return car.EnableValetMode(ctx, args["PIN"]) }, }, - "valet-mode-off": &Command{ + "valet-mode-off": { help: "Disable valet mode", requiresAuth: true, requiresFleetAPI: false, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, _ map[string]string) error { return car.DisableValetMode(ctx) }, }, - "unlock": &Command{ + "unlock": { help: "Unlock vehicle", requiresAuth: true, requiresFleetAPI: false, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, _ map[string]string) error { return car.Unlock(ctx) }, }, - "lock": &Command{ + "lock": { help: "Lock vehicle", requiresAuth: true, requiresFleetAPI: false, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, _ map[string]string) error { return car.Lock(ctx) }, }, - "drive": &Command{ + "drive": { help: "Remote start vehicle", requiresAuth: true, requiresFleetAPI: false, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, _ map[string]string) error { return car.RemoteDrive(ctx) }, }, - "climate-on": &Command{ + "climate-on": { help: "Turn on climate control", requiresAuth: true, requiresFleetAPI: false, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, _ map[string]string) error { return car.ClimateOn(ctx) }, }, - "climate-off": &Command{ + "climate-off": { help: "Turn off climate control", requiresAuth: true, requiresFleetAPI: false, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, _ map[string]string) error { return car.ClimateOff(ctx) }, }, - "climate-set-temp": &Command{ + "climate-set-temp": { help: "Set temperature (Celsius)", requiresAuth: true, requiresFleetAPI: false, args: []Argument{ - Argument{name: "TEMP", help: "Desired temperature (e.g., 70f or 21c; defaults to Celsius)"}, + {name: "TEMP", help: "Desired temperature (e.g., 70f or 21c; defaults to Celsius)"}, }, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, args map[string]string) error { var degrees float32 var unit string if _, err := fmt.Sscanf(args["TEMP"], "%f%s", °rees, &unit); err != nil { @@ -353,16 +353,16 @@ var commands = map[string]*Command{ return car.ChangeClimateTemp(ctx, degrees, degrees) }, }, - "add-key": &Command{ + "add-key": { help: "Add PUBLIC_KEY to vehicle whitelist with ROLE and FORM_FACTOR", requiresAuth: true, requiresFleetAPI: false, args: []Argument{ - Argument{name: "PUBLIC_KEY", help: "file containing public key (or corresponding private key)"}, - Argument{name: "ROLE", help: "One of: owner, driver, fm (fleet manager), vehicle_monitor, charging_manager"}, - Argument{name: "FORM_FACTOR", help: "One of: nfc_card, ios_device, android_device, cloud_key"}, + {name: "PUBLIC_KEY", help: "file containing public key (or corresponding private key)"}, + {name: "ROLE", help: "One of: owner, driver, fm (fleet manager), vehicle_monitor, charging_manager"}, + {name: "FORM_FACTOR", help: "One of: nfc_card, ios_device, android_device, cloud_key"}, }, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, args map[string]string) error { role, ok := keys.Role_value["ROLE_"+strings.ToUpper(args["ROLE"])] if !ok { return fmt.Errorf("%w: invalid ROLE", ErrCommandLineArgs) @@ -378,16 +378,16 @@ var commands = map[string]*Command{ return car.AddKeyWithRole(ctx, publicKey, keys.Role(role), vcsec.KeyFormFactor(formFactor)) }, }, - "add-key-request": &Command{ + "add-key-request": { help: "Request NFC-card approval for a enrolling PUBLIC_KEY with ROLE and FORM_FACTOR", requiresAuth: false, requiresFleetAPI: false, args: []Argument{ - Argument{name: "PUBLIC_KEY", help: "file containing public key (or corresponding private key)"}, - Argument{name: "ROLE", help: "One of: owner, driver, fm (fleet manager), vehicle_monitor, charging_manager"}, - Argument{name: "FORM_FACTOR", help: "One of: nfc_card, ios_device, android_device, cloud_key"}, + {name: "PUBLIC_KEY", help: "file containing public key (or corresponding private key)"}, + {name: "ROLE", help: "One of: owner, driver, fm (fleet manager), vehicle_monitor, charging_manager"}, + {name: "FORM_FACTOR", help: "One of: nfc_card, ios_device, android_device, cloud_key"}, }, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, args map[string]string) error { role, ok := keys.Role_value["ROLE_"+strings.ToUpper(args["ROLE"])] if !ok { return fmt.Errorf("%w: invalid ROLE", ErrCommandLineArgs) @@ -407,14 +407,14 @@ var commands = map[string]*Command{ return nil }, }, - "remove-key": &Command{ + "remove-key": { help: "Remove PUBLIC_KEY from vehicle whitelist", requiresAuth: true, requiresFleetAPI: false, args: []Argument{ - Argument{name: "PUBLIC_KEY", help: "file containing public key (or corresponding private key)"}, + {name: "PUBLIC_KEY", help: "file containing public key (or corresponding private key)"}, }, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, args map[string]string) error { publicKey, err := protocol.LoadPublicKey(args["PUBLIC_KEY"]) if err != nil { return fmt.Errorf("invalid public key: %s", err) @@ -422,15 +422,15 @@ var commands = map[string]*Command{ return car.RemoveKey(ctx, publicKey) }, }, - "rename-key": &Command{ + "rename-key": { help: "Change the human-readable metadata of PUBLIC_KEY to NAME, MODEL, KIND", requiresAuth: false, requiresFleetAPI: true, args: []Argument{ - Argument{name: "PUBLIC_KEY", help: "file containing public key (or corresponding private key)"}, - Argument{name: "NAME", help: "New human-readable name for the public key (e.g., Dave's Phone)"}, + {name: "PUBLIC_KEY", help: "file containing public key (or corresponding private key)"}, + {name: "NAME", help: "New human-readable name for the public key (e.g., Dave's Phone)"}, }, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, acct *account.Account, _ *vehicle.Vehicle, args map[string]string) error { publicKey, err := protocol.LoadPublicKey(args["PUBLIC_KEY"]) if err != nil { return fmt.Errorf("invalid public key: %s", err) @@ -438,14 +438,14 @@ var commands = map[string]*Command{ return acct.UpdateKey(ctx, publicKey, args["NAME"]) }, }, - "get": &Command{ + "get": { help: "GET an owner API http ENDPOINT. Hostname will be taken from -config.", requiresAuth: false, requiresFleetAPI: true, args: []Argument{ - Argument{name: "ENDPOINT", help: "Fleet API endpoint"}, + {name: "ENDPOINT", help: "Fleet API endpoint"}, }, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, acct *account.Account, _ *vehicle.Vehicle, args map[string]string) error { reply, err := acct.Get(ctx, args["ENDPOINT"]) if err != nil { return err @@ -454,17 +454,17 @@ var commands = map[string]*Command{ return nil }, }, - "post": &Command{ + "post": { help: "POST to ENDPOINT the contents of FILE. Hostname will be taken from -config.", requiresAuth: false, requiresFleetAPI: true, args: []Argument{ - Argument{name: "ENDPOINT", help: "Fleet API endpoint"}, + {name: "ENDPOINT", help: "Fleet API endpoint"}, }, optional: []Argument{ - Argument{name: "FILE", help: "JSON file to POST"}, + {name: "FILE", help: "JSON file to POST"}, }, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, acct *account.Account, _ *vehicle.Vehicle, args map[string]string) error { var jsonBytes []byte var err error if filename, ok := args["FILE"]; ok { @@ -486,11 +486,11 @@ var commands = map[string]*Command{ return nil }, }, - "list-keys": &Command{ + "list-keys": { help: "List public keys enrolled on vehicle", requiresAuth: false, requiresFleetAPI: false, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, _ map[string]string) error { summary, err := car.KeySummary(ctx) if err != nil { return err @@ -515,37 +515,37 @@ var commands = map[string]*Command{ return nil }, }, - "honk": &Command{ + "honk": { help: "Honk horn", requiresAuth: true, requiresFleetAPI: false, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, _ map[string]string) error { return car.HonkHorn(ctx) }, }, - "ping": &Command{ + "ping": { help: "Ping vehicle", requiresAuth: true, requiresFleetAPI: false, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, _ map[string]string) error { return car.Ping(ctx) }, }, - "flash-lights": &Command{ + "flash-lights": { help: "Flash lights", requiresAuth: true, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, _ map[string]string) error { return car.FlashLights(ctx) }, }, - "charging-set-limit": &Command{ + "charging-set-limit": { help: "Set charge limit to PERCENT", requiresAuth: true, requiresFleetAPI: false, args: []Argument{ - Argument{name: "PERCENT", help: "Charging limit"}, + {name: "PERCENT", help: "Charging limit"}, }, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, args map[string]string) error { limit, err := strconv.Atoi(args["PERCENT"]) if err != nil { return fmt.Errorf("error parsing PERCENT") @@ -553,14 +553,14 @@ var commands = map[string]*Command{ return car.ChangeChargeLimit(ctx, int32(limit)) }, }, - "charging-set-amps": &Command{ + "charging-set-amps": { help: "Set charge current to AMPS", requiresAuth: true, requiresFleetAPI: false, args: []Argument{ - Argument{name: "AMPS", help: "Charging current"}, + {name: "AMPS", help: "Charging current"}, }, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, args map[string]string) error { limit, err := strconv.Atoi(args["AMPS"]) if err != nil { return fmt.Errorf("error parsing AMPS") @@ -568,30 +568,30 @@ var commands = map[string]*Command{ return car.SetChargingAmps(ctx, int32(limit)) }, }, - "charging-start": &Command{ + "charging-start": { help: "Start charging", requiresAuth: true, requiresFleetAPI: false, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, _ map[string]string) error { return car.ChargeStart(ctx) }, }, - "charging-stop": &Command{ + "charging-stop": { help: "Stop charging", requiresAuth: true, requiresFleetAPI: false, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, _ map[string]string) error { return car.ChargeStop(ctx) }, }, - "charging-schedule": &Command{ + "charging-schedule": { help: "Schedule charging to MINS minutes after midnight and enable daily scheduling", requiresAuth: true, requiresFleetAPI: false, args: []Argument{ - Argument{name: "MINS", help: "Time after midnight in minutes"}, + {name: "MINS", help: "Time after midnight in minutes"}, }, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, args map[string]string) error { minutesAfterMidnight, err := strconv.Atoi(args["MINS"]) if err != nil { return fmt.Errorf("error parsing minutes") @@ -601,22 +601,22 @@ var commands = map[string]*Command{ return car.ScheduleCharging(ctx, true, chargingTime) }, }, - "charging-schedule-cancel": &Command{ + "charging-schedule-cancel": { help: "Cancel scheduled charge start", requiresAuth: true, requiresFleetAPI: false, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, _ map[string]string) error { return car.ScheduleCharging(ctx, false, 0*time.Hour) }, }, - "media-set-volume": &Command{ + "media-set-volume": { help: "Set volume", requiresAuth: true, requiresFleetAPI: false, args: []Argument{ - Argument{name: "VOLUME", help: "Set volume (0.0-10.0"}, + {name: "VOLUME", help: "Set volume (0.0-10.0"}, }, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, args map[string]string) error { volume, err := strconv.ParseFloat(args["VOLUME"], 32) if err != nil { return fmt.Errorf("failed to parse volume") @@ -624,26 +624,26 @@ var commands = map[string]*Command{ return car.SetVolume(ctx, float32(volume)) }, }, - "media-toggle-playback": &Command{ + "media-toggle-playback": { help: "Toggle between play/pause", requiresAuth: true, requiresFleetAPI: false, args: []Argument{}, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, _ map[string]string) error { return car.ToggleMediaPlayback(ctx) }, }, - "software-update-start": &Command{ + "software-update-start": { help: "Start software update after DELAY", requiresAuth: true, requiresFleetAPI: false, args: []Argument{ - Argument{ + { name: "DELAY", help: "Time to wait before starting update. Examples: 2h, 10m.", }, }, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, args map[string]string) error { delay, err := time.ParseDuration(args["DELAY"]) if err != nil { return fmt.Errorf("error parsing DELAY. Valid times are , where is a number (decimals are allowed) and is 's, 'm', or 'h'") @@ -652,22 +652,22 @@ var commands = map[string]*Command{ return car.ScheduleSoftwareUpdate(ctx, delay) }, }, - "software-update-cancel": &Command{ + "software-update-cancel": { help: "Cancel a pending software update", requiresAuth: true, requiresFleetAPI: false, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, _ map[string]string) error { return car.CancelSoftwareUpdate(ctx) }, }, - "sentry-mode": &Command{ + "sentry-mode": { help: "Set sentry mode to STATE ('on' or 'off')", requiresAuth: true, requiresFleetAPI: false, args: []Argument{ - Argument{name: "STATE", help: "'on' or 'off'"}, + {name: "STATE", help: "'on' or 'off'"}, }, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, args map[string]string) error { var state bool switch args["STATE"] { case "on": @@ -680,101 +680,101 @@ var commands = map[string]*Command{ return car.SetSentryMode(ctx, state) }, }, - "wake": &Command{ + "wake": { help: "Wake up vehicle", requiresAuth: false, requiresFleetAPI: false, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, _ map[string]string) error { return car.Wakeup(ctx) }, }, - "tonneau-open": &Command{ + "tonneau-open": { help: "Open Cybertruck tonneau.", requiresAuth: true, requiresFleetAPI: false, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, _ map[string]string) error { return car.OpenTonneau(ctx) }, }, - "tonneau-close": &Command{ + "tonneau-close": { help: "Close Cybertruck tonneau.", requiresAuth: true, requiresFleetAPI: false, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, _ map[string]string) error { return car.CloseTonneau(ctx) }, }, - "tonneau-stop": &Command{ + "tonneau-stop": { help: "Stop moving Cybertruck tonneau.", requiresAuth: true, requiresFleetAPI: false, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, _ map[string]string) error { return car.StopTonneau(ctx) }, }, - "trunk-open": &Command{ + "trunk-open": { help: "Open vehicle trunk. Note that trunk-close only works on certain vehicle types.", requiresAuth: true, requiresFleetAPI: false, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, _ map[string]string) error { return car.OpenTrunk(ctx) }, }, - "trunk-move": &Command{ + "trunk-move": { help: "Toggle trunk open/closed. Closing is only available on certain vehicle types.", requiresAuth: true, requiresFleetAPI: false, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, _ map[string]string) error { return car.ActuateTrunk(ctx) }, }, - "trunk-close": &Command{ + "trunk-close": { help: "Closes vehicle trunk. Only available on certain vehicle types.", requiresAuth: true, requiresFleetAPI: false, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, _ map[string]string) error { return car.CloseTrunk(ctx) }, }, - "frunk-open": &Command{ + "frunk-open": { help: "Open vehicle frunk. Note that there's no frunk-close command!", requiresAuth: true, requiresFleetAPI: false, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, _ map[string]string) error { return car.OpenFrunk(ctx) }, }, - "charge-port-open": &Command{ + "charge-port-open": { help: "Open charge port", requiresAuth: true, requiresFleetAPI: false, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, _ map[string]string) error { return car.OpenChargePort(ctx) }, }, - "charge-port-close": &Command{ + "charge-port-close": { help: "Close charge port", requiresAuth: true, requiresFleetAPI: false, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, _ map[string]string) error { return car.CloseChargePort(ctx) }, }, - "autosecure-modelx": &Command{ + "autosecure-modelx": { help: "Close falcon-wing doors and lock vehicle. Model X only.", - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, _ map[string]string) error { return car.AutoSecureVehicle(ctx) }, }, - "session-info": &Command{ + "session-info": { help: "Retrieve session info for PUBLIC_KEY from DOMAIN", requiresAuth: false, requiresFleetAPI: false, args: []Argument{ - Argument{name: "PUBLIC_KEY", help: "file containing public key (or corresponding private key)"}, - Argument{name: "DOMAIN", help: "'vcsec' or 'infotainment'"}, + {name: "PUBLIC_KEY", help: "file containing public key (or corresponding private key)"}, + {name: "DOMAIN", help: "'vcsec' or 'infotainment'"}, }, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, args map[string]string) error { // See SeatPosition definition for controlling backrest heaters (limited models). domains := map[string]protocol.Domain{ "vcsec": protocol.DomainVCSEC, @@ -796,15 +796,15 @@ var commands = map[string]*Command{ return nil }, }, - "seat-heater": &Command{ + "seat-heater": { help: "Set seat heater at POSITION to LEVEL", requiresAuth: true, requiresFleetAPI: false, args: []Argument{ - Argument{name: "SEAT", help: "- (e.g., 2nd-row-left)"}, - Argument{name: "LEVEL", help: "off, low, medium, or high"}, + {name: "SEAT", help: "- (e.g., 2nd-row-left)"}, + {name: "LEVEL", help: "off, low, medium, or high"}, }, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, args map[string]string) error { // See SeatPosition definition for controlling backrest heaters (limited models). seats := map[string]vehicle.SeatPosition{ "front-left": vehicle.SeatFrontLeft, @@ -835,14 +835,14 @@ var commands = map[string]*Command{ return car.SetSeatHeater(ctx, spec) }, }, - "steering-wheel-heater": &Command{ + "steering-wheel-heater": { help: "Set steering wheel mode to STATE ('on' or 'off')", requiresAuth: true, requiresFleetAPI: false, args: []Argument{ - Argument{name: "STATE", help: "'on' or 'off'"}, + {name: "STATE", help: "'on' or 'off'"}, }, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, args map[string]string) error { var state bool switch args["STATE"] { case "on": @@ -855,11 +855,11 @@ var commands = map[string]*Command{ return car.SetSteeringWheelHeater(ctx, state) }, }, - "product-info": &Command{ + "product-info": { help: "Print JSON product info", requiresAuth: false, requiresFleetAPI: true, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, acct *account.Account, _ *vehicle.Vehicle, _ map[string]string) error { productsJSON, err := acct.Get(ctx, "api/1/products") if err != nil { return err @@ -868,17 +868,17 @@ var commands = map[string]*Command{ return nil }, }, - "auto-seat-and-climate": &Command{ + "auto-seat-and-climate": { help: "Turn on automatic seat heating and HVAC", requiresAuth: true, requiresFleetAPI: false, args: []Argument{ - Argument{name: "POSITIONS", help: "'L' (left), 'R' (right), or 'LR'"}, + {name: "POSITIONS", help: "'L' (left), 'R' (right), or 'LR'"}, }, optional: []Argument{ - Argument{name: "STATE", help: "'on' (default) or 'off'"}, + {name: "STATE", help: "'on' (default) or 'off'"}, }, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, args map[string]string) error { var positions []vehicle.SeatPosition if strings.Contains(args["POSITIONS"], "L") { positions = append(positions, vehicle.SeatFrontLeft) @@ -896,28 +896,28 @@ var commands = map[string]*Command{ return car.AutoSeatAndClimate(ctx, positions, enabled) }, }, - "windows-vent": &Command{ + "windows-vent": { help: "Vent all windows", requiresAuth: true, requiresFleetAPI: false, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, _ map[string]string) error { return car.VentWindows(ctx) }, }, - "windows-close": &Command{ + "windows-close": { help: "Close all windows", requiresAuth: true, requiresFleetAPI: false, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, _ map[string]string) error { return car.CloseWindows(ctx) }, }, - "body-controller-state": &Command{ + "body-controller-state": { help: "Fetch limited vehicle state information. Works over BLE when infotainment is asleep.", domain: protocol.DomainVCSEC, requiresAuth: false, requiresFleetAPI: false, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, _ map[string]string) error { info, err := car.BodyControllerState(ctx) if err != nil { return err @@ -932,46 +932,46 @@ var commands = map[string]*Command{ return nil }, }, - "guest-mode-on": &Command{ + "guest-mode-on": { help: "Enable Guest Mode. See https://developer.tesla.com/docs/fleet-api/endpoints/vehicle-commands#guest-mode.", requiresAuth: true, requiresFleetAPI: false, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, _ map[string]string) error { return car.SetGuestMode(ctx, true) }, }, - "guest-mode-off": &Command{ + "guest-mode-off": { help: "Disable Guest Mode.", requiresAuth: true, requiresFleetAPI: false, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, _ map[string]string) error { return car.SetGuestMode(ctx, false) }, }, - "erase-guest-data": &Command{ + "erase-guest-data": { help: "Erase Guest Mode user data", requiresAuth: true, requiresFleetAPI: false, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, _ map[string]string) error { return car.EraseGuestData(ctx) }, }, - "charging-schedule-add": &Command{ + "charging-schedule-add": { help: "Schedule charge for DAYS START_TIME-END_TIME at LATITUDE LONGITUDE. The END_TIME may be on the following day.", requiresAuth: true, requiresFleetAPI: false, args: []Argument{ - Argument{name: "DAYS", help: "Comma-separated list of any of Sun, Mon, Tues, Wed, Thurs, Fri, Sat OR all OR weekdays"}, - Argument{name: "TIME", help: "Time interval to charge (24-hour clock). Examples: '22:00-6:00', '-6:00', '20:32-"}, - Argument{name: "LATITUDE", help: "Latitude of charging site"}, - Argument{name: "LONGITUDE", help: "Longitude of charging site"}, + {name: "DAYS", help: "Comma-separated list of any of Sun, Mon, Tues, Wed, Thurs, Fri, Sat OR all OR weekdays"}, + {name: "TIME", help: "Time interval to charge (24-hour clock). Examples: '22:00-6:00', '-6:00', '20:32-"}, + {name: "LATITUDE", help: "Latitude of charging site"}, + {name: "LONGITUDE", help: "Longitude of charging site"}, }, optional: []Argument{ - Argument{name: "REPEAT", help: "Set to 'once' or omit to repeat weekly"}, - Argument{name: "ID", help: "The ID of the charge schedule to modify. Not required for new schedules."}, - Argument{name: "ENABLED", help: "Whether the charge schedule is enabled. Expects 'true' or 'false'. Defaults to true."}, + {name: "REPEAT", help: "Set to 'once' or omit to repeat weekly"}, + {name: "ID", help: "The ID of the charge schedule to modify. Not required for new schedules."}, + {name: "ENABLED", help: "Whether the charge schedule is enabled. Expects 'true' or 'false'. Defaults to true."}, }, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, args map[string]string) error { var err error schedule := vehicle.ChargeSchedule{ Id: uint64(time.Now().Unix()), @@ -1034,12 +1034,12 @@ var commands = map[string]*Command{ requiresAuth: true, requiresFleetAPI: false, args: []Argument{ - Argument{name: "TYPE", help: "home|work|other|id"}, + {name: "TYPE", help: "home|work|other|id"}, }, optional: []Argument{ - Argument{name: "ID", help: "numeric ID of schedule to remove when TYPE set to id"}, + {name: "ID", help: "numeric ID of schedule to remove when TYPE set to id"}, }, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, args map[string]string) error { var home, work, other bool switch strings.ToUpper(args["TYPE"]) { case "ID": @@ -1064,22 +1064,22 @@ var commands = map[string]*Command{ return car.BatchRemoveChargeSchedules(ctx, home, work, other) }, }, - "precondition-schedule-add": &Command{ + "precondition-schedule-add": { help: "Schedule precondition for DAYS TIME at LATITUDE LONGITUDE.", requiresAuth: true, requiresFleetAPI: false, args: []Argument{ - Argument{name: "DAYS", help: "Comma-separated list of any of Sun, Mon, Tues, Wed, Thurs, Fri, Sat OR all OR weekdays"}, - Argument{name: "TIME", help: "Time to precondition by. Example: '22:00'"}, - Argument{name: "LATITUDE", help: "Latitude of location to precondition at."}, - Argument{name: "LONGITUDE", help: "Longitude of location to precondition at."}, + {name: "DAYS", help: "Comma-separated list of any of Sun, Mon, Tues, Wed, Thurs, Fri, Sat OR all OR weekdays"}, + {name: "TIME", help: "Time to precondition by. Example: '22:00'"}, + {name: "LATITUDE", help: "Latitude of location to precondition at."}, + {name: "LONGITUDE", help: "Longitude of location to precondition at."}, }, optional: []Argument{ - Argument{name: "REPEAT", help: "Set to 'once' or omit to repeat weekly"}, - Argument{name: "ID", help: "The ID of the precondition schedule to modify. Not required for new schedules."}, - Argument{name: "ENABLED", help: "Whether the precondition schedule is enabled. Expects 'true' or 'false'. Defaults to true."}, + {name: "REPEAT", help: "Set to 'once' or omit to repeat weekly"}, + {name: "ID", help: "The ID of the precondition schedule to modify. Not required for new schedules."}, + {name: "ENABLED", help: "Whether the precondition schedule is enabled. Expects 'true' or 'false'. Defaults to true."}, }, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, args map[string]string) error { var err error schedule := vehicle.PreconditionSchedule{ Id: uint64(time.Now().Unix()), @@ -1105,6 +1105,9 @@ var commands = map[string]*Command{ if timeStr, ok := args["TIME"]; ok { schedule.PreconditionTime, err = MinutesAfterMidnight(timeStr) + if err != nil { + return err + } } else { return errors.New("expected TIME") } @@ -1135,12 +1138,12 @@ var commands = map[string]*Command{ requiresAuth: true, requiresFleetAPI: false, args: []Argument{ - Argument{name: "TYPE", help: "home|work|other|id"}, + {name: "TYPE", help: "home|work|other|id"}, }, optional: []Argument{ - Argument{name: "ID", help: "numeric ID of schedule to remove when TYPE set to id"}, + {name: "ID", help: "numeric ID of schedule to remove when TYPE set to id"}, }, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, args map[string]string) error { var home, work, other bool switch strings.ToUpper(args["TYPE"]) { case "ID": @@ -1170,9 +1173,9 @@ var commands = map[string]*Command{ requiresAuth: true, requiresFleetAPI: false, args: []Argument{ - Argument{name: "CATEGORY", help: "One of " + strings.Join(categoryNames(), ", ")}, + {name: "CATEGORY", help: "One of " + strings.Join(categoryNames(), ", ")}, }, - handler: func(ctx context.Context, acct *account.Account, car *vehicle.Vehicle, args map[string]string) error { + handler: func(ctx context.Context, _ *account.Account, car *vehicle.Vehicle, args map[string]string) error { category, err := GetCategory(args["CATEGORY"]) if err != nil { return err diff --git a/cmd/tesla-control/main.go b/cmd/tesla-control/main.go index 6b26a37e..368254d4 100644 --- a/cmd/tesla-control/main.go +++ b/cmd/tesla-control/main.go @@ -146,11 +146,10 @@ func main() { info.Usage(args[1]) status = 0 return - } else { - if err := configureFlags(config, args[0], forceBLE); err != nil { - writeErr("Missing required flag: %s", err) - return - } + } + if err := configureFlags(config, args[0], forceBLE); err != nil { + writeErr("Missing required flag: %s", err) + return } } diff --git a/cmd/tesla-http-proxy/main.go b/cmd/tesla-http-proxy/main.go index 77aac8ab..064b9869 100644 --- a/cmd/tesla-http-proxy/main.go +++ b/cmd/tesla-http-proxy/main.go @@ -22,8 +22,8 @@ const ( ) const ( - EnvTlsCert = "TESLA_HTTP_PROXY_TLS_CERT" - EnvTlsKey = "TESLA_HTTP_PROXY_TLS_KEY" + EnvTLSCert = "TESLA_HTTP_PROXY_TLS_CERT" + EnvTLSKey = "TESLA_HTTP_PROXY_TLS_KEY" EnvHost = "TESLA_HTTP_PROXY_HOST" EnvPort = "TESLA_HTTP_PROXY_PORT" EnvTimeout = "TESLA_HTTP_PROXY_TIMEOUT" @@ -35,7 +35,7 @@ Do not listen on a network interface without adding client authentication. Unaut be used to create excessive traffic from your IP address to Tesla's servers, which Tesla may respond to by rate limiting or blocking your connections.` -type HttpProxyConfig struct { +type HTTProxyConfig struct { keyFilename string certFilename string verbose bool @@ -45,7 +45,7 @@ type HttpProxyConfig struct { } var ( - httpConfig = &HttpProxyConfig{} + httpConfig = &HTTProxyConfig{} ) func init() { @@ -86,7 +86,11 @@ func main() { flag.Usage = Usage config.RegisterCommandLineFlags() flag.Parse() - readFromEnvironment() + err = readFromEnvironment() + if err != nil { + fmt.Fprintf(os.Stderr, "Error reading environment: %s\n", err) + os.Exit(1) + } config.ReadFromEnvironment() if httpConfig.verbose { @@ -138,11 +142,11 @@ func main() { // Values are not overwritten. func readFromEnvironment() error { if httpConfig.certFilename == "" { - httpConfig.certFilename = os.Getenv(EnvTlsCert) + httpConfig.certFilename = os.Getenv(EnvTLSCert) } if httpConfig.keyFilename == "" { - httpConfig.keyFilename = os.Getenv(EnvTlsKey) + httpConfig.keyFilename = os.Getenv(EnvTLSKey) } if httpConfig.host == "localhost" { diff --git a/cmd/tesla-http-proxy/main_test.go b/cmd/tesla-http-proxy/main_test.go index 0d0ed736..475406a3 100644 --- a/cmd/tesla-http-proxy/main_test.go +++ b/cmd/tesla-http-proxy/main_test.go @@ -18,8 +18,8 @@ func assertEquals(t *testing.T, expected, actual interface{}, message string) { } func TestParseConfig(t *testing.T) { - origCert := os.Getenv(EnvTlsCert) - origKey := os.Getenv(EnvTlsKey) + origCert := os.Getenv(EnvTLSCert) + origKey := os.Getenv(EnvTLSKey) origHost := os.Getenv(EnvHost) origPort := os.Getenv(EnvPort) origVerbose := os.Getenv(EnvVerbose) @@ -28,8 +28,8 @@ func TestParseConfig(t *testing.T) { os.Args = []string{"cmd"} defer func() { - os.Setenv(EnvTlsCert, origCert) - os.Setenv(EnvTlsKey, origKey) + os.Setenv(EnvTLSCert, origCert) + os.Setenv(EnvTLSKey, origKey) os.Setenv(EnvHost, origHost) os.Setenv(EnvPort, origPort) os.Setenv(EnvVerbose, origVerbose) @@ -51,8 +51,8 @@ func TestParseConfig(t *testing.T) { }) t.Run("environment variables", func(t *testing.T) { - os.Setenv(EnvTlsCert, "/env/cert.pem") - os.Setenv(EnvTlsKey, "/env/key.pem") + os.Setenv(EnvTLSCert, "/env/cert.pem") + os.Setenv(EnvTLSKey, "/env/key.pem") os.Setenv(EnvHost, "envhost") os.Setenv(EnvPort, "8443") os.Setenv(EnvVerbose, "true") diff --git a/cmd/tesla-jws/main.go b/cmd/tesla-jws/main.go index 169d468b..b5f8317e 100644 --- a/cmd/tesla-jws/main.go +++ b/cmd/tesla-jws/main.go @@ -21,7 +21,7 @@ usage: tesla-jws [OPTION...] sign APP [JSON_FILE] Verifies that the signature on JWS_FILE is correct, but does not check that the issuer is trusted or that the audience is correct. -Creates or verifies a JWS (JSON Web Siganture) using Schnorr/P256 signatures. This signature type is +Creates or verifies a JWS (JSON Web Signature) using Schnorr/P256 signatures. This signature type is not part of the JWS standard, but permits clients to safely re-use existing ECDH/P256 keys as signing keys. diff --git a/cmd/tesla-keygen/main.go b/cmd/tesla-keygen/main.go index b330ec5a..fe0d06d1 100644 --- a/cmd/tesla-keygen/main.go +++ b/cmd/tesla-keygen/main.go @@ -67,11 +67,14 @@ func printPublicKey(skey protocol.ECDHPrivateKey, outputFile string) bool { writeErr("Failed to create output file: %s", err) return false } - defer file.Close() + + defer func() { + file.Close() + }() out = file } - pem.Encode(out, &pem.Block{Type: "PUBLIC KEY", Bytes: derPublicKey}) + _ = pem.Encode(out, &pem.Block{Type: "PUBLIC KEY", Bytes: derPublicKey}) return true } @@ -84,7 +87,7 @@ func printPrivateKey(skey protocol.ECDHPrivateKey) error { if err != nil { return err } - pem.Encode(os.Stdout, &pem.Block{Type: "EC PRIVATE KEY", Bytes: derPrivateKey}) + _ = pem.Encode(os.Stdout, &pem.Block{Type: "EC PRIVATE KEY", Bytes: derPrivateKey}) return nil } diff --git a/internal/authentication/crypto.go b/internal/authentication/crypto.go index 37a553e5..882b4041 100644 --- a/internal/authentication/crypto.go +++ b/internal/authentication/crypto.go @@ -16,7 +16,7 @@ const ( const ( counterMax = 0xFFFFFFFF - epochIdLength = 16 + epochIDLength = 16 maxSecondsWithoutCounter = 30 windowSize = 32 // Verifier.windowSize is uint64, so must be ≤ 64. ) diff --git a/internal/authentication/dispatcher.go b/internal/authentication/dispatcher.go index 19889cd7..82e4fc26 100644 --- a/internal/authentication/dispatcher.go +++ b/internal/authentication/dispatcher.go @@ -9,10 +9,10 @@ type Dispatcher struct { ECDHPrivateKey } -func (d *Dispatcher) Connect(verifierId []byte, sessionInfo *signatures.SessionInfo) (*Signer, error) { - return NewSigner(d, verifierId, sessionInfo) +func (d *Dispatcher) Connect(verifierID []byte, sessionInfo *signatures.SessionInfo) (*Signer, error) { + return NewSigner(d, verifierID, sessionInfo) } -func (d *Dispatcher) ConnectAuthenticated(verifierId, challenge, encodedSessionInfo, tag []byte) (*Signer, error) { - return NewAuthenticatedSigner(d, verifierId, challenge, encodedSessionInfo, tag) +func (d *Dispatcher) ConnectAuthenticated(verifierID, challenge, encodedSessionInfo, tag []byte) (*Signer, error) { + return NewAuthenticatedSigner(d, verifierID, challenge, encodedSessionInfo, tag) } diff --git a/internal/authentication/dispatcher_test.go b/internal/authentication/dispatcher_test.go index d6e4243e..83688e9c 100644 --- a/internal/authentication/dispatcher_test.go +++ b/internal/authentication/dispatcher_test.go @@ -28,9 +28,9 @@ func ExampleDispatcher() { var challenge [16]byte if _, err := rand.Read(challenge[:]); err != nil { panic(fmt.Sprintf("Failed to generate random challenge: %s", err)) - } else { - challenges = append(challenges, challenge[:]) } + challenges = append(challenges, challenge[:]) + if err != nil { panic(fmt.Sprintf("Failed to generate car key: %s", err)) } diff --git a/internal/authentication/ecdh_test.go b/internal/authentication/ecdh_test.go index 5f303f7c..72589ae7 100644 --- a/internal/authentication/ecdh_test.go +++ b/internal/authentication/ecdh_test.go @@ -7,7 +7,7 @@ import ( "testing" ) -func phonePublicTestKey(t *testing.T) []byte { +func phonePublicTestKey() []byte { return []byte{ 0x04, 0x07, 0xfb, 0x60, 0xb6, 0x5b, 0x94, 0xe0, 0xde, 0x4a, 0x95, 0x4c, 0x53, 0xbe, 0x10, 0x00, 0x3d, 0x9e, 0x69, 0x91, 0x8d, 0xed, @@ -57,7 +57,7 @@ func TestSharedKey(t *testing.T) { 0xac, 0xe2, 0x2c, 0xa1, 0x5e, 0x6f, 0xd8, 0x45, } - phonePublicKey := phonePublicTestKey(t) + phonePublicKey := phonePublicTestKey() vehicleKey := UnmarshalECDHPrivateKey(privateKeyBytes).(*NativeECDHKey) if vehicleKey == nil { t.Fatalf("Error parsing private key") @@ -88,7 +88,7 @@ func TestSharedKey(t *testing.T) { func TestZero(t *testing.T) { privateKeyBytes := make([]byte, 32) - phonePublicKey := phonePublicTestKey(t) + phonePublicKey := phonePublicTestKey() vehicleKey := UnmarshalECDHPrivateKey(privateKeyBytes).(*NativeECDHKey) if vehicleKey == nil { t.Fatalf("Error parsing private key") diff --git a/internal/authentication/example_test.go b/internal/authentication/example_test.go index 7dfba723..821ea091 100644 --- a/internal/authentication/example_test.go +++ b/internal/authentication/example_test.go @@ -26,12 +26,12 @@ func Example() { // Signer and Verifier exchange public keys through an authenticated channel. // Signer must know Verifier domain and name (see protocol description). domain := universal.Domain_DOMAIN_VEHICLE_SECURITY - verifierId := []byte("testVIN-1234") + verifierID := []byte("testVIN-1234") /***** Once per session *****************************************************/ // (A session typically lasts until either Signer or Verifier reboots) - verifier, err := command.NewVerifier(verifierKey, verifierId, domain, signerKey.PublicBytes()) + verifier, err := command.NewVerifier(verifierKey, verifierID, domain, signerKey.PublicBytes()) if err != nil { panic(fmt.Sprintf("Failed to initialize verifier: %s", err)) } @@ -46,7 +46,7 @@ func Example() { // Verifier sends encodedInfo, tag to Signer. // Signer executes: - signer, err := command.NewAuthenticatedSigner(signerKey, verifierId, challenge, encodedInfo, tag) + signer, err := command.NewAuthenticatedSigner(signerKey, verifierID, challenge, encodedInfo, tag) if err != nil { panic(fmt.Sprintf("Failed to initialize signer: %s", err)) } diff --git a/internal/authentication/jwt_test.go b/internal/authentication/jwt_test.go index 8057b72b..2a69e6bd 100644 --- a/internal/authentication/jwt_test.go +++ b/internal/authentication/jwt_test.go @@ -16,7 +16,7 @@ func TestVerify(t *testing.T) { 0x1b, 0x44, 0x98, 0x93, 0xec, } signedToken := "eyJhbGciOiJUZXNsYS5TUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJjb20udGVzbGEudmVoaWNsZS5NM1RFUkFTSElNQUJVQ0swMSIsImZvbyI6ImJhciIsImlzcyI6IkJIZGVMdlZ3MHBMZlFrd0o5dzU5bFdlTlhPZUJKS3l6K1RkYkdZcE5xZm5wOE14dGlBNHVZRzZiQWJENllzWVZEamNhcGRqMzhKeko0YjA2RzBTWWsrdz0ifQ.4Xq2tsynDYOhLBWpnqLJfsAzdOqTOwHkVowI47A-yzVXIQHaawObJvAYRoRs61oVwWiEQ7XYXG8WE_Vz_49eVirAV9NGKymj3HBTTjN5DmViWmFzfaIOXRWKJlE--vzU" - token, err := jwt.Parse(signedToken, func(token *jwt.Token) (interface{}, error) { return pkey, nil }) + token, err := jwt.Parse(signedToken, func(_ *jwt.Token) (interface{}, error) { return pkey, nil }) if err != nil { t.Fatal(err) } diff --git a/internal/authentication/native.go b/internal/authentication/native.go index ebabcbb1..e08b860e 100644 --- a/internal/authentication/native.go +++ b/internal/authentication/native.go @@ -29,37 +29,37 @@ type NativeSession struct { localPublic []byte } -func (b *NativeSession) LocalPublicBytes() []byte { - buff := make([]byte, len(b.localPublic)) - copy(buff, b.localPublic) +func (n *NativeSession) LocalPublicBytes() []byte { + buff := make([]byte, len(n.localPublic)) + copy(buff, n.localPublic) return buff } -func (b *NativeSession) Encrypt(plaintext, associatedData []byte) (nonce, ciphertext, tag []byte, err error) { - if b.gcm == nil { +func (n *NativeSession) Encrypt(plaintext, associatedData []byte) (nonce, ciphertext, tag []byte, err error) { + if n.gcm == nil { err = errors.New("GCM context not initialized") return } - nonce = make([]byte, b.gcm.NonceSize()) + nonce = make([]byte, n.gcm.NonceSize()) if _, err = rand.Read(nonce); err != nil { return } length := len(plaintext) - ciphertext = b.gcm.Seal(nil, nonce, plaintext, associatedData) + ciphertext = n.gcm.Seal(nil, nonce, plaintext, associatedData) tag = ciphertext[length:] ciphertext = ciphertext[:length] return } -func (b *NativeSession) Decrypt(nonce, ciphertext, associatedData, tag []byte) (plaintext []byte, err error) { - if b.gcm == nil { +func (n *NativeSession) Decrypt(nonce, ciphertext, associatedData, tag []byte) (plaintext []byte, err error) { + if n.gcm == nil { err = errors.New("GCM context not initialized") return } ctAndTag := make([]byte, 0, len(ciphertext)+len(tag)) ctAndTag = append(ctAndTag, ciphertext...) ctAndTag = append(ctAndTag, tag...) - plaintext, err = b.gcm.Open(nil, nonce, ctAndTag, associatedData) + plaintext, err = n.gcm.Open(nil, nonce, ctAndTag, associatedData) return } @@ -69,12 +69,12 @@ func (n *NativeSession) subkey(label []byte) []byte { return kdf.Sum(nil) } -func (b *NativeSession) NewHMAC(label string) hash.Hash { - return hmac.New(sha256.New, b.subkey([]byte(label))) +func (n *NativeSession) NewHMAC(label string) hash.Hash { + return hmac.New(sha256.New, n.subkey([]byte(label))) } -func (b *NativeSession) SessionInfoHMAC(id, challenge, encodedInfo []byte) ([]byte, error) { - meta := newMetadataHash(b.NewHMAC(labelSessionInfo)) +func (n *NativeSession) SessionInfoHMAC(id, challenge, encodedInfo []byte) ([]byte, error) { + meta := newMetadataHash(n.NewHMAC(labelSessionInfo)) if err := meta.Add(signatures.Tag_TAG_SIGNATURE_TYPE, []byte{byte(signatures.SignatureType_SIGNATURE_TYPE_HMAC)}); err != nil { return nil, err } @@ -136,12 +136,12 @@ func (n *NativeECDHKey) Exchange(publicBytes []byte) (Session, error) { } func NewECDHPrivateKey(rng io.Reader) (ECDHPrivateKey, error) { - if ecdsaKey, err := ecdsa.GenerateKey(elliptic.P256(), rng); err == nil { - native := &NativeECDHKey{ecdsaKey} - return native, nil - } else { + ecdsaKey, err := ecdsa.GenerateKey(elliptic.P256(), rng) + if err != nil { return nil, err } + native := &NativeECDHKey{ecdsaKey} + return native, nil } func LoadExternalECDHKey(filename string) (ECDHPrivateKey, error) { diff --git a/internal/authentication/peer.go b/internal/authentication/peer.go index 0bd35a54..2a73bd88 100644 --- a/internal/authentication/peer.go +++ b/internal/authentication/peer.go @@ -21,7 +21,7 @@ type Peer struct { domain universal.Domain verifierName []byte counter uint32 - epoch [epochIdLength]byte + epoch [epochIDLength]byte timeZero time.Time session Session } @@ -32,14 +32,14 @@ func (p *Peer) timestamp() uint32 { // extractMetadata populates metadata. func (p *Peer) extractMetadata(meta *metadata, message *universal.RoutableMessage, info sessionInfo, method signatures.SignatureType) error { - meta.Add(signatures.Tag_TAG_SIGNATURE_TYPE, []byte{byte(method)}) + _ = meta.Add(signatures.Tag_TAG_SIGNATURE_TYPE, []byte{byte(method)}) // Authenticate domain. Use domain from message because sender might be using BROADCAST. if x, ok := message.ToDestination.GetSubDestination().(*universal.Destination_Domain); ok { if 0 > x.Domain || x.Domain > 255 { return newError(errCodeInvalidDomain, "domain out of range") } - meta.Add(signatures.Tag_TAG_DOMAIN, []byte{byte(x.Domain)}) + _ = meta.Add(signatures.Tag_TAG_DOMAIN, []byte{byte(x.Domain)}) } else { return newError(errCodeInvalidDomain, "domain missing") } @@ -56,15 +56,15 @@ func (p *Peer) extractMetadata(meta *metadata, message *universal.RoutableMessag return newError(errCodeBadParameter, "out of bounds expiration time") } - meta.Add(signatures.Tag_TAG_EPOCH, p.epoch[:]) - meta.AddUint32(signatures.Tag_TAG_EXPIRES_AT, info.GetExpiresAt()) - meta.AddUint32(signatures.Tag_TAG_COUNTER, info.GetCounter()) + _ = meta.Add(signatures.Tag_TAG_EPOCH, p.epoch[:]) + _ = meta.AddUint32(signatures.Tag_TAG_EXPIRES_AT, info.GetExpiresAt()) + _ = meta.AddUint32(signatures.Tag_TAG_COUNTER, info.GetCounter()) // For backwards compatibility, message flags are only explicitly added to // the metadata hash if at least one of them is set. (If a MITM // clears these bits, the hashes will not match, as desired). if message.Flags > 0 { - meta.AddUint32(signatures.Tag_TAG_FLAGS, message.Flags) + _ = meta.AddUint32(signatures.Tag_TAG_FLAGS, message.Flags) } return nil @@ -104,14 +104,14 @@ func RequestID(message *universal.RoutableMessage) []byte { func (p *Peer) responseMetadata(message *universal.RoutableMessage, id []byte, counter uint32) ([]byte, error) { meta := newMetadata() - meta.Add(signatures.Tag_TAG_SIGNATURE_TYPE, []byte{byte(signatures.SignatureType_SIGNATURE_TYPE_AES_GCM_RESPONSE)}) - meta.Add(signatures.Tag_TAG_DOMAIN, []byte{byte(message.GetFromDestination().GetDomain())}) + _ = meta.Add(signatures.Tag_TAG_SIGNATURE_TYPE, []byte{byte(signatures.SignatureType_SIGNATURE_TYPE_AES_GCM_RESPONSE)}) + _ = meta.Add(signatures.Tag_TAG_DOMAIN, []byte{byte(message.GetFromDestination().GetDomain())}) if err := meta.Add(signatures.Tag_TAG_PERSONALIZATION, p.verifierName); err != nil { return nil, err } - meta.AddUint32(signatures.Tag_TAG_COUNTER, counter) - meta.AddUint32(signatures.Tag_TAG_FLAGS, message.Flags) - meta.Add(signatures.Tag_TAG_REQUEST_HASH, id) - meta.AddUint32(signatures.Tag_TAG_FAULT, uint32(message.GetSignedMessageStatus().GetSignedMessageFault())) + _ = meta.AddUint32(signatures.Tag_TAG_COUNTER, counter) + _ = meta.AddUint32(signatures.Tag_TAG_FLAGS, message.Flags) + _ = meta.Add(signatures.Tag_TAG_REQUEST_HASH, id) + _ = meta.AddUint32(signatures.Tag_TAG_FAULT, uint32(message.GetSignedMessageStatus().GetSignedMessageFault())) return meta.Checksum(nil), nil } diff --git a/internal/authentication/signer_test.go b/internal/authentication/signer_test.go index a7635f41..d55fd378 100644 --- a/internal/authentication/signer_test.go +++ b/internal/authentication/signer_test.go @@ -24,11 +24,11 @@ func TestUpdateSessionInfo(t *testing.T) { } func TestBadSessionInfoProto(t *testing.T) { - verifierId := []byte("foo") + verifierID := []byte("foo") verifierKey, signerKey := getVerifierAndSignerKeys(t) dispatcher := Dispatcher{signerKey} - verifier, err := NewVerifier(verifierKey, verifierId, universal.Domain_DOMAIN_VEHICLE_SECURITY, signerKey.PublicBytes()) + verifier, err := NewVerifier(verifierKey, verifierID, universal.Domain_DOMAIN_VEHICLE_SECURITY, signerKey.PublicBytes()) if err != nil { t.Fatalf("Failed to generate verifier") } @@ -39,7 +39,7 @@ func TestBadSessionInfoProto(t *testing.T) { t.Fatalf("Failed to get session info: %s", err) } - signer, err := dispatcher.ConnectAuthenticated(verifierId, challenge, info, tag) + signer, err := dispatcher.ConnectAuthenticated(verifierID, challenge, info, tag) if err != nil { t.Errorf("Error connecting to verifier: %s", err) } @@ -48,7 +48,7 @@ func TestBadSessionInfoProto(t *testing.T) { err = signer.UpdateSignedSessionInfo(challenge, info, tag) checkError(t, err, errCodeInvalidSignature) - _, err = dispatcher.ConnectAuthenticated(verifierId, challenge, info, tag) + _, err = dispatcher.ConnectAuthenticated(verifierID, challenge, info, tag) checkError(t, err, errCodeDecoding) if tag, err = verifier.session.SessionInfoHMAC(verifier.verifierName, challenge, info); err != nil { diff --git a/internal/authentication/verifier.go b/internal/authentication/verifier.go index 05e1d7dd..81594484 100644 --- a/internal/authentication/verifier.go +++ b/internal/authentication/verifier.go @@ -65,10 +65,9 @@ func (v *Verifier) rotateEpochIfNeeded(force bool) error { if _, err := rand.Read(v.epoch[:]); err != nil { v.counter = 0xFFFFFFFF return newError(errCodeInternal, "RNG failure") - } else { - v.timeZero = time.Now() - v.counter = 0 } + v.timeZero = time.Now() + v.counter = 0 } return nil } diff --git a/internal/authentication/window_test.go b/internal/authentication/window_test.go index b799e4bd..61183320 100644 --- a/internal/authentication/window_test.go +++ b/internal/authentication/window_test.go @@ -15,7 +15,7 @@ func TestSlidingWindow(t *testing.T) { } tests := []windowTest{ // Update should succeed because newCounter is greater than all previous counters. - windowTest{ + { counter: 100, window: uint64((1 << 0) | (1 << 5)), newCounter: 101, @@ -25,7 +25,7 @@ func TestSlidingWindow(t *testing.T) { }, // Update should succeed because newCounter is greater than all previous counters. // In this test, some messages were skipped and so the expectedUpdatedWindow shifts further. - windowTest{ + { counter: 100, window: uint64((1 << 0) | (1 << 5)), newCounter: 103, @@ -35,7 +35,7 @@ func TestSlidingWindow(t *testing.T) { }, // Update should succeed because newCounter is greater than all previous counters. // In this test, the previous counter doesn't fit in sliding window. - windowTest{ + { counter: 100, window: uint64((1 << 0) | (1 << 5)), newCounter: 500, @@ -44,7 +44,7 @@ func TestSlidingWindow(t *testing.T) { expectedOk: true, }, // Update should succeed because newCounter falls in window but isn't set. - windowTest{ + { counter: 100, window: uint64((1 << 0) | (1 << 5)), newCounter: 98, @@ -53,7 +53,7 @@ func TestSlidingWindow(t *testing.T) { expectedOk: true, }, // Update should fail because newCounter falls in window and is already set. - windowTest{ + { counter: 100, window: uint64((1 << 0) | (1 << 5)), newCounter: 99, @@ -62,7 +62,7 @@ func TestSlidingWindow(t *testing.T) { expectedOk: false, }, // Update should fail because newCounter falls outside of window and freshness cannot be validated. - windowTest{ + { counter: 100, window: uint64((1 << 0) | (1 << 5)), newCounter: 3, @@ -71,7 +71,7 @@ func TestSlidingWindow(t *testing.T) { expectedOk: false, }, // Update should fail because newCounter == counter. - windowTest{ + { counter: 100, window: uint64((1 << 0) | (1 << 5)), newCounter: 100, diff --git a/internal/dispatcher/dispatcher_test.go b/internal/dispatcher/dispatcher_test.go index 2069994a..57bb5429 100644 --- a/internal/dispatcher/dispatcher_test.go +++ b/internal/dispatcher/dispatcher_test.go @@ -249,7 +249,7 @@ func (d *dummyConnector) EnqueueSendError(err error) { d.lock.Unlock() } -func (d *dummyConnector) Send(ctx context.Context, buffer []byte) error { +func (d *dummyConnector) Send(_ context.Context, buffer []byte) error { var message universal.RoutableMessage if !d.AckRequests { return errTimeout @@ -898,7 +898,7 @@ func TestNoValidHandshakeResponse(t *testing.T) { const maxCallbacks = 5 callbackCount := 0 - conn.callback = func(d *dummyConnector, message *universal.RoutableMessage) ([]byte, bool) { + conn.callback = func(_ *dummyConnector, message *universal.RoutableMessage) ([]byte, bool) { callbackCount++ // caller holds d.lock reply := initReply(message) reply.Payload = &universal.RoutableMessage_SessionInfo{} diff --git a/internal/schnorr/schnorr.go b/internal/schnorr/schnorr.go index 0569415a..26ea8067 100644 --- a/internal/schnorr/schnorr.go +++ b/internal/schnorr/schnorr.go @@ -79,7 +79,6 @@ func Verify(publicKeyBytes, message, signature []byte) error { pX, pY = p256.Add(tempX, tempY, pX, pY) if pX.Cmp(&vX) == 0 && pY.Cmp(&vY) == 0 { return nil - } else { - return ErrInvalidSignature } + return ErrInvalidSignature } diff --git a/pkg/account/account.go b/pkg/account/account.go index e69c6fd4..a0f82a89 100644 --- a/pkg/account/account.go +++ b/pkg/account/account.go @@ -149,7 +149,7 @@ func New(oauthToken, userAgent string) (*Account, error) { // an AddKeyRequest; see documentation in [pkg/github.com/teslamotors/vehicle-command/pkg/vehicle]. The // sessions parameter may also be nil, but providing a cache.SessionCache avoids a round-trip // handshake with the Vehicle in subsequent connections. -func (a *Account) GetVehicle(ctx context.Context, vin string, privateKey authentication.ECDHPrivateKey, sessions *cache.SessionCache) (*vehicle.Vehicle, error) { +func (a *Account) GetVehicle(_ context.Context, vin string, privateKey authentication.ECDHPrivateKey, sessions *cache.SessionCache) (*vehicle.Vehicle, error) { conn := inet.NewConnection(vin, a.authHeader, a.Host, a.UserAgent) car, err := vehicle.NewVehicle(conn, privateKey, sessions) if err != nil { @@ -176,7 +176,9 @@ func (a *Account) Get(ctx context.Context, endpoint string) ([]byte, error) { if err != nil { return nil, fmt.Errorf("error fetching %s: %w", endpoint, err) } - defer response.Body.Close() + defer func() { + _ = response.Body.Close() + }() if response.StatusCode != http.StatusOK { err := fmt.Errorf("http error when sending command to %s: %s", url, response.Status) return nil, err diff --git a/pkg/cache/cache.go b/pkg/cache/cache.go index 7ade248c..35f14ede 100644 --- a/pkg/cache/cache.go +++ b/pkg/cache/cache.go @@ -46,7 +46,10 @@ func ImportFromFile(filename string) (*SessionCache, error) { if err != nil { return nil, err } - defer file.Close() + + defer func() { + file.Close() + }() return Import(file) } @@ -65,7 +68,9 @@ func (c *SessionCache) ExportToFile(filename string) error { if err != nil { return err } - defer file.Close() + defer func() { + file.Close() + }() return c.Export(file) } diff --git a/pkg/cache/cache_test.go b/pkg/cache/cache_test.go index 0116717b..3fd0859b 100644 --- a/pkg/cache/cache_test.go +++ b/pkg/cache/cache_test.go @@ -78,22 +78,22 @@ func TestEviction(t *testing.T) { c.MaxEntries = 5 // Note that generateTestSessions(n) adds an entry with timestamp n, and entries are evicted // based on timestamp, not the order in which they were added to the cache. - c.Update("7", generateTestSessions(7)) - c.Update("4", generateTestSessions(4)) - c.Update("5", generateTestSessions(5)) - c.Update("3", generateTestSessions(3)) - c.Update("6", generateTestSessions(6)) + _ = c.Update("7", generateTestSessions(7)) + _ = c.Update("4", generateTestSessions(4)) + _ = c.Update("5", generateTestSessions(5)) + _ = c.Update("3", generateTestSessions(3)) + _ = c.Update("6", generateTestSessions(6)) verifyCache(t, c, []int{3, 4, 5, 6, 7}) // Duplicate key updated in place - c.Update("5", generateTestSessions(5)) + _ = c.Update("5", generateTestSessions(5)) verifyCache(t, c, []int{3, 4, 5, 6, 7}) // Evicts oldest entry - c.Update("8", generateTestSessions(8)) + _ = c.Update("8", generateTestSessions(8)) verifyCache(t, c, []int{4, 5, 6, 7, 8}) // Older entry doesn't evict newer entry - c.Update("1", generateTestSessions(1)) + _ = c.Update("1", generateTestSessions(1)) verifyCache(t, c, []int{4, 5, 6, 7, 8}) } diff --git a/pkg/cache/example_test.go b/pkg/cache/example_test.go index f905030d..49b78e5e 100644 --- a/pkg/cache/example_test.go +++ b/pkg/cache/example_test.go @@ -51,7 +51,9 @@ func Example() { fmt.Printf("Error updating session cache: %s\n", err) return } - myCache.ExportToFile(cacheFilename) + if err := myCache.ExportToFile(cacheFilename); err != nil { + fmt.Printf("Error saving session cache: %s\n", err) + } }() // Interact with car diff --git a/pkg/cli/config.go b/pkg/cli/config.go index 9e2aec02..f67c7d62 100644 --- a/pkg/cli/config.go +++ b/pkg/cli/config.go @@ -297,7 +297,7 @@ func (c *Config) UpdateCachedSessions(v *vehicle.Vehicle) { if c.CacheFilename == "" || c.sessions == nil { return } - v.UpdateCachedSessions(c.sessions) + _ = v.UpdateCachedSessions(c.sessions) if err := c.sessions.ExportToFile(c.CacheFilename); err != nil { log.Error("Error updating cache: %s", err) } diff --git a/pkg/cli/keyring.go b/pkg/cli/keyring.go index ce3f7fbc..c3f7cf95 100644 --- a/pkg/cli/keyring.go +++ b/pkg/cli/keyring.go @@ -58,9 +58,8 @@ func (c *Config) getPassword(prompt string) (string, error) { fd = int(os.Stderr.Fd()) if !term.IsTerminal(fd) { return "", fmt.Errorf("no terminal output available for password prompt") - } else { - w = os.Stderr } + w = os.Stderr } else { w = os.Stdout } diff --git a/pkg/connector/ble/ble.go b/pkg/connector/ble/ble.go index 99c80bde..1e665268 100644 --- a/pkg/connector/ble/ble.go +++ b/pkg/connector/ble/ble.go @@ -82,8 +82,8 @@ func (c *Connection) flush() bool { } func (c *Connection) Close() { - c.client.ClearSubscriptions() - c.client.CancelConnection() + _ = c.client.ClearSubscriptions() + _ = c.client.CancelConnection() } func (c *Connection) AllowedLatency() time.Duration { @@ -100,7 +100,7 @@ func (c *Connection) rx(p []byte) { } } -func (c *Connection) Send(ctx context.Context, buffer []byte) error { +func (c *Connection) Send(_ context.Context, buffer []byte) error { c.lock.Lock() defer c.lock.Unlock() diff --git a/pkg/connector/inet/inet.go b/pkg/connector/inet/inet.go index 860b8a25..6372965d 100644 --- a/pkg/connector/inet/inet.go +++ b/pkg/connector/inet/inet.go @@ -54,26 +54,26 @@ The regular expression below extracts domains from HTTP bodies: */ var baseDomainRE = regexp.MustCompile(`use base URL: https://([-a-z0-9.]*)`) -type HttpError struct { +type HTTPError struct { Code int Message string } -func (e *HttpError) Error() string { +func (e *HTTPError) Error() string { if e.Message == "" { return http.StatusText(e.Code) } return e.Message } -func (e *HttpError) MayHaveSucceeded() bool { +func (e *HTTPError) MayHaveSucceeded() bool { if e.Code >= 400 && e.Code < 500 { return false } return e.Code != http.StatusServiceUnavailable } -func (e *HttpError) Temporary() bool { +func (e *HTTPError) Temporary() bool { return e.Code == http.StatusServiceUnavailable || e.Code == http.StatusGatewayTimeout || e.Code == http.StatusRequestTimeout || @@ -105,7 +105,9 @@ func SendFleetAPICommand(ctx context.Context, client *http.Client, userAgent, au if err != nil { return nil, &protocol.CommandError{Err: err, PossibleSuccess: false, PossibleTemporary: true} } - defer result.Body.Close() + defer func() { + _ = result.Body.Close() + }() body = make([]byte, connector.MaxResponseLength+1) body, err = ReadWithContext(ctx, result.Body, body) @@ -130,7 +132,7 @@ func SendFleetAPICommand(ctx context.Context, client *http.Client, userAgent, au return nil, ErrVehicleNotAwake } } - return nil, &HttpError{Code: result.StatusCode, Message: string(body)} + return nil, &HTTPError{Code: result.StatusCode, Message: string(body)} } func ValidTeslaDomainSuffix(domain string) bool { @@ -143,7 +145,7 @@ func (c *Connection) SendFleetAPICommand(ctx context.Context, endpoint string, c url := fmt.Sprintf("https://%s/%s", c.serverURL, endpoint) rsp, err := SendFleetAPICommand(ctx, c.client, c.UserAgent, c.authHeader, url, command) if err != nil { - var httpErr *HttpError + var httpErr *HTTPError if errors.As(err, &httpErr) && httpErr.Code == http.StatusMisdirectedRequest { matches := baseDomainRE.FindStringSubmatch(httpErr.Message) if len(matches) == 2 && ValidTeslaDomainSuffix(matches[1]) { diff --git a/pkg/connector/inet/inet_test.go b/pkg/connector/inet/inet_test.go index 2bc9e9a5..80087fa2 100644 --- a/pkg/connector/inet/inet_test.go +++ b/pkg/connector/inet/inet_test.go @@ -11,7 +11,7 @@ import ( ) func TestSendAfterClose(t *testing.T) { - server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.Write([]byte(`{"response": ""}`)) })) defer server.Close() diff --git a/pkg/protocol/key.go b/pkg/protocol/key.go index eb3f65e4..e656f0ea 100644 --- a/pkg/protocol/key.go +++ b/pkg/protocol/key.go @@ -49,7 +49,9 @@ func LoadPublicKey(filename string) (*ecdh.PublicKey, error) { if err != nil { return nil, err } - defer file.Close() + defer func() { + file.Close() + }() pemBlock, err := io.ReadAll(file) if err != nil { return nil, err diff --git a/pkg/proxy/command.go b/pkg/proxy/command.go index 4b968e6b..1626b14c 100644 --- a/pkg/proxy/command.go +++ b/pkg/proxy/command.go @@ -492,7 +492,7 @@ func ExtractCommandAction(ctx context.Context, command string, params RequestPar return nil, errors.New("command must be 'vent' or 'close'") } default: - return nil, &inet.HttpError{Code: http.StatusBadRequest, Message: "{\"response\":null,\"error\":\"invalid_command\",\"error_description\":\"\"}"} + return nil, &inet.HTTPError{Code: http.StatusBadRequest, Message: "{\"response\":null,\"error\":\"invalid_command\",\"error_description\":\"\"}"} } } diff --git a/pkg/proxy/command_test.go b/pkg/proxy/command_test.go index ccb3baf7..e09e7bf7 100644 --- a/pkg/proxy/command_test.go +++ b/pkg/proxy/command_test.go @@ -32,7 +32,7 @@ func TestExtractCommandAction(t *testing.T) { {"adjust_volume", params, func(v *vehicle.Vehicle) error { return v.SetVolume(ctx, 0.0) }, nil}, {"adjust_volume", nil, nil, &protocol.NominalError{Details: fmt.Errorf("missing volume param")}}, {"remote_boombox", params, nil, proxy.ErrCommandNotImplemented}, - {"invalid_command", params, nil, &inet.HttpError{Code: http.StatusBadRequest, Message: "{\"response\":null,\"error\":\"invalid_command\",\"error_description\":\"\"}"}}, + {"invalid_command", params, nil, &inet.HTTPError{Code: http.StatusBadRequest, Message: "{\"response\":null,\"error\":\"invalid_command\",\"error_description\":\"\"}"}}, } for _, test := range tests { diff --git a/pkg/proxy/proxy.go b/pkg/proxy/proxy.go index 79834d30..9d9b93ee 100644 --- a/pkg/proxy/proxy.go +++ b/pkg/proxy/proxy.go @@ -10,7 +10,6 @@ import ( "net" "net/http" "net/url" - "regexp" "slices" "strings" "sync" @@ -36,7 +35,6 @@ const ( MaxAttempts = 2 ) -var baseDomainRE = regexp.MustCompile(`use base URL: https://([-a-z0-9.]*)`) var h2Prefix = "h2=https://" func getAccount(req *http.Request) (*account.Account, error) { @@ -112,7 +110,7 @@ func (p *Proxy) unlockVIN(vin string) { // // Vehicles must have the public part of skey enrolled on their keychains. (This is a // command-authentication key, not a TLS key.) -func New(ctx context.Context, skey protocol.ECDHPrivateKey, cacheSize int) (*Proxy, error) { +func New(_ context.Context, skey protocol.ECDHPrivateKey, cacheSize int) (*Proxy, error) { return &Proxy{ Timeout: DefaultTimeout, commandKey: skey, @@ -135,7 +133,7 @@ type carResponse struct { func writeJSONError(w http.ResponseWriter, code int, err error) { reply := Response{} - var httpErr *inet.HttpError + var httpErr *inet.HTTPError var jsonBytes []byte if errors.As(err, &httpErr) { code = httpErr.Code @@ -235,7 +233,7 @@ func (p *Proxy) forwardRequest(acct *account.Account, w http.ResponseWriter, req limitedReader := &io.LimitedReader{R: result.Body, N: MaxResponseLength + 1} body, err := io.ReadAll(limitedReader) - result.Body.Close() + _ = result.Body.Close() if err != nil { writeJSONError(w, http.StatusBadGateway, err) @@ -276,7 +274,7 @@ func (p *Proxy) forwardRequest(acct *account.Account, w http.ResponseWriter, req return } - attempts += 1 + attempts++ if attempts == MaxAttempts { writeJSONError(w, http.StatusBadGateway, protocol.NewError("max retry exhausted", false, false)) } @@ -335,7 +333,9 @@ func (p *Proxy) ServeHTTP(w http.ResponseWriter, req *http.Request) { func (p *Proxy) handleFleetTelemetryConfig(acct *account.Account, w http.ResponseWriter, req *http.Request) { log.Info("Processing fleet telemetry configuration...") - defer req.Body.Close() + defer func() { + _ = req.Body.Close() + }() body, err := io.ReadAll(req.Body) if err != nil { writeJSONError(w, http.StatusBadRequest, fmt.Errorf("could not read request body: %s", err)) @@ -413,7 +413,9 @@ func (p *Proxy) handleVehicleCommand(acct *account.Account, w http.ResponseWrite writeJSONError(w, http.StatusInternalServerError, err) return err } - defer car.UpdateCachedSessions(p.sessions) + defer func() { + _ = car.UpdateCachedSessions(p.sessions) + }() if err = commandToExecuteFunc(car); err == ErrCommandUseRESTAPI { return err @@ -460,11 +462,11 @@ func extractCommandAction(ctx context.Context, req *http.Request, command string var params RequestParameters body, err := io.ReadAll(req.Body) if err != nil { - return nil, &inet.HttpError{Code: http.StatusBadRequest, Message: "could not read request body"} + return nil, &inet.HTTPError{Code: http.StatusBadRequest, Message: "could not read request body"} } if len(body) > 0 { if err := json.Unmarshal(body, ¶ms); err != nil { - return nil, &inet.HttpError{Code: http.StatusBadRequest, Message: "error occurred while parsing request parameters"} + return nil, &inet.HTTPError{Code: http.StatusBadRequest, Message: "error occurred while parsing request parameters"} } } diff --git a/pkg/vehicle/climate.go b/pkg/vehicle/climate.go index 8aeecdac..3919cd85 100644 --- a/pkg/vehicle/climate.go +++ b/pkg/vehicle/climate.go @@ -24,7 +24,7 @@ func (v *Vehicle) SetSeatCooler(ctx context.Context, level Level, seat SeatPosit VehicleActionMsg: &carserver.VehicleAction_HvacSeatCoolerActions{ HvacSeatCoolerActions: &carserver.HvacSeatCoolerActions{ HvacSeatCoolerAction: []*carserver.HvacSeatCoolerActions_HvacSeatCoolerAction{ - &carserver.HvacSeatCoolerActions_HvacSeatCoolerAction{ + { SeatCoolerLevel: carserver.HvacSeatCoolerActions_HvacSeatCoolerLevel_E(level + 1), SeatPosition: protoSeat, }, diff --git a/pkg/vehicle/state.go b/pkg/vehicle/state.go index 10d3cecf..e988faf6 100644 --- a/pkg/vehicle/state.go +++ b/pkg/vehicle/state.go @@ -37,18 +37,18 @@ const ( func (c StateCategory) submessage() *carserver.GetVehicleData { messages := map[StateCategory]*carserver.GetVehicleData{ - StateCategoryCharge: &carserver.GetVehicleData{GetChargeState: &carserver.GetChargeState{}}, - StateCategoryClimate: &carserver.GetVehicleData{GetClimateState: &carserver.GetClimateState{}}, - StateCategoryDrive: &carserver.GetVehicleData{GetDriveState: &carserver.GetDriveState{}}, - StateCategoryLocation: &carserver.GetVehicleData{GetLocationState: &carserver.GetLocationState{}}, - StateCategoryClosures: &carserver.GetVehicleData{GetClosuresState: &carserver.GetClosuresState{}}, - StateCategoryChargeSchedule: &carserver.GetVehicleData{GetChargeScheduleState: &carserver.GetChargeScheduleState{}}, - StateCategoryPreconditioningSchedule: &carserver.GetVehicleData{GetPreconditioningScheduleState: &carserver.GetPreconditioningScheduleState{}}, - StateCategoryTirePressure: &carserver.GetVehicleData{GetTirePressureState: &carserver.GetTirePressureState{}}, - StateCategoryMedia: &carserver.GetVehicleData{GetMediaState: &carserver.GetMediaState{}}, - StateCategoryMediaDetail: &carserver.GetVehicleData{GetMediaDetailState: &carserver.GetMediaDetailState{}}, - StateCategorySoftwareUpdate: &carserver.GetVehicleData{GetSoftwareUpdateState: &carserver.GetSoftwareUpdateState{}}, - StateCategoryParentalControls: &carserver.GetVehicleData{GetParentalControlsState: &carserver.GetParentalControlsState{}}, + StateCategoryCharge: {GetChargeState: &carserver.GetChargeState{}}, + StateCategoryClimate: {GetClimateState: &carserver.GetClimateState{}}, + StateCategoryDrive: {GetDriveState: &carserver.GetDriveState{}}, + StateCategoryLocation: {GetLocationState: &carserver.GetLocationState{}}, + StateCategoryClosures: {GetClosuresState: &carserver.GetClosuresState{}}, + StateCategoryChargeSchedule: {GetChargeScheduleState: &carserver.GetChargeScheduleState{}}, + StateCategoryPreconditioningSchedule: {GetPreconditioningScheduleState: &carserver.GetPreconditioningScheduleState{}}, + StateCategoryTirePressure: {GetTirePressureState: &carserver.GetTirePressureState{}}, + StateCategoryMedia: {GetMediaState: &carserver.GetMediaState{}}, + StateCategoryMediaDetail: {GetMediaDetailState: &carserver.GetMediaDetailState{}}, + StateCategorySoftwareUpdate: {GetSoftwareUpdateState: &carserver.GetSoftwareUpdateState{}}, + StateCategoryParentalControls: {GetParentalControlsState: &carserver.GetParentalControlsState{}}, } msg, ok := messages[c] if !ok { diff --git a/pkg/vehicle/vcsec.go b/pkg/vehicle/vcsec.go index b1618cd8..22293f55 100644 --- a/pkg/vehicle/vcsec.go +++ b/pkg/vehicle/vcsec.go @@ -125,7 +125,7 @@ func (v *Vehicle) getVCSECInfo(ctx context.Context, requestType vcsec.Informatio return nil, err } - done := func(v *vcsec.FromVCSECMessage) (bool, error) { return true, nil } + done := func(_ *vcsec.FromVCSECMessage) (bool, error) { return true, nil } return v.getVCSECResult(ctx, encodedPayload, connector.AuthMethodNone, done) } diff --git a/pkg/vehicle/vehicle.go b/pkg/vehicle/vehicle.go index 49c051fc..6272e7f7 100644 --- a/pkg/vehicle/vehicle.go +++ b/pkg/vehicle/vehicle.go @@ -259,9 +259,8 @@ func (v *Vehicle) Send(ctx context.Context, domain universal.Domain, payload []b func (v *Vehicle) Wakeup(ctx context.Context) error { if oapi, ok := v.conn.(connector.FleetAPIConnector); ok { return oapi.Wakeup(ctx) - } else { - return v.wakeupRKE(ctx) } + return v.wakeupRKE(ctx) } func (v *Vehicle) UpdateCachedSessions(c *cache.SessionCache) error { diff --git a/pkg/vehicle/vehicle_mock_test.go b/pkg/vehicle/vehicle_mock_test.go index 45940099..4c60cc3c 100644 --- a/pkg/vehicle/vehicle_mock_test.go +++ b/pkg/vehicle/vehicle_mock_test.go @@ -4,6 +4,6 @@ import "context" type MockVehicle struct{} -func (v *MockVehicle) SetVolume(ctx context.Context, volume float32) error { +func (v *MockVehicle) SetVolume(_ context.Context, _ float32) error { return nil } diff --git a/pkg/vehicle/vehicle_test.go b/pkg/vehicle/vehicle_test.go index 7bb5379f..c281a982 100644 --- a/pkg/vehicle/vehicle_test.go +++ b/pkg/vehicle/vehicle_test.go @@ -46,7 +46,7 @@ type testSender struct { ConnectionErrors []error } -func (s *testSender) StartSessions(ctx context.Context, domains []universal.Domain) error { +func (s *testSender) StartSessions(_ context.Context, _ []universal.Domain) error { if len(s.ConnectionErrors) > 0 { err := s.ConnectionErrors[0] s.ConnectionErrors = s.ConnectionErrors[1:] @@ -59,7 +59,7 @@ func (s *testSender) Cache() []dispatcher.CacheEntry { return nil } -func (s *testSender) LoadCache(entries []dispatcher.CacheEntry) error { +func (s *testSender) LoadCache(_ []dispatcher.CacheEntry) error { return nil } @@ -89,7 +89,7 @@ func (s *testSender) EnqueueResponse(t *testing.T, message *universal.RoutableMe } } -func (s *testSender) SetMaxLatency(latency time.Duration) {} +func (s *testSender) SetMaxLatency(_ time.Duration) {} func newTestVehicle() (*Vehicle, *testSender) { dispatch := newTestSender() @@ -102,9 +102,9 @@ func newTestSender() *testSender { } } -func (t *testSender) Start(ctx context.Context) error { +func (s *testSender) Start(ctx context.Context) error { ready := make(chan struct{}) - go t.Listen(ready) + go s.Listen(ready) select { case <-ready: return nil @@ -113,33 +113,33 @@ func (t *testSender) Start(ctx context.Context) error { } } -func (t *testSender) Listen(ready chan<- struct{}) { - t.lock.Lock() - t.listening = true +func (s *testSender) Listen(ready chan<- struct{}) { + s.lock.Lock() + s.listening = true if ready != nil { close(ready) } - t.lock.Unlock() + s.lock.Unlock() } -func (t *testSender) Stop() { - t.lock.Lock() - t.listening = false - t.lock.Unlock() +func (s *testSender) Stop() { + s.lock.Lock() + s.listening = false + s.lock.Unlock() } -func (t *testSender) Send(ctx context.Context, message *universal.RoutableMessage, authorize connector.AuthMethod) (protocol.Receiver, error) { - t.lock.Lock() - defer t.lock.Unlock() - if t.SendError != nil { - return nil, t.SendError +func (s *testSender) Send(_ context.Context, _ *universal.RoutableMessage, _ connector.AuthMethod) (protocol.Receiver, error) { + s.lock.Lock() + defer s.lock.Unlock() + if s.SendError != nil { + return nil, s.SendError } - if len(t.errQueue) > 0 { - err := t.errQueue[0] - t.errQueue = t.errQueue[1:] + if len(s.errQueue) > 0 { + err := s.errQueue[0] + s.errQueue = s.errQueue[1:] return nil, err } - return &testReceiever{parent: t}, nil + return &testReceiever{parent: s}, nil } func TestVehicleStartSessionFailed(t *testing.T) {