security: remediate review B findings
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user