Skip to content

Add webhook verification functionality for secure payload validation #7103

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

jnsdls
Copy link
Member

@jnsdls jnsdls commented May 20, 2025

PR-Codex overview

This PR primarily updates the zod dependency to version 3.25.24 across multiple packages and introduces a new webhook verification feature with associated tests.

Detailed summary

  • Updated zod from 3.24.3 to 3.25.24 in several package.json files.
  • Increased size limits in packages/thirdweb/.size-limit.json.
  • Added webhook verification functionality in changeset/webhook-verification.md.
  • Introduced Webhook.parse function for verifying webhook signatures.
  • Added tests for the Webhook.parse function in packages/thirdweb/src/bridge/Webhook.test.ts.

The following files were skipped due to too many changes: pnpm-lock.yaml

✨ Ask PR-Codex anything about this PR by commenting with /codex {your question}

Summary by CodeRabbit

  • New Features

    • Introduced secure webhook verification with signature and timestamp validation for incoming webhooks.
    • Added support for version 2 webhook payloads and multiple signature header formats.
    • Enabled configurable timestamp tolerance to prevent replay attacks.
  • Bug Fixes

    • Enhanced error handling for missing headers, invalid signatures, expired timestamps, and unsupported webhook versions.
  • Tests

    • Added comprehensive tests covering webhook processing, signature verification, payload validation, and version enforcement.

Copy link

vercel bot commented May 20, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
docs-v2 ✅ Ready (Inspect) Visit Preview 💬 Add feedback May 23, 2025 7:51pm
login ✅ Ready (Inspect) Visit Preview 💬 Add feedback May 23, 2025 7:51pm
thirdweb_playground ✅ Ready (Inspect) Visit Preview 💬 Add feedback May 23, 2025 7:51pm
thirdweb-www ✅ Ready (Inspect) Visit Preview 💬 Add feedback May 23, 2025 7:51pm
wallet-ui ✅ Ready (Inspect) Visit Preview 💬 Add feedback May 23, 2025 7:51pm

Copy link

changeset-bot bot commented May 20, 2025

🦋 Changeset detected

Latest commit: 9aea6f4

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
thirdweb Minor
@thirdweb-dev/wagmi-adapter Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link
Contributor

coderabbitai bot commented May 20, 2025

Walkthrough

This update introduces webhook verification to the thirdweb package by adding a Webhook.parse function. The function validates incoming webhook requests by checking signatures, timestamps, and payload versions. New types and exports are added for webhook payloads, and a comprehensive test suite is included to ensure correct verification and error handling. Dependency versions for zod were updated across several packages.

Changes

File(s) Change Summary
.changeset/webhook-verification.md Documents the addition of webhook verification, describing the new Webhook.parse function, its signature and timestamp validation, version support, and usage example.
packages/thirdweb/src/bridge/Webhook.ts Adds the parse function for webhook verification, including signature and timestamp checks, JSON parsing, version enforcement, and the WebhookPayload type definition.
packages/thirdweb/src/bridge/Webhook.test.ts Introduces a test suite for the parse function, covering valid and invalid signatures, header checks, timestamp tolerance, JSON parsing, and version enforcement.
packages/thirdweb/src/bridge/index.ts Updates exports to include the new webhook functionality: exports all from Webhook.js as Webhook, and explicitly exports parse and the WebhookPayload type. Also reorders the Action export to BridgeAction.
apps/dashboard/package.json, apps/playground-web/package.json, apps/wallet-ui/package.json, packages/service-utils/package.json, packages/thirdweb/package.json Updates the zod dependency version from 3.24.3 to 3.25.24 and reformats some JSON arrays from multiline to single-line without changing content.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Server
    participant Webhook

    Client->>Server: Sends webhook request (payload, headers)
    Server->>Webhook: Calls parse(payload, headers, secret, tolerance?)
    Webhook->>Server: Validates signature & timestamp, parses payload
    alt Valid request & supported version
        Webhook-->>Server: Returns parsed payload
        Server-->>Client: Processes webhook
    else Invalid signature/timestamp/version
        Webhook-->>Server: Throws error
        Server-->>Client: Responds with error
    end
Loading

Suggested labels

Portal


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3c5e0c1 and 9aea6f4.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (9)
  • apps/dashboard/package.json (1 hunks)
  • apps/playground-web/package.json (1 hunks)
  • apps/wallet-ui/package.json (1 hunks)
  • packages/service-utils/package.json (2 hunks)
  • packages/thirdweb/.size-limit.json (1 hunks)
  • packages/thirdweb/package.json (4 hunks)
  • packages/thirdweb/src/bridge/Webhook.test.ts (1 hunks)
  • packages/thirdweb/src/bridge/Webhook.ts (1 hunks)
  • packages/thirdweb/src/bridge/index.ts (2 hunks)
✅ Files skipped from review due to trivial changes (3)
  • apps/playground-web/package.json
  • apps/wallet-ui/package.json
  • packages/thirdweb/.size-limit.json
🚧 Files skipped from review as they are similar to previous changes (6)
  • apps/dashboard/package.json
  • packages/service-utils/package.json
  • packages/thirdweb/src/bridge/Webhook.test.ts
  • packages/thirdweb/src/bridge/index.ts
  • packages/thirdweb/package.json
  • packages/thirdweb/src/bridge/Webhook.ts
⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: E2E Tests (pnpm, esbuild)
  • GitHub Check: E2E Tests (pnpm, webpack)
  • GitHub Check: Unit Tests
  • GitHub Check: E2E Tests (pnpm, vite)
  • GitHub Check: Size
  • GitHub Check: Build Packages
  • GitHub Check: Lint Packages
  • GitHub Check: Analyze (javascript)
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions github-actions bot added packages SDK Involves changes to the thirdweb SDK labels May 20, 2025
Copy link
Member Author

jnsdls commented May 20, 2025


How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:

  • merge-queue - adds this PR to the back of the merge queue
  • hotfix - for urgent hot fixes, skip the queue and merge this PR next

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

@jnsdls jnsdls marked this pull request as ready for review May 20, 2025 23:31
@jnsdls jnsdls requested review from a team as code owners May 20, 2025 23:31
@jnsdls jnsdls force-pushed the Add_webhook_verification_functionality_for_secure_payload_validation branch from 09b2a7e to 4884440 Compare May 20, 2025 23:32
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 1

🧹 Nitpick comments (2)
packages/thirdweb/src/bridge/Webhook.test.ts (1)

127-127: Remove debug console.log statement.

This debug log statement should be removed before merging the code.

-    console.log("Payload string:", v1PayloadString); // Debug log
packages/thirdweb/src/bridge/Webhook.ts (1)

76-82: Consider adding payload structure validation.

After parsing the JSON, it would be beneficial to validate that the payload structure matches the expected structure for the given version, especially for critical fields. This would provide earlier and clearer error messages for malformed payloads.

🛑 Comments failed to post (1)
packages/thirdweb/src/bridge/Webhook.ts (1)

85-89: 🛠️ Refactor suggestion

Add explicit version validation to ensure only version 2 is accepted.

Currently, the code rejects version 1 payloads but doesn't explicitly verify that the version is 2. This could potentially allow payloads with other version numbers to be processed without proper validation.

  // Check version after successful JSON parsing
  if (parsedPayload.version === 1) {
    throw new Error(
      "Invalid webhook payload: version 1 is no longer supported, please upgrade to webhook version 2.",
    );
  }
