package api import ( "image" "image/color" "image/png" "net/http/httptest" "os" "path/filepath" "testing" "chfs-ng/internal/auth" "chfs-ng/internal/storage" ) func TestAuditExistEnforcesReadPermission(t *testing.T) { root := t.TempDir() if err := os.WriteFile(filepath.Join(root, "secret.txt"), []byte("x"), 0600); err != nil { t.Fatal(err) } s := &Server{Store: storage.Local{Root: root}, Auth: &auth.Manager{Guest: &auth.Account{Mask: auth.None}}} rr := httptest.NewRecorder() req := httptest.NewRequest("GET", "/chfs/exist?file=secret.txt", nil) s.exist(rr, req) if rr.Code != 403 { t.Fatalf("exist status=%d want 403", rr.Code) } } func TestAuditThumbnailRejectsOversizeBeforeDecode(t *testing.T) { root := t.TempDir() if err := os.WriteFile(filepath.Join(root, "big.bin"), make([]byte, 64), 0600); err != nil { t.Fatal(err) } s := &Server{Store: storage.Local{Root: root}, Auth: &auth.Manager{Guest: &auth.Account{Mask: auth.Read}}, ImagePreview: true, ThumbnailMaxSize: 32} rr := httptest.NewRecorder() req := httptest.NewRequest("GET", "/chfs/thumbnail?file=big.bin", nil) s.thumbnail(rr, req) if rr.Code != 413 { t.Fatalf("thumbnail status=%d want 413", rr.Code) } } func TestAuditZipLimitsUseOriginalFileSizes(t *testing.T) { root := t.TempDir() if err := os.WriteFile(filepath.Join(root, "a.txt"), make([]byte, 100), 0600); err != nil { t.Fatal(err) } s := &Server{Store: storage.Local{Root: root}, ZipMaxSize: 99, ZipMaxFiles: 10} a := &auth.Account{Mask: auth.Read} if err := s.checkZipLimits(a, []string{""}); err == nil { t.Fatal("expected zip size limit") } } func TestAuditSearchLimit(t *testing.T) { root := t.TempDir() for _, name := range []string{"a.txt", "b.txt", "c.txt"} { if err := os.WriteFile(filepath.Join(root, name), []byte(name), 0600); err != nil { t.Fatal(err) } } s := &Server{Store: storage.Local{Root: root}, Auth: &auth.Manager{Guest: &auth.Account{Mask: auth.Read}}, SearchMaxResults: 2} rr := httptest.NewRecorder() req := httptest.NewRequest("GET", "/chfs/search?str=.txt", nil) s.search(rr, req) if rr.Code != 200 { t.Fatalf("search status=%d", rr.Code) } if len(rr.Body.Bytes()) == 0 || !contains(rr.Body.String(), `"truncated":true`) { t.Fatalf("search did not report truncation: %s", rr.Body.String()) } } func contains(s, sub string) bool { return len(s) >= len(sub) && (s == sub || func() bool { for i := 0; i+len(sub) <= len(s); i++ { if s[i:i+len(sub)] == sub { return true } } return false }()) } func TestAuditThumbnailPixelLimit(t *testing.T) { root := t.TempDir() img := image.NewRGBA(image.Rect(0, 0, 20, 20)) img.Set(0, 0, color.White) f, err := os.Create(filepath.Join(root, "large.png")) if err != nil { t.Fatal(err) } if err := png.Encode(f, img); err != nil { f.Close() t.Fatal(err) } f.Close() s := &Server{Store: storage.Local{Root: root}, Auth: &auth.Manager{Guest: &auth.Account{Mask: auth.Read}}, ImagePreview: true, ThumbnailMaxPixels: 100} rr := httptest.NewRecorder() req := httptest.NewRequest("GET", "/chfs/thumbnail?file=large.png", nil) s.thumbnail(rr, req) if rr.Code != 415 { t.Fatalf("thumbnail pixel-limit status=%d want 415", rr.Code) } }