feat: complete guest shared download flow
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"chfs-ng/internal/auth"
|
||||
@@ -30,7 +31,7 @@ func TestSharedRoutesMatchCHFS(t *testing.T) {
|
||||
r = httptest.NewRequest(http.MethodGet, "/chfs/shared/hello.txt", nil)
|
||||
w = httptest.NewRecorder()
|
||||
s.Handler().ServeHTTP(w, r)
|
||||
if w.Code != http.StatusOK || w.Header().Get("Content-Disposition") != `attachment;filename="hello.txt"` {
|
||||
if w.Code != http.StatusOK || w.Header().Get("Content-Disposition") != `attachment; filename=hello.txt` {
|
||||
t.Fatalf("shared file: status=%d disposition=%q", w.Code, w.Header().Get("Content-Disposition"))
|
||||
}
|
||||
body, _ := io.ReadAll(w.Result().Body)
|
||||
@@ -39,6 +40,66 @@ func TestSharedRoutesMatchCHFS(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSharedGuestFlowAndSafeURLs(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
if err := os.Mkdir(filepath.Join(root, "docs"), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.Mkdir(filepath.Join(root, "private"), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(root, "docs", "你好 #?.txt"), []byte("shared"), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(root, "private", "secret.txt"), []byte("secret"), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
s := &Server{Store: storage.Local{Root: root}, Auth: &auth.Manager{Accounts: map[string]*auth.Account{}, Guest: &auth.Account{
|
||||
Mask: auth.Read,
|
||||
Rules: []auth.PathRule{{Dir: "private", Deny: true}},
|
||||
}},
|
||||
}
|
||||
for _, tc := range []struct {
|
||||
name, url string
|
||||
want int
|
||||
}{
|
||||
{"guest file", "/chfs/shared/docs/%E4%BD%A0%E5%A5%BD%20%23%3F.txt", http.StatusOK},
|
||||
{"denied file", "/chfs/shared/private/secret.txt", http.StatusNotFound},
|
||||
{"traversal", "/chfs/shared/../private/secret.txt", http.StatusNotFound},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
r := httptest.NewRequest(http.MethodGet, tc.url, nil)
|
||||
w := httptest.NewRecorder()
|
||||
if tc.name == "traversal" {
|
||||
s.shared(w, r)
|
||||
} else {
|
||||
s.Handler().ServeHTTP(w, r)
|
||||
}
|
||||
if w.Code != tc.want {
|
||||
t.Fatalf("status=%d want=%d body=%q", w.Code, tc.want, w.Body.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
r := httptest.NewRequest(http.MethodGet, "/chfs/shared/docs", nil)
|
||||
w := httptest.NewRecorder()
|
||||
s.Handler().ServeHTTP(w, r)
|
||||
if w.Code != http.StatusOK || !strings.Contains(w.Body.String(), "你好 #?") {
|
||||
t.Fatalf("shared directory listing status=%d body=%q", w.Code, w.Body.String())
|
||||
}
|
||||
if strings.Contains(w.Body.String(), "private") {
|
||||
t.Fatalf("denied entry leaked in listing: %q", w.Body.String())
|
||||
}
|
||||
r = httptest.NewRequest(http.MethodHead, "/chfs/shared/docs/%E4%BD%A0%E5%A5%BD%20%23%3F.txt", nil)
|
||||
w = httptest.NewRecorder()
|
||||
s.Handler().ServeHTTP(w, r)
|
||||
if w.Code != http.StatusOK || w.Body.Len() != 0 {
|
||||
t.Fatalf("HEAD status=%d body=%d", w.Code, w.Body.Len())
|
||||
}
|
||||
if got := w.Header().Get("Content-Disposition"); !strings.Contains(got, "attachment") {
|
||||
t.Fatalf("disposition=%q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDownloadDirArchiveHeadersAndEntries(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
dir := filepath.Join(root, "pack")
|
||||
|
||||
Reference in New Issue
Block a user