+  // Ensure only version 2 is accepted
+  if (parsedPayload.version !== 2) {
+    throw new Error(
+      `Invalid webhook payload: unsupported version ${parsedPayload.version}. Only version 2 is supported.`
+    );
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

  // Check version after successful JSON parsing
  if (parsedPayload.version === 1) {
    throw new Error(
      "Invalid webhook payload: version 1 is no longer supported, please upgrade to webhook version 2.",
    );
  }

  // Ensure only version 2 is accepted
  if (parsedPayload.version !== 2) {
    throw new Error(
      `Invalid webhook payload: unsupported version ${parsedPayload.version}. Only version 2 is supported.`
    );
  }
🤖 Prompt for AI Agents
In packages/thirdweb/src/bridge/Webhook.ts around lines 85 to 89, the code only
rejects version 1 payloads but does not explicitly check that the payload
version is exactly 2. Update the validation logic to explicitly verify that
parsedPayload.version equals 2, and throw an error for any other version values
to ensure only version 2 payloads are accepted.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
packages/thirdweb/src/bridge/Webhook.ts (2)

30-33: Consider case-insensitive header matching for more robust header handling.

The current implementation checks for specific header names with exact casing, but HTTP headers are case-insensitive according to the spec. Using lowercase for all header names would make this more robust against variations in header casing from different environments or proxies.

-  const receivedSignature =
-    headers["x-payload-signature"] || headers["x-pay-signature"];
-  const receivedTimestamp =
-    headers["x-timestamp"] || headers["x-pay-timestamp"];
+  // Normalize headers to lowercase for case-insensitive matching
+  const normalizedHeaders = Object.fromEntries(
+    Object.entries(headers).map(([key, value]) => [key.toLowerCase(), value])
+  );
+  const receivedSignature =
+    normalizedHeaders["x-payload-signature"] || normalizedHeaders["x-pay-signature"];
+  const receivedTimestamp =
+    normalizedHeaders["x-timestamp"] || normalizedHeaders["x-pay-timestamp"];

1-92: Add function to generate webhook signatures for testing and validation.

Consider adding a companion function that generates webhook signatures using the same algorithm. This would be valuable for users who need to test their webhook handlers or validate the signature generation process.

/**
 * Generates a signature for an outgoing webhook payload.
 * This is useful for testing webhook handlers.
 * 
 * @param payload - The payload to sign
 * @param secret - The webhook secret to sign with
 * @returns The signature and timestamp that can be used in headers
 */
export async function generateSignature(
  payload: string,
  secret: string,
): Promise<{ signature: string; timestamp: string }> {
  const timestamp = Math.floor(Date.now() / 1000).toString();
  const encoder = new TextEncoder();
  const key = await crypto.subtle.importKey(
    "raw",
    encoder.encode(secret),
    { name: "HMAC", hash: "SHA-256" },
    false,
    ["sign"],
  );

  const signature = await crypto.subtle.sign(
    "HMAC",
    key,
    encoder.encode(`${timestamp}.${payload}`),
  );

  // Convert the signature to hex string
  const hexSignature = Array.from(new Uint8Array(signature))
    .map((b) => b.toString(16).padStart(2, "0"))
    .join("");

  return {
    signature: hexSignature,
    timestamp,
  };
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4884440 and e7c744f.

📒 Files selected for processing (4)
  • .changeset/webhook-verification.md (1 hunks)
  • packages/thirdweb/src/bridge/Webhook.test.ts (1 hunks)
  • packages/thirdweb/src/bridge/Webhook.ts (1 hunks)
  • packages/thirdweb/src/bridge/index.ts (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • packages/thirdweb/src/bridge/Webhook.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • .changeset/webhook-verification.md
  • packages/thirdweb/src/bridge/index.ts
🧰 Additional context used
🧬 Code Graph Analysis (1)
packages/thirdweb/src/bridge/Webhook.ts (2)
packages/thirdweb/src/bridge/index.ts (2)
  • parse (9-9)
  • WebhookPayload (22-22)
apps/dashboard/next.config.ts (1)
  • headers (129-143)
⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: Analyze (javascript)
  • GitHub Check: E2E Tests (pnpm, esbuild)
  • GitHub Check: E2E Tests (pnpm, webpack)
  • GitHub Check: Unit Tests
  • GitHub Check: E2E Tests (pnpm, vite)
  • GitHub Check: Size
  • GitHub Check: Lint Packages
🔇 Additional comments (3)
packages/thirdweb/src/bridge/Webhook.ts (3)

39-48: Excellent timestamp validation implementation.

The timestamp validation logic is well-implemented with proper tolerance handling and clear error messaging. Using Math.abs() correctly handles both future and past timestamps, which is important for webhook validation across systems with clock skew.


94-119: Well-structured type definition with strong typing.

The WebhookPayload type is well-designed using a discriminated union based on version. The version 2 payload has proper typing for all fields, including using template literals for Ethereum addresses (0x${string}), which helps prevent address format errors.


50-74:

✅ Verification successful

Robust signature verification using Web Crypto API.

The signature verification implementation is secure and follows best practices by using the Web Crypto API with HMAC-SHA256. The hex conversion is correctly implemented, and the signature comparison uses strict equality.

Note that the Web Crypto API may not be available in all Node.js environments. Please verify your target environments support this API or consider adding a fallback implementation:


🏁 Script executed:

#!/bin/bash
# Check if this code is intended to run in Node.js versions that might not support Web Crypto API
fd -e json "package.json" --exec grep -l "\"engines\"" {} \; | xargs cat

Length of output: 27662


Signature verification via Web Crypto API is safe on supported environments

The HMAC-SHA256 signature check is implemented correctly and securely using the Web Crypto API. Since our packages/thirdweb package.json declares “engines”: { "node": ">=18" } and all browsers support Web Crypto, the global crypto.subtle API is guaranteed in all targeted runtimes—no fallback is required.

  • File: packages/thirdweb/src/bridge/Webhook.ts (lines 50–74)

Copy link
Contributor

github-actions bot commented May 20, 2025

size-limit report 📦

Path Size Loading time (3g) Running time (snapdragon) Total time
thirdweb (esm) 61.7 KB (+8.81% 🔺) 1.3 s (+8.81% 🔺) 293 ms (+123.84% 🔺) 1.6 s
thirdweb (cjs) 344.59 KB (+11.32% 🔺) 6.9 s (+11.32% 🔺) 1.3 s (+8.79% 🔺) 8.2 s
thirdweb (minimal + tree-shaking) 5.69 KB (0%) 114 ms (0%) 41 ms (+228.67% 🔺) 155 ms
thirdweb/chains (tree-shaking) 531 B (0%) 11 ms (0%) 35 ms (+636.34% 🔺) 46 ms
thirdweb/react (minimal + tree-shaking) 19.54 KB (+0.23% 🔺) 391 ms (+0.23% 🔺) 96 ms (+191.92% 🔺) 487 ms

Copy link

codecov bot commented May 20, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 55.67%. Comparing base (916ecf4) to head (9aea6f4).
Report is 6 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #7103      +/-   ##
==========================================
+ Coverage   55.62%   55.67%   +0.05%     
==========================================
  Files         902      904       +2     
  Lines       58190    58328     +138     
  Branches     4098     4120      +22     
==========================================
+ Hits        32369    32475     +106     
- Misses      25716    25748      +32     
  Partials      105      105              
Flag Coverage Δ
packages 55.67% <100.00%> (+0.05%) ⬆️
Files with missing lines Coverage Δ
packages/thirdweb/src/bridge/Webhook.ts 100.00% <100.00%> (ø)
packages/thirdweb/src/bridge/index.ts 100.00% <100.00%> (ø)

... and 9 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@jnsdls jnsdls force-pushed the Add_webhook_verification_functionality_for_secure_payload_validation branch from 3824d8d to c5e2b36 Compare May 23, 2025 19:03
@github-actions github-actions bot added Dashboard Involves changes to the Dashboard. Playground Changes involving the Playground codebase. Ecosystem Portal Involves changes to the Ecosystem Portal labels May 23, 2025
Copy link

socket-security bot commented May 23, 2025

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updatedzod@​3.25.7 ⏵ 3.25.2410010074 -196 +1100

View full report

@jnsdls jnsdls merged commit f3abea3 into main May 23, 2025
25 checks passed
@jnsdls jnsdls deleted the Add_webhook_verification_functionality_for_secure_payload_validation branch May 23, 2025 20:59
@joaquim-verges joaquim-verges mentioned this pull request May 23, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Dashboard Involves changes to the Dashboard. Ecosystem Portal Involves changes to the Ecosystem Portal packages Playground Changes involving the Playground codebase. SDK Involves changes to the thirdweb SDK
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants