Skip to content

fix: BE-18 — bound member-list query parameters (#638) - #825

Merged
Qoder-Voidd merged 3 commits into
Akazajan:mainfrom
VeronicDev:fix/issue-638-be-18-bound-member-list-query
Oct 20, 2026
Merged

Qoder-Voidd merged 3 commits into
Akazajan:mainfrom
VeronicDev:fix/issue-638-be-18-bound-member-list-query

Conversation

@VeronicDev

Copy link
Copy Markdown
Contributor

Closes #638

Problem

MemberQueryDto inherited page / limit from PaginationDto, which caps limit at MAX_LIMIT but left page completely unbounded, and search had no length bound at all. ?page=999999999 produced a multi-billion row OFFSET that forced Postgres to scan and discard the whole table before returning nothing.

GetMembersProvider then trusted the raw query object with const { page = 1, limit = 20 } = query — the destructuring defaults only fire on undefined. Any caller reaching the provider outside the DTO layer (internal service calls, the admin controller) could pass limit: 0, which reached take(0) and made totalPages divide by zero.

Fix

Validation (400 with the existing centralised error shape):

  • page is capped at the new MAX_MEMBER_PAGE (10_000) and must be an integer >= 1, keeping the worst-case offset finite.
  • search is capped at the new 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. Fractional values are truncated so OFFSET stays an integer.
  • resolveMemberSearch lowercases, bounds and escapes the LIKE wildcards (%, _) 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, the LIKE escaping, the soft-delete filters, and the DTO validation errors for each rejected filter.

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.
@drips-wave

drips-wave Bot commented Oct 19, 2026

Copy link
Copy Markdown

@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! 🚀

Learn more about application limits

@Qoder-Voidd Qoder-Voidd left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@Qoder-Voidd
Qoder-Voidd merged commit 7876532 into Akazajan:main Oct 20, 2026
0 of 7 checks passed
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.

BE-18 — Bound member-list query parameters

2 participants