feat: complete guest shared download flow

This commit is contained in:
OpenClaw Agent
2026-09-22 04:55:52 +08:00
parent 73c05ef112
commit d8c938a0c8
3 changed files with 100 additions and 15 deletions
+62 -1
View File
@@ -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")