Five loop-body read filters in routers/variants.py, all of the form has_permission(user_data, variant.score_set, Action.READ):
| Line |
Context |
| 99 |
single-variant lookup |
| 211, 225 |
ClinGen allele search |
| 408, 422 |
ClinGen allele search, second pass |
The router filters after the query, so counts and pagination are already incorrect. The allele-search sites return variants from arbitrary score sets, so assert_permission at the router boundary never covered them — there is no parent entity to guard.
Scope
Delete all five. RLS on scoresets covers them: a variant whose score set is invisible cannot be reached through the join.
Where a filter relied on implicit lazy traversal of Variant.score_set, add an explicit eager load so the loading decision is deliberate rather than incidental.
Do not begin until the policy-gap counter in #835 has read zero for a full release cycle.
Acceptance criteria
Five loop-body read filters in
routers/variants.py, all of the formhas_permission(user_data, variant.score_set, Action.READ):The router filters after the query, so counts and pagination are already incorrect. The allele-search sites return variants from arbitrary score sets, so
assert_permissionat the router boundary never covered them — there is no parent entity to guard.Scope
Delete all five. RLS on
scoresetscovers them: a variant whose score set is invisible cannot be reached through the join.Where a filter relied on implicit lazy traversal of
Variant.score_set, add an explicit eager load so the loading decision is deliberate rather than incidental.Do not begin until the policy-gap counter in #835 has read zero for a full release cycle.
Acceptance criteria