Skip to content

fix(activity): clamp pagination params#282

Open
sevencat2004 wants to merge 2 commits into
profullstack:masterfrom
sevencat2004:fix/activity-pagination-bounds
Open

fix(activity): clamp pagination params#282
sevencat2004 wants to merge 2 commits into
profullstack:masterfrom
sevencat2004:fix/activity-pagination-bounds

Conversation

@sevencat2004
Copy link
Copy Markdown

Summary

  • clamp /api/activity pagination query params before building the Supabase range
  • default invalid/non-positive limit to 20 and cap large limits at 50
  • default invalid/negative offset to 0
  • add route coverage for auth, invalid pagination, and capped limits

Fixes #281

Payment

  • Solana wallet: Dy4yMkjCfupxaURt6iTMUrxqSDEmAJPPkKF66QahxJZD

Test plan

  • npx.cmd vitest run src/app/api/activity/route.test.ts
  • git diff --check -- src/app/api/activity/route.ts src/app/api/activity/route.test.ts

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 28, 2026

Greptile Summary

This PR hardens the /api/activity pagination parameters by replacing bare parseInt calls with two small helper functions that validate and clamp limit (defaults to 20, max 50) and offset (defaults to 0, never negative), preventing malformed query strings from producing invalid Supabase range calls.

  • route.ts: introduces parsePositiveInt and parseNonNegativeInt helpers and wires them into the existing limit/offset parsing; the rest of the route is unchanged.
  • route.test.ts: new test file covering unauthenticated requests, negative-param clamping, large-limit capping, and a valid within-range request.

Confidence Score: 5/5

Safe to merge — the change is additive input sanitization with no side-effects on the happy path.

The two helper functions correctly reject zero, negative, and NaN values and fall back to safe defaults. The Math.min(..., 50) cap is preserved, and the Supabase range call remains structurally identical. All four test scenarios confirm the clamping behaviour matches expectations.

No files require special attention.

Important Files Changed

Filename Overview
src/app/api/activity/route.ts Adds two helper functions to sanitize pagination params; replaces bare parseInt calls with clamped, validated equivalents. Logic is correct — negative/zero/NaN limits fall back to 20, negative offsets fall back to 0, large limits are capped at 50.
src/app/api/activity/route.test.ts New test file covering auth rejection, negative param clamping, large-limit capping, and a valid within-range request. Mock chain matches the actual Supabase call chain in the route.

Sequence Diagram

sequenceDiagram
    participant Client
    participant GET /api/activity
    participant parsePositiveInt
    participant parseNonNegativeInt
    participant Supabase

    Client->>GET /api/activity: GET ?limit=X&offset=Y
    GET /api/activity->>getAuthContext: verify session
    alt not authenticated
        getAuthContext-->>GET /api/activity: null
        GET /api/activity-->>Client: 401 Unauthorized
    else authenticated
        getAuthContext-->>GET /api/activity: { user, supabase }
        GET /api/activity->>parsePositiveInt: parse limit (fallback 20)
        parsePositiveInt-->>GET /api/activity: clamped limit (1-50)
        GET /api/activity->>parseNonNegativeInt: parse offset (fallback 0)
        parseNonNegativeInt-->>GET /api/activity: clamped offset (>=0)
        GET /api/activity->>Supabase: .from(activities).select().eq().order().range(offset, offset+limit-1)
        Supabase-->>GET /api/activity: { data, error, count }
        GET /api/activity-->>Client: 200 { data, pagination: { total, limit, offset } }
    end
Loading

Reviews (2): Last reviewed commit: "test(activity): cover valid pagination p..." | Re-trigger Greptile

Comment on lines +4 to +12
function parsePositiveInt(value: string | null, fallback: number): number {
const parsed = Number.parseInt(value || "", 10);
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
}

function parseNonNegativeInt(value: string | null, fallback: number): number {
const parsed = Number.parseInt(value || "", 10);
return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Number.isFinite is redundant here. Number.parseInt can only return an integer or NaN — it never produces Infinity — so the > 0 / >= 0 guards already reject NaN on their own. The check is harmless but misleading (it implies parseInt could return Infinity).

Suggested change
function parsePositiveInt(value: string | null, fallback: number): number {
const parsed = Number.parseInt(value || "", 10);
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
}
function parseNonNegativeInt(value: string | null, fallback: number): number {
const parsed = Number.parseInt(value || "", 10);
return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback;
}
function parsePositiveInt(value: string | null, fallback: number): number {
const parsed = Number.parseInt(value || "", 10);
return parsed > 0 ? parsed : fallback;
}
function parseNonNegativeInt(value: string | null, fallback: number): number {
const parsed = Number.parseInt(value || "", 10);
return parsed >= 0 ? parsed : fallback;
}

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment thread src/app/api/activity/route.test.ts
@sevencat2004
Copy link
Copy Markdown
Author

Follow-up pushed in b1a1d5f: added the happy-path pagination coverage Greptile suggested, verifying explicit valid limit and offset values are passed through to the Supabase range unchanged.\n\nValidation run locally:\n- corepack pnpm test -- src/app/api/activity/route.test.ts

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.

Activity feed accepts negative pagination ranges

1 participant