From adcf0827ee185ff28b30b54266eafa34a5671e65 Mon Sep 17 00:00:00 2001 From: James Houlahan Date: Fri, 15 Jan 2021 10:31:19 +0100 Subject: [PATCH] feat: report corrupt update files --- cmd/launcher/main.go | 18 ++++++++++++++---- internal/app/base/base.go | 2 +- internal/versioner/version.go | 13 +++++++++++-- pkg/sentry/reporter.go | 33 ++++++++++++++++++++++++--------- 4 files changed, 50 insertions(+), 16 deletions(-) diff --git a/cmd/launcher/main.go b/cmd/launcher/main.go index b74cd345..e2b5fa53 100644 --- a/cmd/launcher/main.go +++ b/cmd/launcher/main.go @@ -18,6 +18,7 @@ package main import ( + "fmt" "os" "os/exec" "path/filepath" @@ -43,9 +44,9 @@ var ( ) func main() { // nolint[funlen] - sentryReporter := sentry.NewReporter(appName, constants.Version) + reporter := sentry.NewReporter(appName, constants.Version) - crashHandler := crash.NewHandler(sentryReporter.Report) + crashHandler := crash.NewHandler(reporter.ReportException) defer crashHandler.HandlePanic() locationsProvider, err := locations.NewDefaultProvider(filepath.Join(constants.VendorName, ConfigName)) @@ -84,7 +85,7 @@ func main() { // nolint[funlen] versioner := versioner.New(updatesPath) - exe, err := getPathToExecutable(ExeName, versioner, kr) + exe, err := getPathToExecutable(ExeName, versioner, kr, reporter) if err != nil { if exe, err = getFallbackExecutable(ExeName, versioner); err != nil { logrus.WithError(err).Fatal("Failed to find any launchable executable") @@ -140,7 +141,12 @@ func appendLauncherPath(path string, args []string) []string { return res } -func getPathToExecutable(name string, versioner *versioner.Versioner, kr *crypto.KeyRing) (string, error) { +func getPathToExecutable( + name string, + versioner *versioner.Versioner, + kr *crypto.KeyRing, + reporter *sentry.Reporter, +) (string, error) { versions, err := versioner.ListVersions() if err != nil { return "", errors.Wrap(err, "failed to list available versions") @@ -152,6 +158,10 @@ func getPathToExecutable(name string, versioner *versioner.Versioner, kr *crypto if err := version.VerifyFiles(kr); err != nil { vlog.WithError(err).Error("Files failed verification and will be removed") + if err := reporter.ReportMessage(fmt.Sprintf("version %v failed verification: %v", version, err)); err != nil { + vlog.WithError(err).Error("Failed to report corrupt update files") + } + if err := version.Remove(); err != nil { vlog.WithError(err).Error("Failed to remove files") } diff --git a/internal/app/base/base.go b/internal/app/base/base.go index 37afd7c5..53ec56bb 100644 --- a/internal/app/base/base.go +++ b/internal/app/base/base.go @@ -93,7 +93,7 @@ func New( // nolint[funlen] sentryReporter := sentry.NewReporter(appName, constants.Version) crashHandler := crash.NewHandler( - sentryReporter.Report, + sentryReporter.ReportException, crash.ShowErrorNotification(appName), ) defer crashHandler.HandlePanic() diff --git a/internal/versioner/version.go b/internal/versioner/version.go index c2962fda..376f0eb6 100644 --- a/internal/versioner/version.go +++ b/internal/versioner/version.go @@ -19,7 +19,8 @@ package versioner import ( "bytes" - "errors" + "encoding/base64" + "fmt" "io/ioutil" "os" "path/filepath" @@ -50,6 +51,10 @@ func (v Versions) Swap(i, j int) { v[i], v[j] = v[j], v[i] } +func (v *Version) String() string { + return fmt.Sprintf("%v", v.version) +} + // VerifyFiles verifies all files in the version directory. func (v *Version) VerifyFiles(kr *crypto.KeyRing) error { fileBytes, err := ioutil.ReadFile(filepath.Join(v.path, sumFile)) // nolint[gosec] @@ -76,7 +81,11 @@ func (v *Version) VerifyFiles(kr *crypto.KeyRing) error { } if !bytes.Equal(sum, fileBytes) { - return errors.New("sum mismatch") + return fmt.Errorf( + "sum mismatch: %v should be %v", + base64.RawStdEncoding.EncodeToString(sum), + base64.RawStdEncoding.EncodeToString(fileBytes), + ) } return nil diff --git a/pkg/sentry/reporter.go b/pkg/sentry/reporter.go index 9f2902e6..051b9cc6 100644 --- a/pkg/sentry/reporter.go +++ b/pkg/sentry/reporter.go @@ -67,8 +67,30 @@ func (r *Reporter) SetUserAgentProvider(uap userAgentProvider) { r.uap = uap } +func (r *Reporter) ReportException(i interface{}) error { + err := fmt.Errorf("recover: %v", i) + + return r.scopedReport(func() { + if eventID := sentry.CaptureException(err); eventID != nil { + logrus.WithError(err). + WithField("reportID", *eventID). + Warn("Captured exception") + } + }) +} + +func (r *Reporter) ReportMessage(msg string) error { + return r.scopedReport(func() { + if eventID := sentry.CaptureMessage(msg); eventID != nil { + logrus.WithField("message", msg). + WithField("reportID", *eventID). + Warn("Captured message") + } + }) +} + // Report reports a sentry crash with stacktrace from all goroutines. -func (r *Reporter) Report(i interface{}) (err error) { +func (r *Reporter) scopedReport(doReport func()) error { SkipDuringUnwind() if os.Getenv("PROTONMAIL_ENV") == "dev" { @@ -83,8 +105,6 @@ func (r *Reporter) Report(i interface{}) (err error) { userAgent = runtime.GOOS } - reportErr := fmt.Errorf("recover: %v", i) - tags := map[string]string{ "OS": runtime.GOOS, "Client": r.appName, @@ -93,21 +113,16 @@ func (r *Reporter) Report(i interface{}) (err error) { "UserID": "", } - var reportID string sentry.WithScope(func(scope *sentry.Scope) { SkipDuringUnwind() scope.SetTags(tags) - if eventID := sentry.CaptureException(reportErr); eventID != nil { - reportID = string(*eventID) - } + doReport() }) if !sentry.Flush(time.Second * 10) { return errors.New("failed to report sentry error") } - logrus.WithField("error", reportErr).WithField("id", reportID).Warn("Sentry error reported") - return nil }