---
name: stripe-webhook-guard
description: Use when about to deploy or push changes that touch a Stripe webhook handler — audits stripe.webhooks.constructEvent calls for rawBody/rotation safety before the deploy lands.
---

# stripe-webhook-guard

## Purpose

Catches the most common Stripe webhook bypass in Next.js / Express / Fastify
codebases: calling `stripe.webhooks.constructEvent` against a parsed JSON body
instead of the raw text body, which silently disables signature verification.

This is a *real* sample SKILL.md — same format and depth as what you receive
from a Stack Audit. The audit version would be code-grounded with file:line
references from your repo. This sample uses placeholder paths.

## When to fire

Trigger phrases the assistant watches for:
- "deploy webhook"
- "deploying stripe"
- "push the webhook fix"
- "ship payments"

## What it does

1. Greps the repo for `stripe.webhooks.constructEvent(` call sites.
2. For each call site, traces the body argument back to its source.
3. Flags any case where:
   - body comes from `req.body` after `express.json()` middleware (parsed JSON)
   - body comes from `await request.json()` in Next.js App Router routes
   - body comes from `JSON.parse(...)` upstream
4. Suggests the correction:
   - Express: `app.post('/webhook', express.raw({type:'application/json'}), handler)`
   - Next.js App Router: `const body = await request.text(); ...constructEvent(body, sig, secret)`
   - Fastify: `addContentTypeParser('application/json', { parseAs: 'buffer' }, ...)`
5. Cross-checks `STRIPE_WEBHOOK_SECRET` env var presence in `.env.example`
   and rotation date in any `secrets.md` or `runbooks/` doc.

## Example output (from a real audit deliverable)

```
[stripe-webhook-guard] checked 1 webhook handler:

  ⚠ app/api/stripe/webhook/route.ts:34
      stripe.webhooks.constructEvent(JSON.parse(body), sig, secret)
      ↑ BYPASS — JSON.parse alters the bytes; signature verification
        will fail on real Stripe events but pass on locally-crafted ones.

  Fix: replace lines 28-34 with:
      const body = await request.text();
      const event = stripe.webhooks.constructEvent(body, sig, secret);

  Env audit:
      ✓ STRIPE_WEBHOOK_SECRET in .env.example
      ✗ No rotation date documented. Add to runbooks/secrets.md.
```

## Files this skill writes

- Inline corrections in the route file (preview shown before commit)
- Optional: `runbooks/secrets.md` stub if missing

## What this skill does NOT do

- Doesn't rotate the actual webhook secret (operator action)
- Doesn't replay missed webhooks (separate runbook concern)
- Doesn't refactor the webhook handler logic — only fixes verification

## Install

Drop this file at `~/.claude/skills/stripe-webhook-guard/SKILL.md`. The
assistant will pick it up on next session. See `install.md` in this samples
directory for the 60-second drop-in instructions.

## License

You bought this. Use it freely in your projects. Don't resell as-is.

— AgentStack Stack Audit (sample)
