Skip to content

Commit 2bf2df3

Browse files
fix(comments): validate pagination params (#131)
Co-authored-by: rissrice2105-agent <289161642+rissrice2105-agent@users.noreply.github.com>
1 parent 0f5b968 commit 2bf2df3

2 files changed

Lines changed: 22 additions & 2 deletions

File tree

src/app/api/torrents/[id]/comments/route.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,25 @@ describe('Torrent Comments API', () => {
134134
expect(mockService.getCommentsWithUserVotes).toHaveBeenCalledWith(TEST_TORRENT_ID, null, 10, 20);
135135
});
136136

137+
it('should fall back to safe pagination for malformed params', async () => {
138+
const mockService = {
139+
getCommentsWithUserVotes: vi.fn().mockResolvedValue([]),
140+
getCommentCount: vi.fn().mockResolvedValue(0),
141+
};
142+
143+
(getCommentsService as ReturnType<typeof vi.fn>).mockReturnValue(mockService);
144+
(getActiveProfileId as ReturnType<typeof vi.fn>).mockResolvedValue(null);
145+
146+
const request = new NextRequest(
147+
`http://localhost/api/torrents/${TEST_TORRENT_ID}/comments?limit=10items&offset=-20`
148+
);
149+
const response = await GET(request, { params: Promise.resolve({ id: TEST_TORRENT_ID }) });
150+
151+
expect(response.status).toBe(200);
152+
expect(mockService.getCommentsWithUserVotes).toHaveBeenCalledWith(TEST_TORRENT_ID, null, 50, 0);
153+
await expect(response.json()).resolves.toMatchObject({ limit: 50, offset: 0 });
154+
});
155+
137156
it('should return 400 for missing torrent ID', async () => {
138157
const request = new NextRequest('http://localhost/api/torrents//comments');
139158
const response = await GET(request, { params: Promise.resolve({ id: '' }) });

src/app/api/torrents/[id]/comments/route.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { NextRequest, NextResponse } from 'next/server';
1212
import { getCommentsService } from '@/lib/comments';
1313
import { getAuthenticatedUser } from '@/lib/auth';
1414
import { getActiveProfileId } from '@/lib/profiles/profile-utils';
15+
import { parseIntegerParam } from '@/lib/api/pagination';
1516

1617
interface RouteParams {
1718
params: Promise<{ id: string }>;
@@ -50,8 +51,8 @@ export async function GET(
5051

5152
// Parse pagination params
5253
const searchParams = request.nextUrl.searchParams;
53-
const limit = parseInt(searchParams.get('limit') ?? '50', 10);
54-
const offset = parseInt(searchParams.get('offset') ?? '0', 10);
54+
const limit = parseIntegerParam(searchParams.get('limit'), { min: 1, max: 100 }) ?? 50;
55+
const offset = parseIntegerParam(searchParams.get('offset'), { min: 0 }) ?? 0;
5556

5657
// DHT torrents (non-UUID IDs) don't support comments
5758
// Return empty results instead of failing

0 commit comments

Comments
 (0)