fix(19): WR-07 reject route ids with trailing garbage via strict integer parse
This commit is contained in:
@@ -78,6 +78,19 @@ const noEchoHook = (result: { success: boolean }, c: Context) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WR-07: strictly parse a positive-integer route param. parseInt('12abc', 10) returns 12
|
||||||
|
* and passes an isNaN guard, silently accepting malformed ids. Number('12abc') is NaN, so
|
||||||
|
* Number.isInteger(Number(raw)) rejects trailing garbage. Returns null for anything that is
|
||||||
|
* not a whole positive integer (empty, '12abc', '1.5', '-3', '0', etc.) so the caller can 400.
|
||||||
|
*/
|
||||||
|
function parsePositiveIntParam(raw: string | undefined): number | null {
|
||||||
|
if (raw === undefined || raw.trim() === '') return null;
|
||||||
|
const n = Number(raw);
|
||||||
|
if (!Number.isInteger(n) || n <= 0) return null;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// GET /api/admin/members
|
// GET /api/admin/members
|
||||||
//
|
//
|
||||||
@@ -217,8 +230,8 @@ adminRouter.post(
|
|||||||
'/members/:id/password',
|
'/members/:id/password',
|
||||||
zValidator('json', resetPasswordSchema, noEchoHook),
|
zValidator('json', resetPasswordSchema, noEchoHook),
|
||||||
async (c) => {
|
async (c) => {
|
||||||
const targetId = parseInt(c.req.param('id'), 10);
|
const targetId = parsePositiveIntParam(c.req.param('id'));
|
||||||
if (isNaN(targetId)) {
|
if (targetId === null) {
|
||||||
return c.json({ error: 'Invalid member id' }, 400);
|
return c.json({ error: 'Invalid member id' }, 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,8 +329,8 @@ adminRouter.get('/calendars', async (c) => {
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
adminRouter.put('/calendars/:id/shared', async (c) => {
|
adminRouter.put('/calendars/:id/shared', async (c) => {
|
||||||
const targetId = parseInt(c.req.param('id'), 10);
|
const targetId = parsePositiveIntParam(c.req.param('id'));
|
||||||
if (isNaN(targetId)) {
|
if (targetId === null) {
|
||||||
return c.json({ error: 'Invalid calendar id' }, 400);
|
return c.json({ error: 'Invalid calendar id' }, 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -483,6 +483,19 @@ describe('PUT /api/admin/calendars/:id/shared', () => {
|
|||||||
.limit(1);
|
.limit(1);
|
||||||
expect(rowA.isShared).toBe(true);
|
expect(rowA.isShared).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('WR-07: rejects a calendar id with trailing garbage (e.g. "1abc") with 400', async () => {
|
||||||
|
const adminId = await seedUser('admin-shared-badid', true);
|
||||||
|
currentDevUserId = adminId;
|
||||||
|
const app = await getApp();
|
||||||
|
|
||||||
|
// parseInt('1abc', 10) === 1 would have silently accepted this; the strict
|
||||||
|
// Number.isInteger parse must reject it as a malformed id.
|
||||||
|
const res = await app.fetch(jsonRequest('PUT', '/api/admin/calendars/1abc/shared'));
|
||||||
|
expect(res.status).toBe(400);
|
||||||
|
const body = (await res.json()) as { error: string };
|
||||||
|
expect(body.error).toBe('Invalid calendar id');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
|
|||||||
Reference in New Issue
Block a user