Skip to content

Commit e3eaa69

Browse files
fix: return 400 instead of 500 for invalid eventTypeId in booking flow (#26732)
* fix: return 400 instead of 500 for invalid eventTypeId in booking flow Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * refactor: use ErrorWithCode instead of HttpError in BotDetectionService Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * refactor: use vi.stubEnv for safer environment variable handling in tests Co-Authored-By: benny@cal.com <sldisek783@gmail.com> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
1 parent fc0a2b5 commit e3eaa69

File tree

2 files changed

+54
-13
lines changed

2 files changed

+54
-13
lines changed

packages/features/bot-detection/BotDetectionService.test.ts

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import type { IncomingHttpHeaders } from "node:http";
2-
import { beforeEach, describe, expect, it, vi } from "vitest";
2+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
33

44
import type { EventTypeRepository } from "@calcom/features/eventtypes/repositories/eventTypeRepository";
55
import type { FeaturesRepository } from "@calcom/features/flags/features.repository";
6+
import { ErrorCode } from "@calcom/lib/errorCodes";
7+
import { ErrorWithCode } from "@calcom/lib/errors";
68
import { HttpError } from "@calcom/lib/http-error";
79

810
import { BotDetectionService } from "./BotDetectionService";
@@ -30,10 +32,8 @@ describe("BotDetectionService", () => {
3032
let mockHeaders: IncomingHttpHeaders;
3133

3234
beforeEach(() => {
33-
// Reset all mocks before each test
3435
vi.clearAllMocks();
3536

36-
// Setup mock repositories
3737
mockFeaturesRepository = {
3838
checkIfTeamHasFeature: vi.fn(),
3939
} as unknown as FeaturesRepository;
@@ -48,14 +48,15 @@ describe("BotDetectionService", () => {
4848
};
4949

5050
botDetectionService = new BotDetectionService(mockFeaturesRepository, mockEventTypeRepository);
51+
});
5152

52-
// Reset environment variable
53-
delete process.env.NEXT_PUBLIC_VERCEL_USE_BOTID_IN_BOOKER;
53+
afterEach(() => {
54+
vi.unstubAllEnvs();
5455
});
5556

5657
describe("checkBotDetection", () => {
5758
it("should return early if BotID is not enabled at instance level", async () => {
58-
process.env.NEXT_PUBLIC_VERCEL_USE_BOTID_IN_BOOKER = "0";
59+
vi.stubEnv("NEXT_PUBLIC_VERCEL_USE_BOTID_IN_BOOKER", "0");
5960

6061
await botDetectionService.checkBotDetection({
6162
eventTypeId: 123,
@@ -66,7 +67,7 @@ describe("BotDetectionService", () => {
6667
});
6768

6869
it("should return early if no eventTypeId is provided", async () => {
69-
process.env.NEXT_PUBLIC_VERCEL_USE_BOTID_IN_BOOKER = "1";
70+
vi.stubEnv("NEXT_PUBLIC_VERCEL_USE_BOTID_IN_BOOKER", "1");
7071

7172
await botDetectionService.checkBotDetection({
7273
headers: mockHeaders,
@@ -75,8 +76,43 @@ describe("BotDetectionService", () => {
7576
expect(mockEventTypeRepository.getTeamIdByEventTypeId).not.toHaveBeenCalled();
7677
});
7778

79+
it("should throw ErrorWithCode for invalid eventTypeId", async () => {
80+
vi.stubEnv("NEXT_PUBLIC_VERCEL_USE_BOTID_IN_BOOKER", "1");
81+
82+
await expect(
83+
botDetectionService.checkBotDetection({
84+
eventTypeId: "3823346KiEg1Zk6') OR 370=(SELECT 370 FROM PG_SLEEP(15))--" as unknown as number,
85+
headers: mockHeaders,
86+
})
87+
).rejects.toThrow(ErrorWithCode);
88+
89+
await expect(
90+
botDetectionService.checkBotDetection({
91+
eventTypeId: -1,
92+
headers: mockHeaders,
93+
})
94+
).rejects.toThrow(ErrorWithCode);
95+
96+
await expect(
97+
botDetectionService.checkBotDetection({
98+
eventTypeId: 1.5,
99+
headers: mockHeaders,
100+
})
101+
).rejects.toThrow(ErrorWithCode);
102+
103+
try {
104+
await botDetectionService.checkBotDetection({
105+
eventTypeId: -1,
106+
headers: mockHeaders,
107+
});
108+
} catch (error) {
109+
expect(error).toBeInstanceOf(ErrorWithCode);
110+
expect((error as ErrorWithCode).code).toBe(ErrorCode.BadRequest);
111+
}
112+
});
113+
78114
it("should return early if event type has no teamId", async () => {
79-
process.env.NEXT_PUBLIC_VERCEL_USE_BOTID_IN_BOOKER = "1";
115+
vi.stubEnv("NEXT_PUBLIC_VERCEL_USE_BOTID_IN_BOOKER", "1");
80116
vi.mocked(mockEventTypeRepository.getTeamIdByEventTypeId).mockResolvedValue({
81117
teamId: null,
82118
});
@@ -90,7 +126,7 @@ describe("BotDetectionService", () => {
90126
});
91127

92128
it("should return early if BotID feature is not enabled for the team", async () => {
93-
process.env.NEXT_PUBLIC_VERCEL_USE_BOTID_IN_BOOKER = "1";
129+
vi.stubEnv("NEXT_PUBLIC_VERCEL_USE_BOTID_IN_BOOKER", "1");
94130
vi.mocked(mockEventTypeRepository.getTeamIdByEventTypeId).mockResolvedValue({
95131
teamId: 456,
96132
});
@@ -107,7 +143,7 @@ describe("BotDetectionService", () => {
107143
});
108144

109145
it("should throw HttpError when a bot is detected", async () => {
110-
process.env.NEXT_PUBLIC_VERCEL_USE_BOTID_IN_BOOKER = "1";
146+
vi.stubEnv("NEXT_PUBLIC_VERCEL_USE_BOTID_IN_BOOKER", "1");
111147
vi.mocked(mockEventTypeRepository.getTeamIdByEventTypeId).mockResolvedValue({
112148
teamId: 456,
113149
});
@@ -140,7 +176,7 @@ describe("BotDetectionService", () => {
140176
});
141177

142178
it("should pass when a human is detected", async () => {
143-
process.env.NEXT_PUBLIC_VERCEL_USE_BOTID_IN_BOOKER = "1";
179+
vi.stubEnv("NEXT_PUBLIC_VERCEL_USE_BOTID_IN_BOOKER", "1");
144180
vi.mocked(mockEventTypeRepository.getTeamIdByEventTypeId).mockResolvedValue({
145181
teamId: 456,
146182
});
@@ -167,7 +203,7 @@ describe("BotDetectionService", () => {
167203

168204
it("should check feature flag with correct teamId", async () => {
169205
const teamId = 789;
170-
process.env.NEXT_PUBLIC_VERCEL_USE_BOTID_IN_BOOKER = "1";
206+
vi.stubEnv("NEXT_PUBLIC_VERCEL_USE_BOTID_IN_BOOKER", "1");
171207
vi.mocked(mockEventTypeRepository.getTeamIdByEventTypeId).mockResolvedValue({
172208
teamId,
173209
});

packages/features/bot-detection/BotDetectionService.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import type { IncomingHttpHeaders } from "node:http";
33

44
import type { EventTypeRepository } from "@calcom/features/eventtypes/repositories/eventTypeRepository";
55
import type { FeaturesRepository } from "@calcom/features/flags/features.repository";
6+
import { ErrorCode } from "@calcom/lib/errorCodes";
7+
import { ErrorWithCode } from "@calcom/lib/errors";
68
import { HttpError } from "@calcom/lib/http-error";
79
import logger from "@calcom/lib/logger";
810

@@ -34,7 +36,10 @@ export class BotDetectionService {
3436
}
3537

3638
if (!Number.isInteger(eventTypeId) || eventTypeId <= 0) {
37-
throw new Error(`Invalid eventTypeId: ${eventTypeId}. Must be a positive integer.`);
39+
throw new ErrorWithCode(
40+
ErrorCode.BadRequest,
41+
`Invalid eventTypeId: ${eventTypeId}. Must be a positive integer.`
42+
);
3843
}
3944

4045
// Fetch only the teamId from the event type

0 commit comments

Comments
 (0)