From b85216fd51a7ba885ec2a9d3b324b4e992009260 Mon Sep 17 00:00:00 2001 From: Tomas Dvorak Date: Sat, 19 Sep 2026 00:19:57 +0200 Subject: [PATCH] fix(share): allow special characters in share passwords (#2528) Passwords containing emoji or URI-reserved characters produced an unparseable cloudreve:// share URI, and the create/update binding rejected anything non-alphanumeric. - NewShareUri percent-encodes the userinfo via url.UserPassword; the parse side already decodes it - drop the alphanum binding on ShareCreateService.Password - round-trip test covers emoji, spaces, and reserved characters Authored By: TDvorak Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- pkg/filemanager/fs/uri.go | 4 ++-- pkg/filemanager/fs/uri_test.go | 21 +++++++++++++++++++++ service/share/manage.go | 2 +- 3 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 pkg/filemanager/fs/uri_test.go diff --git a/pkg/filemanager/fs/uri.go b/pkg/filemanager/fs/uri.go index 3f5bb147..84868a23 100644 --- a/pkg/filemanager/fs/uri.go +++ b/pkg/filemanager/fs/uri.go @@ -356,9 +356,9 @@ func SearchCategoryFromString(s string) setting.SearchCategory { func NewShareUri(id, password string) string { if password != "" { - return fmt.Sprintf("%s://%s:%s@%s", constants.CloudreveScheme, id, password, constants.FileSystemShare) + return fmt.Sprintf("%s://%s@%s", constants.CloudreveScheme, url.UserPassword(id, password), constants.FileSystemShare) } - return fmt.Sprintf("%s://%s@%s", constants.CloudreveScheme, id, constants.FileSystemShare) + return fmt.Sprintf("%s://%s@%s", constants.CloudreveScheme, url.User(id), constants.FileSystemShare) } func NewMyUri(id string) string { diff --git a/pkg/filemanager/fs/uri_test.go b/pkg/filemanager/fs/uri_test.go new file mode 100644 index 00000000..fc95af86 --- /dev/null +++ b/pkg/filemanager/fs/uri_test.go @@ -0,0 +1,21 @@ +package fs + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestShareUriPasswordRoundTrip(t *testing.T) { + for _, password := range []string{"plain", "with space", "a:b@c/d?e", "😀😴😭", "$%^&*"} { + u, err := NewUriFromString(NewShareUri("abc123", password)) + require.NoError(t, err, "password %q", password) + require.Equal(t, "abc123", u.ID("")) + require.Equal(t, password, u.Password(), "password %q", password) + } + + u, err := NewUriFromString(NewShareUri("abc123", "")) + require.NoError(t, err) + require.Equal(t, "abc123", u.ID("")) + require.Equal(t, "", u.Password()) +} diff --git a/service/share/manage.go b/service/share/manage.go index 570a01d1..bb266af4 100644 --- a/service/share/manage.go +++ b/service/share/manage.go @@ -22,7 +22,7 @@ type ( ShareCreateService struct { Uri string `json:"uri" binding:"required"` IsPrivate bool `json:"is_private"` - Password string `json:"password" binding:"omitempty,max=32,alphanum"` + Password string `json:"password" binding:"omitempty,max=32"` RemainDownloads int `json:"downloads"` Expire int `json:"expire"` ShareView bool `json:"share_view"`