refactor(repo): conform structure to AGENTS.md (web->frontend, internal/api->cmd/webui/{api,handlers}, docs/README+CHANGELOGs, module .gitignores, drop stray library/ and committed debug bins)
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
package handlers_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"mime/multipart"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLibraryCreateListUpload(t *testing.T) {
|
||||
_, _, h, booksDir := setupAPI(t)
|
||||
tok := adminToken(t, h)
|
||||
root := filepath.Join(booksDir, "lib1") // 必须落在解析过软链的 booksDir 内
|
||||
os.MkdirAll(root, 0o755)
|
||||
w := do(h, "POST", "/api/libraries", tok, map[string]string{"name": "comics", "root_path": root})
|
||||
if w.Code != 201 {
|
||||
t.Fatalf("create lib %d %s", w.Code, w.Body)
|
||||
}
|
||||
var lib map[string]any
|
||||
json.Unmarshal(w.Body.Bytes(), &lib)
|
||||
libID := itoa(lib["id"])
|
||||
w = do(h, "GET", "/api/libraries", tok, nil)
|
||||
if !strings.Contains(w.Body.String(), `"comics"`) {
|
||||
t.Fatalf("list: %s", w.Body)
|
||||
}
|
||||
// 相对路径 root 必须 400(前缀校验的根)
|
||||
w = do(h, "POST", "/api/libraries", tok, map[string]string{"name": "x", "root_path": "relative/path"})
|
||||
if w.Code != 400 {
|
||||
t.Fatalf("relative root want 400 got %d", w.Code)
|
||||
}
|
||||
// 上传:白名单 + 防穿越 + 原子落盘
|
||||
body, mw := uploadBody("my 01.cbz", []byte("zipbytes"))
|
||||
req := httptest.NewRequest("POST", "/api/libraries/"+libID+"/upload", body)
|
||||
req.Header.Set("Content-Type", mw.FormDataContentType())
|
||||
req.Header.Set("Authorization", "Bearer "+tok)
|
||||
ww := httptest.NewRecorder()
|
||||
h.ServeHTTP(ww, req)
|
||||
if ww.Code != 202 {
|
||||
t.Fatalf("upload %d %s", ww.Code, ww.Body)
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(root, "my 01.cbz")); err != nil {
|
||||
t.Fatal("uploaded file missing:", err)
|
||||
}
|
||||
body, mw = uploadBody("../../evil.cbz", []byte("x"))
|
||||
req = httptest.NewRequest("POST", "/api/libraries/"+libID+"/upload", body)
|
||||
req.Header.Set("Content-Type", mw.FormDataContentType())
|
||||
req.Header.Set("Authorization", "Bearer "+tok)
|
||||
ww = httptest.NewRecorder()
|
||||
h.ServeHTTP(ww, req)
|
||||
if ww.Code != 202 { // 名字被清洗成 evil.cbz,落在 root 内
|
||||
t.Fatalf("sanitize upload %d", ww.Code)
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(root, "evil.cbz")); err != nil {
|
||||
t.Fatal("evil upload not sanitized")
|
||||
}
|
||||
body, mw = uploadBody("virus.exe", []byte("x"))
|
||||
req = httptest.NewRequest("POST", "/api/libraries/"+libID+"/upload", body)
|
||||
req.Header.Set("Content-Type", mw.FormDataContentType())
|
||||
req.Header.Set("Authorization", "Bearer "+tok)
|
||||
ww = httptest.NewRecorder()
|
||||
h.ServeHTTP(ww, req)
|
||||
if ww.Code != 400 {
|
||||
t.Fatalf("bad ext want 400 got %d", ww.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func uploadBody(filename string, content []byte) (*bytes.Buffer, *multipart.Writer) {
|
||||
buf := &bytes.Buffer{}
|
||||
mw := multipart.NewWriter(buf)
|
||||
fw, _ := mw.CreateFormFile("file", filename)
|
||||
fw.Write(content)
|
||||
mw.Close()
|
||||
return buf, mw
|
||||
}
|
||||
Reference in New Issue
Block a user