Skip to content

fix: treat a negative page size as an empty page, not a syntax error - #827

Merged
blaipr merged 1 commit into
mainfrom
fix/a-negative-page-size-is-not-a-query
Aug 18, 2026
Merged

fix: treat a negative page size as an empty page, not a syntax error#827
blaipr merged 1 commit into
mainfrom
fix/a-negative-page-size-is-not-a-query

Conversation

@blaipr

@blaipr blaipr commented Aug 18, 2026

Copy link
Copy Markdown
Member

How far into a list to start, and how much of it to take, both come from the query string, and nothing narrowed them on the way to the database. Traced end to end:

?count=-1  ->  Filter::getInt('-1') = -1      (negatives pass through)
           ->  analyzeInt('count', …) = -1    (present and readable, so no default)
           ->  ItemSearchDto                  (no clamp)
           ->  Aura: "LIMIT -1 OFFSET -5"
           ->  MariaDB: ERROR 1064 … syntax error near '-1'

So asking for a page came back as a database failure. Nothing is disclosed and nothing is harmed — but a value an ordinary query string can carry should not reach the server as unparseable SQL.

Two ways in, so two places to clamp

  • the item grids and every API search build an ItemSearchDto;
  • the account search reads start and rpp straight from the request into AccountSearchFilterDto, never touching that DTO.

Clamping only the first would have left the search people actually use still broken. Both are clamped, and the tests cover both paths through one data provider.

The third test

It asserts the generated statement carries no negative LIMIT or OFFSET, because the getters were never the problem — the string that reached MariaDB was. Removing either clamp fails five cases across the three tests.

Deliberately not changed

count has no upper bound, so an authenticated caller can ask for a million rows. Left alone: it is authenticated, it returns only rows the caller may already see, and capping it would change behaviour for legitimate large queries. Speculative hardening without evidence of harm is how the ConfigBackup deserialization change went wrong earlier in this series.

3988 unit tests pass; PHPStan level 6 on src and PHPCS clean; integration running.

How far into a list to start, and how much of it to take, both come from the
query string, and nothing narrowed them on the way to the database:

    ?count=-1  ->  Filter::getInt('-1') = -1  ->  no clamp  ->  LIMIT -1
    ?start=-5  ->                                            OFFSET -5

which MariaDB answers with `ERROR 1064 ... syntax error near '-1'`. Asking for a
page came back as a database failure. Nothing was disclosed and nothing was
harmed, but a value an ordinary request can carry should not reach the server as
unparseable SQL.

There are two ways in, so there are two places to clamp. The item grids and
every API search build an `ItemSearchDto`; the account search reads `start` and
`rpp` straight from the request into `AccountSearchFilterDto`. Clamping only the
first would have left the search people actually use still broken.

The third test asserts the generated statement carries no negative LIMIT or
OFFSET, because the getters were never the problem — the string that reached
MariaDB was.

The upper end is deliberately left alone: `count` has no maximum, but a large
one is authenticated, returns only rows the caller may already see, and capping
it would change behaviour for legitimate large queries.
@blaipr
blaipr merged commit 975e5c2 into main Aug 18, 2026
8 checks passed
@blaipr
blaipr deleted the fix/a-negative-page-size-is-not-a-query branch August 18, 2026 23:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant