Skip to content

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

Open

Description

@gonzalesedwin1123

Describe the bug

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.
  • Total leak (latent). total = search_count(domain) is replaced only when this page met a hidden record
    (filter.py:214-216). That's the rule fix(api_v2): REST API v2 defects found by the OpenFn adaptor (#554) #555 replaced for the GET searches with page_total_and_next.
  • 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions