You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
security(spp_api_v2): POST /Individual/_search and /Group/_search skip scope checks and the consent total rule; the endpoint returns nothing [Severity: Medium, latent] #575
Confirmed with an HTTP probe on #555's branch (2026-09-28):
No scope check. A client with only program:read gets 200 on POST /Individual/_search and POST /Group/_search. routers/filter.py never calls has_scope, and there's no router-level dependency.
(GET /_filters does return 403.)
It returns nothing.Model.search/search_count run as the endpoint's public user without sudo()
(filter.py:165, 177), unlike the GET searches (search_service.py uses sudo()), so record rules hide every
registrant. Every request gets an empty bundle.
Paging is dead code.PaginationSpec has no last_id, so the _lastId cursor never applies and offset is
ignored. next is dropped on a short page, and the id > cursor combined with an arbitrary sort would skip rows.
Why it matters
Today nothing leaks, because the endpoint returns nothing. Anyone who "fixes" the missing sudo() alone opens it
to every authenticated client regardless of scope, with the raw total. The scope check and the consent total rule
must land before or with the sudo() fix.
Expected behavior
individual:search/group:search (or :read) scope enforced; searches run like the GET searches; totals via page_total_and_next; working pagination. Or retire the endpoint if nothing uses it.
Found while checking OpenSPP2 REST API v2 against the OpenFn @openfn/language-openspp v4 adaptor (#554, PR #555); not fixed in #555.
Activity