From 4b8a8d72ce554dde378b5091da74aa930ea18327 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Sat, 25 Jul 2026 07:55:50 +0200 Subject: [PATCH] fix(storage): reject case-folded home directory collisions GetByScope compared scopes with a case-sensitive exact match, so on a case-insensitive filesystem two accounts whose scopes differ only in case (e.g. /users/CaseVictim and /users/casevictim) were treated as distinct even though both resolve to the same physical home directory. The second account could then read, overwrite and delete the first account's files. Match the scope case-insensitively so the collision check performed during signup, proxy and hook provisioning catches these aliases and rejects the duplicate. Refs GHSA-576v-w77m-gr84 --- storage/bolt/users.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/storage/bolt/users.go b/storage/bolt/users.go index 33f67abb..f37b707b 100644 --- a/storage/bolt/users.go +++ b/storage/bolt/users.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "reflect" + "regexp" "github.com/asdine/storm/v3" "github.com/asdine/storm/v3/q" @@ -44,7 +45,11 @@ func (st usersBackend) GetBy(i interface{}) (user *users.User, err error) { func (st usersBackend) GetByScope(scope string) (*users.User, error) { user := &users.User{} - err := st.db.Select(q.Eq("Scope", scope)).First(user) + // Match case-insensitively: on a case-insensitive filesystem two scopes + // that differ only in case (e.g. /users/Alice and /users/alice) resolve to + // the same home directory, so they must be treated as a collision. + pattern := "(?i)^" + regexp.QuoteMeta(scope) + "$" + err := st.db.Select(q.Re("Scope", pattern)).First(user) if err != nil { if errors.Is(err, storm.ErrNotFound) { return nil, fberrors.ErrNotExist