-
-
Notifications
You must be signed in to change notification settings - Fork 88
Description
Summary
When renaming or deleting a tag, the dashboard entry scan queries ALL users' dashboard entries without filtering by user_id. This allows cross-user data modification (UpdateTag) and cross-user information disclosure (RemoveTag).
Details
UpdateTag - Cross-user data modification (HIGH)
File: tag/update.go lines 53-73
When renaming a tag, the code searches ALL dashboard entries matching the tag key, then modifies them:
// Lines 53-59: Searches ALL entries, not just current user's
if err := tx.Where("keys LIKE ?", "%"+key).
Or("keys like ?", "%"+key+"%").
Or("keys like ?", key+"%").
Find(&usedInEntries).Error; err != nil {
// Lines 61-73: MODIFIES entries from any user
for _, entry := range usedInEntries {
tags := strings.Split(entry.Keys, ",")
for index, tagInEntry := range tags {
if tagInEntry == key {
tags[index] = *newKey // Modifies other users' entries!
}
}
entry.Keys = strings.Join(tags, ",")
tx.Save(&entry) // Writes to DB
}RemoveTag - Cross-user info disclosure (LOW)
File: tag/remove.go lines 23-33
When trying to delete a tag, the code checks ALL entries and leaks another user's dashboard name:
if len(usedInEntries) > 0 {
dashboard := &model.Dashboard{ID: usedInEntries[0].DashboardID}
r.DB.Find(dashboard)
return nil, fmt.Errorf("tag '%s' is used in dashboard '%s' entry '%s'...", key, dashboard.Name, ...)
// Leaks another user's dashboard name and entry title
}Secure comparison
TimeSpan tag updates (same file, lines 37-49) correctly scope to user:
timeSpansIdsOfUser := tx.Model(new(model.TimeSpan)).
Select("id").
Where(&model.TimeSpan{UserID: userID}). // ✓ Scoped to user
SubQuery()Impact
If two users create tags with the same key (e.g., "project"), User A renaming their tag will silently modify User B's dashboard entries. User A deleting their tag may also be blocked by User B's entries and leak User B's dashboard name.
Recommended Fix
Join to the dashboards table and filter by user_id:
if err := tx.
Joins("INNER JOIN dashboards ON dashboard_entries.dashboard_id = dashboards.id").
Where("dashboards.user_id = ?", userID).
Where("keys LIKE ? OR keys LIKE ? OR keys LIKE ?", "%"+key, "%"+key+"%", key+"%").
Find(&usedInEntries).Error; err != nil {