fix(feed): a malformed saved view cannot break the autotag cleanup, and a foreign private tag reads as missing

E32 review round 1 (B5, B6).

- The autotag orphan cleanup reads every user's feed views. A view whose tag pick was not a list
  ("tags": 5) raised for everyone's job, True passed as tag id 1, and ~66k ids pushed the NOT IN
  past psycopg's 65535 bind parameters. The views are now read defensively and the kept ids
  filtered in Python; the saved-views route holds filters.tags to a list of at most 500 ids, on
  create and on update.
- tags_by_category resolved any tag id, so another user's private tag landed in its real category
  and the feed summary answered differently than for an id that does not exist, which told the
  caller which foreign ids exist. It is owner-scoped now (pre-existing; S2's missing-tag branch
  made the difference visible).
This commit is contained in:
2026-10-04 02:14:04 +02:00
parent df4a79a1f1
commit 887b4e546c
6 changed files with 118 additions and 21 deletions
+22
View File
@@ -12,6 +12,8 @@ from datetime import datetime, timezone
import pytest
from app import feedquery
from app.models import Channel, ChannelTag, Subscription, Tag, Video, VideoState
@@ -192,6 +194,26 @@ def test_a_deleted_tags_id_filters_to_nothing_not_to_everything(db, as_user, mak
assert r.json()["items"] == [], qs
def test_another_users_private_tag_answers_like_one_that_does_not_exist(db, as_user, make_user):
"""The category split resolved any tag id, so a foreign PRIVATE tag landed in its real category
and the summary's facets differed from a non-existent id's: an oracle for which foreign ids exist
(E32 review). Both must now read as "missing"."""
user, tag_ids = _seed_tagged(db, make_user)
stranger = make_user(email="stranger@example.com")
private = Tag(user_id=stranger.id, name="Secret", category="language")
db.add(private)
db.commit()
assert feedquery.tags_by_category(db, user, [tag_ids["L1"], private.id]) == {
"language": [tag_ids["L1"]],
feedquery.MISSING_TAG_CATEGORY: [private.id],
}
client = as_user(user)
foreign = client.get(f"/api/feed/summary?show=all&tags={tag_ids['T1']}&tags={private.id}")
absent = client.get(f"/api/feed/summary?show=all&tags={tag_ids['T1']}&tags=987654")
assert foreign.status_code == absent.status_code == 200
assert foreign.json() == absent.json()
def test_untagged_filter_keeps_only_channels_the_user_has_not_tagged(db, as_user, make_user):
"""The "No tag" filter (item B): `untagged=true` keeps only channels with NO tag the USER personally
attached (no user-owned ChannelTag). System/auto tags (user_id NULL) don't count, and another user's