fix: validate swipe restaurant ids against room candidates

This commit is contained in:
2026-03-03 12:06:03 +08:00
parent f3d8a58603
commit 8b4ab415fd
3 changed files with 24 additions and 2 deletions
+6 -1
View File
@@ -38,7 +38,12 @@
- 后端补充房间号格式校验与明确错误提示; - 后端补充房间号格式校验与明确错误提示;
- 增加 E2E 用例覆盖“手输邀请码加入”。 - 增加 E2E 用例覆盖“手输邀请码加入”。
### P0-2 投票接口未校验 `restaurantId` 是否属于房间候选列表(可污染房间状态) ### P0-2 投票接口未校验 `restaurantId` 是否属于房间候选列表(可污染房间状态)【已完成】
- 修复状态:✅ 已完成(2026-03-03
- 修复内容:
- `POST /api/room/[id]/swipe` 增加 `restaurantId` 必须属于当前房间候选列表的强校验;
- 对非法 `restaurantId` 返回 400
- 增加对应 API 测试用例,防止状态污染回归。
- 证据: - 证据:
- `src/app/api/room/[id]/swipe/route.ts:25`(仅查 index - `src/app/api/room/[id]/swipe/route.ts:25`(仅查 index
- `src/app/api/room/[id]/swipe/route.ts:32-42`(即使 `restaurantIndex === -1` 仍可写入 `likes` 并可能设置 `match` - `src/app/api/room/[id]/swipe/route.ts:32-42`(即使 `restaurantIndex === -1` 仍可写入 `likes` 并可能设置 `match`
+15
View File
@@ -113,6 +113,21 @@ describe("POST /api/room/[id]/swipe", () => {
expect(res.status).toBe(400); expect(res.status).toBe(400);
}); });
it("returns 400 when restaurantId is not in room candidates", async () => {
mockAtomicUpdate.mockImplementation(async (_id, updater) => {
const data = structuredClone(TEST_ROOM_DATA);
return updater(data);
});
const req = createRequest("/api/room/ROOM01/swipe", {
method: "POST",
body: { userId: "user-1", restaurantId: "unknown-id", action: "like" },
});
const ctx = createRouteContext({ id: "ROOM01" });
const res = await POST(req, ctx);
expect(res.status).toBe(400);
});
it("returns 404 when room not found", async () => { it("returns 404 when room not found", async () => {
mockAtomicUpdate.mockResolvedValue(null); mockAtomicUpdate.mockResolvedValue(null);
+3 -1
View File
@@ -23,8 +23,10 @@ export const POST = apiHandler(async (req, { params }) => {
} }
const restaurantIndex = data.restaurants.findIndex((r) => r.id === rid); const restaurantIndex = data.restaurants.findIndex((r) => r.id === rid);
if (restaurantIndex < 0) {
throw new ApiError("restaurantId 不存在于该房间候选列表", 400);
}
const alreadySwiped = const alreadySwiped =
restaurantIndex >= 0 &&
restaurantIndex < (data.swipeCounts[userId] ?? 0); restaurantIndex < (data.swipeCounts[userId] ?? 0);
if (alreadySwiped) return data; if (alreadySwiped) return data;