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" ;
23import type { GetServerSidePropsContext } from "next" ;
34import { stringify } from "querystring" ;
45import { v4 as uuidv4 } from "uuid" ;
@@ -16,6 +17,7 @@ import { getUrlSearchParamsToForward } from "@calcom/app-store/routing-forms/pag
1617import type { FormResponse } from "@calcom/app-store/routing-forms/types/types" ;
1718import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains" ;
1819import { isAuthorizedToViewFormOnOrgDomain } from "@calcom/features/routing-forms/lib/isAuthorizedToViewForm" ;
20+ import { checkRateLimitAndThrowError } from "@calcom/lib/checkRateLimitAndThrowError" ;
1921import logger from "@calcom/lib/logger" ;
2022import { withReporting } from "@calcom/lib/sentryWrapper" ;
2123import { 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+
3347function 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