fix(security): list items need write access to their list (FLAW-27)
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 59s

Creating, checking, editing, deleting, reordering and clearing list
items did not check the list at all: any logged-in user with a list or
item ID could change it, viewers of shared lists as well. All item
endpoints and converting a thought into a list item now require owner
or editor rights and that the item belongs to the list (404 without
access, 403 for viewers).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
sandrodachandClaude Opus 5.5 committed 2026-10-29 14:28:39 +01:00
1 parent 11c14fa9ff
commit 3900c6104e
5 files changed
+125

No files matched your search

+45
View File
@@ -500,3 +500,48 @@ func (s *Store) ReorderListItems(listID string, items []domain.ListItemReorderIt
_, _ = tx.Exec(`UPDATE lists SET updated_at = ? WHERE id = ?`, domain.NowISO(), listID)
return tx.Commit()
}
// ErrListNotFound: the list does not exist or the user has no access to it (deliberately the same).
var ErrListNotFound = errors.New("Liste nicht gefunden")
// ErrListReadOnly: the list is shared with the user as viewer.
var ErrListReadOnly = errors.New("Diese Liste ist für dich nur lesbar")
// ListRole is the user's role in a list: owner, editor, viewer or "" without access.
func (s *Store) ListRole(userID, listID string) string {
var role string
err := s.db.QueryRow(`
SELECT CASE WHEN l.user_id = ? THEN 'owner'
WHEN MAX(CASE WHEN ls.role = 'editor' THEN 1 ELSE 0 END) = 1 THEN 'editor'
ELSE 'viewer' END
FROM lists l
LEFT JOIN list_shares ls ON ls.list_id = l.id AND (
(ls.target_type = 'user' AND ls.target_id = ?)
OR (ls.target_type = 'team' AND ls.target_id IN (SELECT team_id FROM team_members WHERE user_id = ?))
)
WHERE l.id = ? AND (l.user_id = ? OR ls.id IS NOT NULL)
GROUP BY l.id`, userID, userID, userID, listID, userID).Scan(&role)
if err != nil {
return ""
}
return role
}
// EnsureListItemsWritable checks that the user may change the items of a list (owner or editor) and,
// if itemID is given, that the item belongs to that list.
func (s *Store) EnsureListItemsWritable(userID, listID, itemID string) error {
switch s.ListRole(userID, listID) {
case "":
return ErrListNotFound
case "viewer":
return ErrListReadOnly
}
if itemID != "" {
var n int
_ = s.db.QueryRow(`SELECT COUNT(*) FROM list_items WHERE id = ? AND list_id = ?`, itemID, listID).Scan(&n)
if n == 0 {
return ErrListNotFound
}
}
return nil
}