From eb26f610706767d3d9bc8d410b5878c79516654c Mon Sep 17 00:00:00 2001 From: Carlos Date: Mon, 19 Jan 2026 11:42:23 -0800 Subject: [PATCH] security: sanitize user input in temp directory names Replace direct use of email in temp directory names with a SHA256 hash to prevent directory traversal attacks. - Add utils/sanitize.go with SafeTempDirPrefix() function - Use 8-character hash of email instead of raw email - Update all tw/*.go files to use SafeTempDirPrefix This prevents potential path traversal if user-controlled email contains special characters like path separators. Co-Authored-By: Claude Opus 4.5 --- backend/utils/sanitize.go | 19 +++++++++++++++++++ backend/utils/tw/add_task.go | 2 +- backend/utils/tw/complete_task.go | 2 +- backend/utils/tw/complete_tasks.go | 2 +- backend/utils/tw/delete_task.go | 2 +- backend/utils/tw/delete_tasks.go | 2 +- backend/utils/tw/edit_task.go | 2 +- backend/utils/tw/fetch_tasks.go | 2 +- backend/utils/tw/modify_task.go | 2 +- 9 files changed, 27 insertions(+), 8 deletions(-) create mode 100644 backend/utils/sanitize.go diff --git a/backend/utils/sanitize.go b/backend/utils/sanitize.go new file mode 100644 index 00000000..3d8d48f3 --- /dev/null +++ b/backend/utils/sanitize.go @@ -0,0 +1,19 @@ +package utils + +import ( + "crypto/sha256" + "encoding/hex" +) + +// SafeTempDirPrefix creates a safe prefix for temporary directory names. +// Instead of using user-provided values directly (which could contain path +// separators or special characters), we use a hash of the value. +// This prevents directory traversal attacks while still providing +// unique prefixes per user. +func SafeTempDirPrefix(prefix, userIdentifier string) string { + // Create a short hash of the user identifier + hash := sha256.Sum256([]byte(userIdentifier)) + // Use first 8 characters of hex-encoded hash (32 bits of entropy) + shortHash := hex.EncodeToString(hash[:])[:8] + return prefix + shortHash +} diff --git a/backend/utils/tw/add_task.go b/backend/utils/tw/add_task.go index d362f903..d445aa22 100644 --- a/backend/utils/tw/add_task.go +++ b/backend/utils/tw/add_task.go @@ -14,7 +14,7 @@ func AddTaskToTaskwarrior(req models.AddTaskRequestBody, dueDate string) error { return fmt.Errorf("error deleting Taskwarrior data: %v", err) } - tempDir, err := os.MkdirTemp("", "taskwarrior-"+req.Email) + tempDir, err := os.MkdirTemp("", utils.SafeTempDirPrefix("taskwarrior-", req.Email)) if err != nil { return fmt.Errorf("failed to create temporary directory: %v", err) } diff --git a/backend/utils/tw/complete_task.go b/backend/utils/tw/complete_task.go index f27bbece..1195a941 100644 --- a/backend/utils/tw/complete_task.go +++ b/backend/utils/tw/complete_task.go @@ -10,7 +10,7 @@ func CompleteTaskInTaskwarrior(email, encryptionSecret, uuid, taskuuid string) e if err := utils.ExecCommand("rm", "-rf", "/root/.task"); err != nil { return fmt.Errorf("error deleting Taskwarrior data: %v", err) } - tempDir, err := os.MkdirTemp("", "taskwarrior-"+email) + tempDir, err := os.MkdirTemp("", utils.SafeTempDirPrefix("taskwarrior-", email)) if err != nil { return fmt.Errorf("failed to create temporary directory: %v", err) } diff --git a/backend/utils/tw/complete_tasks.go b/backend/utils/tw/complete_tasks.go index 159e3a28..c97f0561 100644 --- a/backend/utils/tw/complete_tasks.go +++ b/backend/utils/tw/complete_tasks.go @@ -13,7 +13,7 @@ func CompleteTasksInTaskwarrior(email, encryptionSecret, uuid string, taskUUIDs return nil, fmt.Errorf("error deleting Taskwarrior data: %v", err) } - tempDir, err := os.MkdirTemp("", "taskwarrior-"+email) + tempDir, err := os.MkdirTemp("", utils.SafeTempDirPrefix("taskwarrior-", email)) if err != nil { return nil, fmt.Errorf("failed to create temporary directory: %v", err) diff --git a/backend/utils/tw/delete_task.go b/backend/utils/tw/delete_task.go index b90b3e16..46c4fa2c 100644 --- a/backend/utils/tw/delete_task.go +++ b/backend/utils/tw/delete_task.go @@ -10,7 +10,7 @@ func DeleteTaskInTaskwarrior(email, encryptionSecret, uuid, taskuuid string) err if err := utils.ExecCommand("rm", "-rf", "/root/.task"); err != nil { return fmt.Errorf("error deleting Taskwarrior data: %v", err) } - tempDir, err := os.MkdirTemp("", "taskwarrior-"+email) + tempDir, err := os.MkdirTemp("", utils.SafeTempDirPrefix("taskwarrior-", email)) if err != nil { return fmt.Errorf("failed to create temporary directory: %v", err) } diff --git a/backend/utils/tw/delete_tasks.go b/backend/utils/tw/delete_tasks.go index bda2496a..a746abea 100644 --- a/backend/utils/tw/delete_tasks.go +++ b/backend/utils/tw/delete_tasks.go @@ -13,7 +13,7 @@ func DeleteTasksInTaskwarrior(email, encryptionSecret, uuid string, taskUUIDs [] return nil, fmt.Errorf("error deleting Taskwarrior data: %v", err) } - tempDir, err := os.MkdirTemp("", "taskwarrior-"+email) + tempDir, err := os.MkdirTemp("", utils.SafeTempDirPrefix("taskwarrior-", email)) if err != nil { return nil, fmt.Errorf("failed to create temporary directory: %v", err) diff --git a/backend/utils/tw/edit_task.go b/backend/utils/tw/edit_task.go index cf185ff8..3207a5b6 100644 --- a/backend/utils/tw/edit_task.go +++ b/backend/utils/tw/edit_task.go @@ -13,7 +13,7 @@ func EditTaskInTaskwarrior(uuid, description, email, encryptionSecret, taskID st if err := utils.ExecCommand("rm", "-rf", "/root/.task"); err != nil { return fmt.Errorf("error deleting Taskwarrior data: %v", err) } - tempDir, err := os.MkdirTemp("", "taskwarrior-"+email) + tempDir, err := os.MkdirTemp("", utils.SafeTempDirPrefix("taskwarrior-", email)) if err != nil { return fmt.Errorf("failed to create temporary directory: %v", err) } diff --git a/backend/utils/tw/fetch_tasks.go b/backend/utils/tw/fetch_tasks.go index 50d5dcff..67d2d137 100644 --- a/backend/utils/tw/fetch_tasks.go +++ b/backend/utils/tw/fetch_tasks.go @@ -14,7 +14,7 @@ func FetchTasksFromTaskwarrior(email, encryptionSecret, origin, UUID string) ([] return nil, fmt.Errorf("error deleting Taskwarrior data: %v", err) } - tempDir, err := os.MkdirTemp("", "taskwarrior-"+email) + tempDir, err := os.MkdirTemp("", utils.SafeTempDirPrefix("taskwarrior-", email)) if err != nil { return nil, fmt.Errorf("failed to create temporary directory: %v", err) } diff --git a/backend/utils/tw/modify_task.go b/backend/utils/tw/modify_task.go index bbeb6786..e1050455 100644 --- a/backend/utils/tw/modify_task.go +++ b/backend/utils/tw/modify_task.go @@ -11,7 +11,7 @@ func ModifyTaskInTaskwarrior(uuid, description, project, priority, status, due, if err := utils.ExecCommand("rm", "-rf", "/root/.task"); err != nil { return fmt.Errorf("error deleting Taskwarrior data: %v", err) } - tempDir, err := os.MkdirTemp("", "taskwarrior-"+email) + tempDir, err := os.MkdirTemp("", utils.SafeTempDirPrefix("taskwarrior-", email)) if err != nil { return fmt.Errorf("failed to create temporary directory: %v", err) }