fix(security): add webhook HMAC signature verification for Airtable, HubSpot, and Webflow#4550
fix(security): add webhook HMAC signature verification for Airtable, HubSpot, and Webflow#4550waleedlatif1 wants to merge 4 commits intostagingfrom
Conversation
…HubSpot, and Webflow - Airtable: verify X-Airtable-Content-MAC via HMAC-SHA256 of raw body; strip hmac-sha256= prefix before comparing; fail-open with warning when macSecretBase64 is absent (existing webhooks predating secret storage) - HubSpot: verify X-HubSpot-Signature via SHA-256(clientSecret + body) per v1 spec - Webflow: verify X-Webflow-Signature via HMAC-SHA256(secretKey, timestamp:body); store secretKey from webhook creation response; add 5-min replay protection; fail-open when secretKey absent for pre-existing webhooks - tool-schemas-v1.ts: linter formatting cleanup (computed keys)
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryHigh Risk Overview Airtable now verifies Also normalizes the generated Reviewed by Cursor Bugbot for commit 6ccc7b4. Configure here. |
Greptile SummaryThis PR adds HMAC-based webhook signature verification to three providers (Airtable, HubSpot, Webflow) that previously had no
Confidence Score: 5/5Safe to merge — the three new verifyAuth implementations are self-contained, follow provider-specific signing specs, and use constant-time comparison throughout. All three handlers correctly compute and compare HMAC/hash signatures using Node's built-in crypto module. Constant-time comparison via safeCompare is used in every path. Fail-open/fail-closed decisions match the PR description and documented backwards-compat constraints. No functional regressions found in existing subscription creation or deletion logic. No files require special attention — the security-critical verification paths in all three provider files look correct. Important Files Changed
Reviews (3): Last reviewed commit: "fix(security): correct Webflow timestamp..." | Re-trigger Greptile |
… hmac encoding - webflow: compare Date.now()/1000 against x-webflow-timestamp (both in Unix seconds) — the original Date.now() - ts comparison always exceeded the 300 000ms threshold, rejecting every valid webhook - airtable: use utf8 encoding in HMAC update instead of ascii — ascii silently mangles bytes above 127, producing an incorrect digest for non-ASCII payloads - hubspot: add TSDoc comment clarifying v1 signature scheme is intentional for CRM subscriptions
|
@greptile |
|
@cursor review |
… artifacts - webflow: wrap HMAC computation and safeCompare in try-catch to return a clean 401 instead of an unhandled 500 when secretKey is a non-string truthy value (mirrors the defensive pattern already used by Airtable and HubSpot in the same PR) - airtable: remove CRITICAL_TRACE/TRACE/DEBUG log prefixes, redundant "Error logging handled by logging session" comments, and other stale implementation notes that pre-date the refactor
…s milliseconds Per official Webflow docs (example value 1722370035277, 13 digits), the header is Unix milliseconds. Comparing Date.now()/1000 against a ms timestamp produced a permanently-negative delta, making replay protection always pass. Reverted to Date.now() - ts > 5 * 60 * 1000, matching Webflow's own reference implementation. Also normalized caught error via toError().
|
@cursor review |
|
@greptile |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 6ccc7b4. Configure here.
Summary
verifyAuthusing HMAC-SHA256 of the raw body keyed by the base64-decodedmacSecretBase64from providerConfig; compare against thehmac-sha256=<hex>prefix inX-Airtable-Content-MAC; fail-open with a warning whenmacSecretBase64is absent (existing webhooks created before secret storage — no migration path since Airtable only returns the secret once at creation)verifyAuthusing SHA-256(clientSecret+ raw body) compared againstX-HubSpot-Signatureper the v1 CRM webhook spec;clientSecretis already a required subBlock field in all HubSpot triggers so no existing webhooks breakverifyAuthusing HMAC-SHA256(secretKey,${timestamp}:${rawBody}) compared againstX-Webflow-Signature; store thesecretKeyfrom the webhook creation API response in providerConfig; add 5-minute replay protection viax-webflow-timestamp; fail-open whensecretKeyis absent for pre-existing webhookssafeComparefrom@sim/security/comparefor constant-time comparison and fail-closed on missing secrets (except for migration backwards-compat cases which fail-open with a warning log)Type of Change
Testing
Tested manually. Algorithms validated against official provider docs: Airtable webhooks overview, HubSpot webhook validation docs (v1 confirmed for CRM object subscriptions), Webflow Data API webhook docs. Airtable
hmac-sha256=prefix confirmed from live header examples in community threads.Checklist