From 39f6cf9155ed25b0fa74bb5765fb352010f61ca1 Mon Sep 17 00:00:00 2001 From: Wikid82 Date: Thu, 20 Nov 2025 23:07:48 -0500 Subject: [PATCH] fix: enhance path sanitization in backup and log services to prevent directory traversal --- backend/internal/services/backup_service.go | 7 ++++--- .../internal/services/backup_service_test.go | 21 +++++++++++++++++++ backend/internal/services/log_service.go | 2 +- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/backend/internal/services/backup_service.go b/backend/internal/services/backup_service.go index 6918e9e3..bb78b69e 100644 --- a/backend/internal/services/backup_service.go +++ b/backend/internal/services/backup_service.go @@ -160,20 +160,21 @@ func (s *BackupService) addDirToZip(w *zip.Writer, srcDir, zipBase string) error // DeleteBackup removes a backup file func (s *BackupService) DeleteBackup(filename string) error { // Basic sanitization to prevent directory traversal - clean := filepath.Base(filename) + clean := filepath.Base(filepath.Clean(filename)) return os.Remove(filepath.Join(s.BackupDir, clean)) } // GetBackupPath returns the full path to a backup file (for downloading) func (s *BackupService) GetBackupPath(filename string) string { - clean := filepath.Base(filename) + clean := filepath.Base(filepath.Clean(filename)) return filepath.Join(s.BackupDir, clean) } // RestoreBackup restores the database and caddy data from a zip archive func (s *BackupService) RestoreBackup(filename string) error { // 1. Verify backup exists - srcPath := filepath.Join(s.BackupDir, filename) + clean := filepath.Base(filepath.Clean(filename)) + srcPath := filepath.Join(s.BackupDir, clean) if _, err := os.Stat(srcPath); err != nil { return err } diff --git a/backend/internal/services/backup_service_test.go b/backend/internal/services/backup_service_test.go index 30cc666b..f156598e 100644 --- a/backend/internal/services/backup_service_test.go +++ b/backend/internal/services/backup_service_test.go @@ -103,3 +103,24 @@ func TestBackupService_Restore_ZipSlip(t *testing.T) { assert.Error(t, err) assert.Contains(t, err.Error(), "illegal file path") } + +func TestBackupService_PathTraversal(t *testing.T) { + tmpDir := t.TempDir() + service := &BackupService{ + DataDir: filepath.Join(tmpDir, "data"), + BackupDir: filepath.Join(tmpDir, "backups"), + } + os.MkdirAll(service.BackupDir, 0755) + + // Test GetBackupPath with traversal + // Should resolve to .../backups/passwd, NOT .../etc/passwd + path := service.GetBackupPath("../../etc/passwd") + expected := filepath.Join(service.BackupDir, "passwd") + assert.Equal(t, expected, path) + + // Test DeleteBackup with traversal + // Should try to delete .../backups/passwd, fail because it doesn't exist + err := service.DeleteBackup("../../etc/passwd") + assert.Error(t, err) + assert.True(t, os.IsNotExist(err)) +} diff --git a/backend/internal/services/log_service.go b/backend/internal/services/log_service.go index 68bbc32d..755f940c 100644 --- a/backend/internal/services/log_service.go +++ b/backend/internal/services/log_service.go @@ -59,7 +59,7 @@ func (s *LogService) ListLogs() ([]LogFile, error) { // GetLogPath returns the absolute path to a log file if it exists and is valid func (s *LogService) GetLogPath(filename string) (string, error) { - clean := filepath.Base(filename) + clean := filepath.Base(filepath.Clean(filename)) path := filepath.Join(s.LogDir, clean) // Verify file exists