package storage import ( "os" "path/filepath" "strings" "testing" ) func setupStorage(t *testing.T) *Storage { t.Helper() dir := t.TempDir() s, err := NewStorage(dir) if err != nil { t.Fatalf("NewStorage: %v", err) } return s } func TestSaveUpload(t *testing.T) { s := setupStorage(t) path, err := s.SaveUpload(1, "test.blend", strings.NewReader("data")) if err != nil { t.Fatalf("SaveUpload: %v", err) } data, err := os.ReadFile(path) if err != nil { t.Fatalf("read saved file: %v", err) } if string(data) != "data" { t.Errorf("got %q, want %q", data, "data") } } func TestSaveUpload_PathTraversal(t *testing.T) { s := setupStorage(t) path, err := s.SaveUpload(1, "../../etc/passwd", strings.NewReader("evil")) if err != nil { t.Fatalf("SaveUpload: %v", err) } // filepath.Base strips traversal, so the file should be inside the job dir if !strings.HasPrefix(path, s.JobPath(1)) { t.Errorf("saved file %q escaped job directory %q", path, s.JobPath(1)) } if filepath.Base(path) != "passwd" { t.Errorf("expected basename 'passwd', got %q", filepath.Base(path)) } } func TestSaveOutput(t *testing.T) { s := setupStorage(t) path, err := s.SaveOutput(42, "output.png", strings.NewReader("img")) if err != nil { t.Fatalf("SaveOutput: %v", err) } data, err := os.ReadFile(path) if err != nil { t.Fatalf("read saved output: %v", err) } if string(data) != "img" { t.Errorf("got %q, want %q", data, "img") } } func TestSaveOutput_PathTraversal(t *testing.T) { s := setupStorage(t) path, err := s.SaveOutput(42, "../../etc/passwd", strings.NewReader("evil")) if err != nil { t.Fatalf("SaveOutput: %v", err) } outRoot := filepath.Join(s.BasePath(), "outputs", "42") if !strings.HasPrefix(path, outRoot) { t.Errorf("saved file %q escaped output directory %q", path, outRoot) } if filepath.Base(path) != "passwd" { t.Errorf("expected basename passwd, got %q", filepath.Base(path)) } } func TestSanitizeFilename(t *testing.T) { got, err := SanitizeFilename("../../etc/passwd") if err != nil { t.Fatalf("SanitizeFilename: %v", err) } if got != "passwd" { t.Fatalf("got %q want passwd", got) } if _, err := SanitizeFilename(".."); err == nil { t.Fatal("expected error for ..") } } func TestSafePathUnderRoot(t *testing.T) { root := t.TempDir() ok, err := SafePathUnderRoot(root, "subdir/file.blend") if err != nil { t.Fatalf("SafePathUnderRoot: %v", err) } if !strings.HasPrefix(ok, root) { t.Fatalf("path %q not under root %q", ok, root) } if _, err := SafePathUnderRoot(root, "../escape.blend"); err == nil { t.Fatal("expected escape to fail") } if _, err := SafePathUnderRoot(root, "/abs.blend"); err == nil { t.Fatal("expected absolute path to fail") } } func TestGetFile(t *testing.T) { s := setupStorage(t) savedPath, err := s.SaveUpload(1, "readme.txt", strings.NewReader("hello")) if err != nil { t.Fatalf("SaveUpload: %v", err) } f, err := s.GetFile(savedPath) if err != nil { t.Fatalf("GetFile: %v", err) } defer f.Close() buf := make([]byte, 64) n, _ := f.Read(buf) if string(buf[:n]) != "hello" { t.Errorf("got %q, want %q", string(buf[:n]), "hello") } } func TestJobPath(t *testing.T) { s := setupStorage(t) path := s.JobPath(99) if !strings.Contains(path, "99") { t.Errorf("JobPath(99) = %q, expected to contain '99'", path) } }