fix(pagination): enforce bounded page size on sensitive list/export endpoints (SB-C02) - #1287
Merged
Merged
Conversation
…ndpoints SB-C02: unbounded pagination let a single authenticated request pull up to 50,000 full records (sensitive-species coordinates, confidential records, observer PII). The `-1` "unlimited" sentinel and un-floored `Math.min` caps allowed unbounded or negative limits to reach the database. - Add config/pagination.js with hard ceilings (formListMax 1000, formExportMax 50000, userListMax 5000, zoneListMax 50000, alertListMax 500, publicMax 1000). - Add helpers/pagination.js: resolveLimit (-1/negative -> bounded max, never unbounded), clampLimit (floor-then-cap), clampOffset. - formMethods: <form>:list -> formListMax; <form>:export -> formExportMax via a threaded opts.max; public-context clamp preserved. - formActions: strip limit/offset before count() so totals stay true. - user:list, zone:list (previously fully unbounded), and suspiciousActivityAlert:list now bounded. - auditHelpers.validatePagination delegates to the shared helpers (dedup). - Tests (Postgres-only): helper units plus list/export/user/zone/alert clamp and count-integrity assertions. Reference-data endpoints (helpers/paging.js consumers) left untouched by design; client-side follow-ups tracked in code6x/bspb#57 and code6x/bspb#58. Refs code6x/bspb#1 Claude-Session: https://claude.ai/code/session_018gAWWqf9usV6Wb7ERB4xNY
groupsky
force-pushed
the
fix/sb-c02-pagination-limits
branch
from
July 11, 2026 19:23
594bd30 to
cf88a2e
Compare
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.
SB-C02 · Unbounded pagination (Critical)
Problem
List/export endpoints let a single authenticated request pull an unbounded number of full records — sensitive-species exact coordinates,
confidentialrecords, and observer PII included. The-1"unlimited" sentinel mapped to 20,000/50,000 (or truly unbounded), positive limits were honored verbatim with no ceiling, and un-flooredMath.mincaps letlimit=-1slip past intended limits (even dropping theLIMITclause entirely onuser:list).Fix
Centralised, floor-then-cap pagination with hard server-side ceilings that apply to every role and input:
config/pagination.js—formListMax 1000,formExportMax 50000,userListMax 5000,zoneListMax 50000,alertListMax 500,publicMax 1000.helpers/pagination.js—resolveLimit(-1/negative → boundedmax, never unbounded or the accidental→1collapse),clampLimit(floor-then-cap),clampOffset.initializers/formMethods.js—<form>:list→formListMax;<form>:export→formExportMaxthreaded via a newopts.max; public-context clamp preserved.initializers/formActions.js— striplimit/offsetbeforecount()so pagination totals stay true.actions/user.js,actions/zones.js(was fully unbounded — no limit applied),actions/suspiciousActivityAlerts.js— now bounded.helpers/auditHelpers.js—validatePaginationdelegates to the shared helpers (dedup; existing tests are the regression guard).Reference-data endpoints (
helpers/paging.jsconsumers:location:*,ebp:speciesList,nomenclature:*) are intentionally out of scope — clients rely on their full-list-1default; they need a separate client-pagination / static-JSON / permissioned-storage migration.Testing
__tests__/helpers/pagination.js,__tests__/actions/pagination.js): helper units +list/export/user/zone/alertclamp and count-integrity assertions, including the-1 → max(not1, not unbounded) property.test/banner.js(locale/banner rendering, fails identically onmaster) and__tests__/initializers/filestorage.js(heif-convertbinary absent locally; CI provides it).standardlint clean.Android is unaffected (forms are POST; no
user:list). Exports remain bounded (50000 = prior admin ceiling); chunking/approval of very large exports is deferred to the SB-C05/SB-C03 work.