Skip to content

Commit aacbbc1

Browse files
fix: Rate limit /router endpoint (calcom#21940)
* fix: add rate limiting for /router/embed * Abstract out hashing code --------- Co-authored-by: Hariom <hariombalhara@gmail.com>
1 parent 3714d5f commit aacbbc1

1 file changed

Lines changed: 21 additions & 0 deletions

File tree

packages/lib/server/getRoutedUrl.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// !IMPORTANT! changes to this file requires publishing new version of platform libraries in order for the changes to be applied to APIV2
2+
import { createHash } from "crypto";
23
import type { GetServerSidePropsContext } from "next";
34
import { stringify } from "querystring";
45
import { v4 as uuidv4 } from "uuid";
@@ -16,6 +17,7 @@ import { getUrlSearchParamsToForward } from "@calcom/app-store/routing-forms/pag
1617
import type { FormResponse } from "@calcom/app-store/routing-forms/types/types";
1718
import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains";
1819
import { isAuthorizedToViewFormOnOrgDomain } from "@calcom/features/routing-forms/lib/isAuthorizedToViewForm";
20+
import { checkRateLimitAndThrowError } from "@calcom/lib/checkRateLimitAndThrowError";
1921
import logger from "@calcom/lib/logger";
2022
import { withReporting } from "@calcom/lib/sentryWrapper";
2123
import { RoutingFormRepository } from "@calcom/lib/server/repository/routingForm";
@@ -30,6 +32,18 @@ const querySchema = z
3032
})
3133
.catchall(z.string().or(z.array(z.string())));
3234

35+
const getDeterministicHashForResponse = (fieldsResponses: Record<string, unknown>) => {
36+
const sortedFields = Object.keys(fieldsResponses)
37+
.sort()
38+
.reduce((obj: Record<string, unknown>, key) => {
39+
obj[key] = fieldsResponses[key];
40+
return obj;
41+
}, {});
42+
const paramsString = JSON.stringify(sortedFields);
43+
const hash = createHash("sha256").update(paramsString).digest("hex");
44+
return hash;
45+
};
46+
3347
function hasEmbedPath(pathWithQuery: string) {
3448
const onlyPath = pathWithQuery.split("?")[0];
3549
return onlyPath.endsWith("/embed") || onlyPath.endsWith("/embed/");
@@ -57,6 +71,13 @@ const _getRoutedUrl = async (context: Pick<GetServerSidePropsContext, "query" |
5771
"cal.queueFormResponse": queueFormResponseParam,
5872
...fieldsResponses
5973
} = queryParsed.data;
74+
75+
const responseHash = getDeterministicHashForResponse(fieldsResponses);
76+
77+
await checkRateLimitAndThrowError({
78+
identifier: `form:${formId}:hash:${responseHash}`,
79+
});
80+
6081
const isBookingDryRun = isBookingDryRunParam === "true";
6182
const shouldQueueFormResponse = queueFormResponseParam === "true";
6283
const paramsToBeForwardedAsIs = {

0 commit comments

Comments
 (0)