Repository navigation
fix: BE-18 — bound member-list query parameters (#638) - #825
Merged
Qoder-Voidd merged 3 commits intoOct 20, 2026
Merged
Qoder-Voidd merged 3 commits into
Qoder-Voidd merged 3 commits into
Conversation
fix(contact): improve rate-limit key normalization for IPv4 and IPv4-mapped IPv6
`MemberQueryDto` inherited `page` / `limit` from `PaginationDto`, which caps `limit` at `MAX_LIMIT` but left `page` unbounded, and `search` had no length bound at all. `GetMembersProvider` trusted the raw query object, so a caller that reached the provider outside the DTO layer (internal service calls, the admin controller) could ask for an arbitrary OFFSET or a `limit` of `0` — which made `totalPages` divide by zero. Validation (400 with the standard centralised error shape): - `page` is now capped at `MAX_MEMBER_PAGE` (10_000) and must be an integer >= 1, so the worst-case OFFSET stays finite. - `search` is capped at `MAX_MEMBER_SEARCH_LENGTH` (100) and trimmed before it reaches the provider. - `limit` keeps the inherited `MAX_LIMIT` cap and rejects zero/negative/non-integer values. Provider (defence in depth): - `resolveMemberPage` / `resolveMemberLimit` clamp whatever they are handed: oversized pages are capped at `MAX_LIMIT` rather than rejected, out-of-range values are clamped into `[1, max]`, and `NaN` / `Infinity` / non-numeric input falls back to the shared default instead of leaking into the SQL builder. - `resolveMemberSearch` lowercases, bounds and escapes the `LIKE` wildcards (`%`, `_`) so a search term is matched literally instead of widening into a full-table match. Adds `get-members.provider.spec.ts` covering the defaults, the page size ceiling, the page ceiling, the filter normalisation and the DTO validation errors.
|
@VeronicDev Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #638
Problem
MemberQueryDtoinheritedpage/limitfromPaginationDto, which capslimitatMAX_LIMITbut leftpagecompletely unbounded, andsearchhad no length bound at all.?page=999999999produced a multi-billion rowOFFSETthat forced Postgres to scan and discard the whole table before returning nothing.GetMembersProviderthen trusted the raw query object withconst { page = 1, limit = 20 } = query— the destructuring defaults only fire onundefined. Any caller reaching the provider outside the DTO layer (internal service calls, the admin controller) could passlimit: 0, which reachedtake(0)and madetotalPagesdivide by zero.Fix
Validation (400 with the existing centralised error shape):
pageis capped at the newMAX_MEMBER_PAGE(10_000) and must be an integer >= 1, keeping the worst-case offset finite.searchis capped at the newMAX_MEMBER_SEARCH_LENGTH(100) and trimmed before it reaches the provider.limitkeeps the inheritedMAX_LIMITcap and rejects zero / negative / non-integer values.Provider (defence in depth):
resolveMemberPage/resolveMemberLimitclamp whatever they are handed: oversized pages are capped atMAX_LIMITrather than rejected, out-of-range values are clamped into[1, max], andNaN/Infinity/ non-numeric input falls back to the shared default instead of leaking into the SQL builder. Fractional values are truncated soOFFSETstays an integer.resolveMemberSearchlowercases, bounds and escapes theLIKEwildcards (%,_) so a search term is matched literally instead of widening into a full-table match, and drops a whitespace-only term instead of filtering on%%.Tests
get-members.provider.spec.ts— 27 tests covering the defaults, the page-size ceiling, the page ceiling, out-of-range and non-numeric input, theLIKEescaping, the soft-delete filters, and the DTO validation errors for each rejected filter.