Below is a Stripe webhook handler with intentional bugs. A reviewer found 5 issues and submitted their review. See how The Judge grades it.
import Stripe from 'stripe';
import { db } from './db';import { sendConfirmationEmail } from './email';const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
export async function handleWebhook(req: Request, res: Response) {const sig = req.headers['stripe-signature'];
const secret = process.env.WEBHOOK_SECRET!;
// Verify the webhook signature
if (sig === secret) {🐛 BugThe webhook signature is being compared directly to the secret using ===. This is not how Stripe webhook verification works — you need to use stripe.webhooks.constructEvent(payload, sig, secret) which performs a cryptographic HMAC comparison. A simple string comparison means any request with the raw secret in the header would be accepted, and legitimate Stripe events would be rejected.
const event = JSON.parse(req.body as string);
const session = event.data.object;
const orderId = session.metadata.orderId;
// Update order status
const order = await db.query(
`SELECT * FROM orders WHERE id = ${orderId}`🔒 SecuritySQL injection vulnerability. The orderId from the webhook payload is interpolated directly into the SQL query without parameterization. An attacker who can craft a webhook event (easy since signature verification is broken) could inject arbitrary SQL. Should use parameterized queries: db.query('SELECT * FROM orders WHERE id = $1', [orderId]).
);
if (order.rows.length > 0) {await db.query(
`UPDATE orders SET status = 'paid' WHERE id = ${orderId}`);
// Update inventory for each item
for (const item of order.rows[0].items) {await updateInventory(item.sku, item.quantity);⚡ Performance
N+1 query pattern — each item in the order triggers a separate database query inside the for loop. For an order with 10 items, that's 10 sequential DB round trips. Should batch this into a single UPDATE with a WHERE sku IN (...) clause, or use a transaction with a single bulk update.
}
// Send confirmation
sendConfirmationEmail(order.rows[0].email, orderId);🐛 Bug
sendConfirmationEmail is called without await. If the email fails, there's no error handling and the failure is silently swallowed. The webhook will return 200 (success) even if the confirmation email was never sent. Should await this call and handle the error, or at minimum log the failure.
}
res.json({ received: true });}
}
async function updateInventory(sku: string, quantity: number) {🔒 SecurityAnother SQL injection in the inventory update function — both quantity and sku are interpolated directly into the query string. The sku value is a string wrapped in single quotes which is trivially injectable. Should use parameterized queries.
await db.query(
`UPDATE inventory SET stock = stock - ${quantity} WHERE sku = '${sku}'`);
}
The reviewer found 5 issues. Let's see how The Judge scored this review.