Skip to content

Add framework examples and tighten Express raw-body handling#55

Open
Prateek32177 wants to merge 1 commit intomainfrom
codex/add-framework-examples-and-setup-guide
Open

Add framework examples and tighten Express raw-body handling#55
Prateek32177 wants to merge 1 commit intomainfrom
codex/add-framework-examples-and-setup-guide

Conversation

@Prateek32177
Copy link
Copy Markdown
Contributor

Motivation

  • Provide four self-contained framework examples (Hono, Next.js App Router, Cloudflare Workers, Express) that demonstrate webhook verification against the local @hookflo/tern build to make onboarding and testing with the Stripe CLI straightforward.
  • Fix the Express verification failure mode where global express.json() consumption of the body causes signature verification to fail silently.
  • Expose a CLI-first quick-start flow so users can scaffold handlers with npx @hookflo/tern-cli and run local tests without external storage.

Description

  • Add examples/ workspace and per-framework example packages with package.json, tsconfig.json, src/ handlers, and per-example README.md files for Hono, Next.js, Cloudflare Workers, and Express, and a top-level examples/README.md with quick starts and CLI guidance.
  • Point each example's dependency on @hookflo/tern to the local repo via "@hookflo/tern": "../../" so examples use the local build.
  • Update src/adapters/shared.ts to accept an optional rawBody on MinimalNodeRequest and add hasRawByteBody and hasUsableRawBody helpers and prefer rawBody when extracting bytes.
  • Update the Express adapter (createWebhookMiddleware) to only return a strict-mode error when the request body was parsed (hasParsedBody) and no usable raw bytes are present (!hasUsableRawBody), preserving existing strict safety but allowing frameworks/middleware that provide raw bytes to work.

Testing

  • npm run build at the repo root completed successfully.
  • npm install inside each example directory was attempted but blocked by registry policy in this environment (returned 403 Forbidden).
  • Running the local express test runner (node --loader tsx src/adapters/testExpress.ts) failed in this environment due to the tsx loader not being available.

Codex Task

@socket-security
Copy link
Copy Markdown

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

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addednext@​15.5.146299919770
Added@​types/​react@​19.2.141001007992100
Addedtsx@​4.21.01001008185100
Addedreact@​19.2.41001008497100
Addedexpress@​4.22.19710010087100
Addedwrangler@​4.78.0981009296100
Addedreact-dom@​19.2.41001009298100
Added@​hono/​node-server@​1.19.1110010010093100
Addedhono@​4.12.9991009796100
Added@​cloudflare/​workers-types@​4.20260329.1100100100100100

View full report

Comment on lines +21 to +24
createWebhookMiddleware({
platform: 'stripe',
secret: process.env.STRIPE_WEBHOOK_SECRET ?? '',
}),

Check failure

Code scanning / CodeQL

Missing rate limiting High

This route handler performs
authorization
, but is not rate-limited.

Copilot Autofix

AI 2 days ago

To fix the problem, introduce an Express rate‑limiting middleware and apply it to the webhook route (and optionally to other routes) so that expensive verification work in createWebhookMiddleware cannot be abused by unbounded request rates. The fix should not alter the existing behavior of the route for legitimate traffic, just bound the number of requests per time window.

The best way to do this in this context is to use the widely‑used express-rate-limit package. In examples/express/src/index.ts, we will:

  1. Import rateLimit from express-rate-limit.
  2. Define a stripeWebhookLimiter configured with a reasonable windowMs and max requests.
  3. Insert stripeWebhookLimiter as middleware in the app.post('/webhooks/stripe', ...) call, before createWebhookMiddleware(...), ensuring that abusive traffic is rejected before the verification work runs.
  4. Leave the rest of the app structure unchanged, respecting the existing comment about the critical middleware order (webhook route before express.json()).

Concretely:

  • Add a new import line after the existing express import.
  • Add a small configuration block for the limiter after const app = express();.
  • Modify the app.post('/webhooks/stripe', ...) definition to include the limiter as an additional argument between the route path and createWebhookMiddleware({ ... }).
Suggested changeset 2
examples/express/src/index.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/examples/express/src/index.ts b/examples/express/src/index.ts
--- a/examples/express/src/index.ts
+++ b/examples/express/src/index.ts
@@ -1,8 +1,14 @@
 import express from 'express';
 import { createWebhookMiddleware } from '@hookflo/tern/express';
+import rateLimit from 'express-rate-limit';
 
 const app = express();
 
+const stripeWebhookLimiter = rateLimit({
+  windowMs: 15 * 60 * 1000, // 15 minutes
+  max: 100, // limit each IP to 100 Stripe webhook requests per windowMs
+});
+
 // ─── CRITICAL ORDER ───────────────────────────────────────────────────────────
 // Register the webhook route BEFORE app.use(express.json()).
 // If express.json() runs first it consumes the raw body and signature
@@ -18,6 +21,7 @@
 // and attaches the verified payload to req.webhook before calling next().
 app.post(
   '/webhooks/stripe',
+  stripeWebhookLimiter,
   createWebhookMiddleware({
     platform: 'stripe',
     secret: process.env.STRIPE_WEBHOOK_SECRET ?? '',
EOF
@@ -1,8 +1,14 @@
import express from 'express';
import { createWebhookMiddleware } from '@hookflo/tern/express';
import rateLimit from 'express-rate-limit';

const app = express();

const stripeWebhookLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 Stripe webhook requests per windowMs
});

// ─── CRITICAL ORDER ───────────────────────────────────────────────────────────
// Register the webhook route BEFORE app.use(express.json()).
// If express.json() runs first it consumes the raw body and signature
@@ -18,6 +21,7 @@
// and attaches the verified payload to req.webhook before calling next().
app.post(
'/webhooks/stripe',
stripeWebhookLimiter,
createWebhookMiddleware({
platform: 'stripe',
secret: process.env.STRIPE_WEBHOOK_SECRET ?? '',
examples/express/package.json
Outside changed files

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/examples/express/package.json b/examples/express/package.json
--- a/examples/express/package.json
+++ b/examples/express/package.json
@@ -9,7 +9,8 @@
   },
   "dependencies": {
     "@hookflo/tern": "../../",
-    "express": "^4.21.2"
+    "express": "^4.21.2",
+    "express-rate-limit": "^8.3.1"
   },
   "devDependencies": {
     "@types/express": "^5.0.1",
EOF
@@ -9,7 +9,8 @@
},
"dependencies": {
"@hookflo/tern": "../../",
"express": "^4.21.2"
"express": "^4.21.2",
"express-rate-limit": "^8.3.1"
},
"devDependencies": {
"@types/express": "^5.0.1",
This fix introduces these dependencies
Package Version Security advisories
express-rate-limit (npm) 8.3.1 None
Copilot is powered by AI and may make mistakes. Always verify output.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 41a636f8e2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 89 to 90
if (body instanceof Uint8Array) {
return body;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Convert ArrayBuffer raw bodies before parsed-body fallback

hasUsableRawBody now treats ArrayBuffer as a valid raw payload, but extractRawBody does not have an ArrayBuffer branch, so requests with req.rawBody as ArrayBuffer fall through to the parsed-body warning path (or stream read) instead of using the raw bytes. In strict mode this can let the middleware proceed and then fail signature verification because the body is re-serialized/empty, which breaks the new raw-body compatibility this commit is trying to add.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant