From d19a93deced7b7a4c79fbf3e758f7cc68eee9479 Mon Sep 17 00:00:00 2001 From: kolaente Date: Fri, 21 Mar 2025 17:48:08 +0100 Subject: [PATCH] fix(migration): check if uploaded csv is empty --- pkg/modules/migration/errors.go | 19 +++++++++++++++ pkg/modules/migration/ticktick/ticktick.go | 27 ++++++++++++++++++++-- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/pkg/modules/migration/errors.go b/pkg/modules/migration/errors.go index 06b7a4112..bc6c78182 100644 --- a/pkg/modules/migration/errors.go +++ b/pkg/modules/migration/errors.go @@ -40,3 +40,22 @@ func (err *ErrNotAZipFile) HTTPError() web.HTTPError { Message: "The provided file is not a valid zip file.", } } + +// ErrFileIsEmpty represents a "ErrFileIsEmpty" kind of error. +type ErrFileIsEmpty struct{} + +func (err *ErrFileIsEmpty) Error() string { + return "The provided file does not contain any data" +} + +// ErrCodeFileIsEmpty holds the unique world-error code of this error +const ErrCodeFileIsEmpty = 14002 + +// HTTPError holds the http error description +func (err *ErrFileIsEmpty) HTTPError() web.HTTPError { + return web.HTTPError{ + HTTPCode: http.StatusBadRequest, + Code: ErrCodeFileIsEmpty, + Message: "The provided file does not contain any data.", + } +} diff --git a/pkg/modules/migration/ticktick/ticktick.go b/pkg/modules/migration/ticktick/ticktick.go index 33a24d975..95b7b2fae 100644 --- a/pkg/modules/migration/ticktick/ticktick.go +++ b/pkg/modules/migration/ticktick/ticktick.go @@ -190,16 +190,39 @@ func newLineSkipDecoder(r io.Reader, linesToSkip int) gocsv.SimpleDecoder { // @Failure 500 {object} models.Message "Internal server error" // @Router /migration/ticktick/migrate [post] func (m *Migrator) Migrate(user *user.User, file io.ReaderAt, size int64) error { + // Check if file is empty + if size == 0 { + return &migration.ErrFileIsEmpty{} + } + fr := io.NewSectionReader(file, 0, size) - //r := csv.NewReader(fr) + + // Check if the file contains only headers (or less content than expected) + // We can do a preliminary check by reading a small buffer + buf := make([]byte, 64) // Small buffer to check initial content + n, err := fr.Read(buf) + if errors.Is(err, io.EOF) || n == 0 { + return &migration.ErrFileIsEmpty{} + } + if err != nil { + return err + } + + // Reset the reader position to start + _, _ = fr.Seek(0, io.SeekStart) allTasks := []*tickTickTask{} decode := newLineSkipDecoder(fr, 3) - err := gocsv.UnmarshalDecoder(decode, &allTasks) + err = gocsv.UnmarshalDecoder(decode, &allTasks) if err != nil { return err } + // Also check if no tasks were found after decoding + if len(allTasks) == 0 { + return &migration.ErrFileIsEmpty{} + } + for _, task := range allTasks { if task.IsChecklistString == "Y" { task.IsChecklist = true