CVE-2026-55496
Cloudreve: Information Exposure in `GET /api/v4/user/search`: `SearchActive` omits the active-status predicate, leaking inactive/banned account emails
Summary
## Summary `GET /api/v4/user/search` is available to any logged-in user. The service calls `userClient.SearchActive`, but despite its name that method filters only by email/nickname keyword and **never adds a `StatusActive` predicate** — while the sibling lookups `GetActiveByID` and `GetActiveByDavAccount`, defined a few lines above it, do. Search hits are serialized at `RedactLevelUser`, which includes the email address. A normal logged-in user can therefore enumerate and retrieve the email (plus nickname, avatar, creation time, redacted group, profile share-visibility) of **inactive and banned** accounts that an active-user directory is supposed to suppress. No global status interceptor compensates — the only User query interceptor is soft-delete, and inactive/banned rows are not soft-deleted. ### Details ## Root cause (verified at `26b6b10`) **1. Route — logged-in + `UserInfo.Read` scope** (`routers/router.go`): ```go user := v4.Group("user") // protected user group (login required) user.GET("search", middleware.RequiredScopes(types.ScopeUserInfoRead), controllers.FromQuery[usersvc.SearchUserService](...), controllers.UserSearch) ``` The `RequiredScopes` check applies to scoped OAuth tokens; plain session requests are not gated by it — so any logged-in user reaches the search. **2. Service — 2-char keyword to `SearchActive`** (`service/user/info.go`): ```go type SearchUserService struct { Keyword string `form:"keyword" binding:"required,min=2"` } const resultLimit = 10 func (s *SearchUserService) Search(c *gin.Context) ([]*ent.User, error) { return dep.UserClient().SearchActive(c, resultLimit, s.Keyword) } ``` **3. The bug — `SearchActive` has no status predicate** (`inventory/user.go`): ```go func (c *userClient) SearchActive(ctx context.Context, limit int, keyword string) ([]*ent.User, error) { ctx = context.WithValue(ctx, LoadUserGroup{}, true) return withUserEagerLoading(ctx, c.client.User.Query(). Where(user.Or(user.EmailContainsFold(keyword), user.NickContainsFold(keyword))). Limit(limit), // =2 chars of the target email/nick> Cookie: cloudreve-session= ``
Affected packages
| Ecosystem | Package | Affected versions | Fixed versions |
|---|---|---|---|
| Go | github.com/cloudreve/Cloudreve/v4 | — | 4.0.0-20260613023921-7e1289d55279 |
| Go | github.com/cloudreve/Cloudreve/v3 | — | — |
Remediation: Upgrade to 4.0.0-20260613023921-7e1289d55279 or later.
References
Includes data from the GitHub Advisory Database, licensed under CC-BY 4.0.
CVE® is a registered trademark of The MITRE Corporation. CVE content reproduced under the CVE Terms of Use; copyright designation © MITRE.