openspec/specs/objects-crud requires:
Every object list/search endpoint SHALL clamp the effective page size to a hard maximum. A client-supplied _limit above the maximum SHALL be reduced to the maximum; it SHALL NOT cause the server to load an arbitrarily large result set.
It is implemented on two paths and not on the third — and the third is the one the fleet's apps actually use.
| path |
clamp |
cross-schema UNION (MagicMapper) |
yes, MAX_PAGE_SIZE (1000) |
external database (DbalObjectSourceProvider) |
yes, MAX_RESULTS (1000) |
single schema (MagicSearchHandler::searchObjects) |
no |
MagicSearchHandler passes the value straight to setMaxResults(), so GET /api/objects/{register}/{schema}?_limit=1000000 loads a million rows and materialises them in PHP before serialising. That is precisely what the requirement was written to prevent.
Worse, an ABSENT _limit on that path is also unbounded: Doctrine documents setMaxResults(null) as "retrieve all results", and nothing upstream injects a default. The ?? 20 in ObjectsController only shapes the pagination METADATA — it never reaches the query. So the documented "default: 20" has never been the number of rows returned.
Deliberately not fixed in #2885. Adding the clamp would REDUCE what existing callers receive, silently, for any caller currently relying on an unbounded read — a behaviour change in the dangerous direction, and one that deserves its own decision rather than riding along with a _limit normalisation. #2885 keeps the existing clamps exactly where they were and only adds an explicit opt-out (_limit=false).
Two things to decide:
- Should the canonical path clamp numeric limits like the other two?
- Should an absent
_limit mean a default page size rather than everything? If so, callers omitting it today will start receiving a page, which needs announcing.
Found while implementing #2885.
openspec/specs/objects-crudrequires:It is implemented on two paths and not on the third — and the third is the one the fleet's apps actually use.
MagicMapper)MAX_PAGE_SIZE(1000)DbalObjectSourceProvider)MAX_RESULTS(1000)MagicSearchHandler::searchObjects)MagicSearchHandlerpasses the value straight tosetMaxResults(), soGET /api/objects/{register}/{schema}?_limit=1000000loads a million rows and materialises them in PHP before serialising. That is precisely what the requirement was written to prevent.Worse, an ABSENT
_limiton that path is also unbounded: Doctrine documentssetMaxResults(null)as "retrieve all results", and nothing upstream injects a default. The?? 20inObjectsControlleronly shapes the pagination METADATA — it never reaches the query. So the documented "default: 20" has never been the number of rows returned.Deliberately not fixed in #2885. Adding the clamp would REDUCE what existing callers receive, silently, for any caller currently relying on an unbounded read — a behaviour change in the dangerous direction, and one that deserves its own decision rather than riding along with a
_limitnormalisation. #2885 keeps the existing clamps exactly where they were and only adds an explicit opt-out (_limit=false).Two things to decide:
_limitmean a default page size rather than everything? If so, callers omitting it today will start receiving a page, which needs announcing.Found while implementing #2885.