From 8b0c7508367abfa45cfc7e22214b7aaac294da44 Mon Sep 17 00:00:00 2001 From: waleed Date: Thu, 2 Jul 2026 11:41:54 -0700 Subject: [PATCH] fix(google-forms): fail-closed auth, legacy formId key fallback, idempotency - verifyAuth now rejects with 401 when no token is configured instead of silently allowing unauthenticated requests through - formatInput falls back to the legacy formId providerConfig key (pre-#3141 rename to triggerFormId) so old deployments keep working - add extractIdempotencyId keyed on formId:responseId to dedupe retried Apps Script deliveries --- .../lib/webhooks/providers/google-forms.ts | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/apps/sim/lib/webhooks/providers/google-forms.ts b/apps/sim/lib/webhooks/providers/google-forms.ts index 67e7fd8c997..ef943e7d408 100644 --- a/apps/sim/lib/webhooks/providers/google-forms.ts +++ b/apps/sim/lib/webhooks/providers/google-forms.ts @@ -29,7 +29,12 @@ export const googleFormsHandler: WebhookProviderHandler = { const responseId = (b?.responseId || b?.id || '') as string const createTime = (b?.createTime || b?.timestamp || new Date().toISOString()) as string const lastSubmittedTime = (b?.lastSubmittedTime || createTime) as string - const formId = (b?.formId || providerConfig.formId || '') as string + // triggerFormId is the current subBlock id; formId is the pre-#3141 id still + // present in provider_config for webhooks deployed before that rename. + const formId = (b?.formId || + providerConfig.triggerFormId || + providerConfig.formId || + '') as string const includeRaw = providerConfig.includeRawPayload !== false return { input: { @@ -46,7 +51,10 @@ export const googleFormsHandler: WebhookProviderHandler = { verifyAuth({ request, requestId, providerConfig }: AuthContext) { const expectedToken = providerConfig.token as string | undefined if (!expectedToken) { - return null + logger.warn(`[${requestId}] Google Forms webhook secret not configured`) + return new NextResponse('Unauthorized - Missing Google Forms webhook secret', { + status: 401, + }) } const secretHeaderName = providerConfig.secretHeaderName as string | undefined @@ -57,4 +65,17 @@ export const googleFormsHandler: WebhookProviderHandler = { return null }, + + extractIdempotencyId(body: unknown): string | null { + const b = body as Record + // Mirrors formatInput's responseId resolution. formId is deliberately not part + // of this key: the final key is already scoped by webhookId (one webhook per + // deployed form), and extractIdempotencyId has no access to providerConfig, so + // a body-only formId fallback would risk a bogus 'unknown' segment. + const responseId = (b?.responseId || b?.id) as string | undefined + if (typeof responseId !== 'string' || !responseId) { + return null + } + return `google_forms:${responseId}` + }, }