fix: atomic usage limit check prevents race condition bypass#1464
fix: atomic usage limit check prevents race condition bypass#1464Xenon010101 wants to merge 1 commit into
Conversation
Moves the usageLog count check AND create into a single Prisma transaction with SELECT FOR UPDATE on the User row, serializing concurrent requests from the same user. Removes now-redundant usageLog.create calls from all controllers/services that already run behind the usageLimit middleware. Closes Sachinchaurasiya360#1426 Signed-off-by: Xenon010101 <xenon010101@users.noreply.github.com>
|
Hi @Xenon010101, thanks for contributing to InternHack! 🎉 I have automatically:
Our workflows will now analyze your changes to classify:
Tip Ensure your PR description references the issue it resolves (e.g. Happy coding! 🚀 |
📝 WalkthroughWalkthroughThis PR fixes a concurrency race condition in the daily usage limit enforcement by moving usage log creation from scattered downstream calls into a single atomic middleware transaction. The middleware now locks the user row, counts today's usage, and pre-creates a usage log entry within a transaction before the request proceeds, eliminating the window where concurrent requests could bypass the daily cap. ChangesUsage Limit Race Condition Fix
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
server/src/module/student/student.external-application.service.test.ts (1)
12-14: 💤 Low valueConsider removing unused
usageLogmocks.The
usageLogmocks in the hoisted setup andtxobject are no longer asserted in any test case since usage logging was moved to the middleware. These can be safely removed to keep the test setup minimal.🧹 Proposed cleanup
const mocks = vi.hoisted(() => ({ prisma: { $transaction: vi.fn(), adminJob: { findUnique: vi.fn(), }, externalJobApplication: { create: vi.fn(), }, - usageLog: { - create: vi.fn(), - }, }, tx: { externalJobApplication: { create: vi.fn(), }, - usageLog: { - create: vi.fn(), - }, }, badgeService: { checkAndAwardBadges: vi.fn().mockResolvedValue(undefined), }, }));Also applies to: 20-22
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@server/src/module/student/student.external-application.service.test.ts` around lines 12 - 14, Remove the now-unused usageLog mocks from the hoisted test setup and from the tx/mock transaction object: delete the usageLog: { create: vi.fn() } entries and any references to usageLog inside the tx mock used in student.external-application.service.test.ts, keeping the rest of the tx structure intact so tests still hit the same mocked DB methods; search for "usageLog" and remove those mock definitions in the file (including the hoisted setup and the tx object) to keep the test fixtures minimal.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@server/src/module/student/student.external-application.service.test.ts`:
- Around line 12-14: Remove the now-unused usageLog mocks from the hoisted test
setup and from the tx/mock transaction object: delete the usageLog: { create:
vi.fn() } entries and any references to usageLog inside the tx mock used in
student.external-application.service.test.ts, keeping the rest of the tx
structure intact so tests still hit the same mocked DB methods; search for
"usageLog" and remove those mock definitions in the file (including the hoisted
setup and the tx object) to keep the test fixtures minimal.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 732468b9-fd9b-496c-893b-3f156ecdc5aa
📒 Files selected for processing (10)
server/src/middleware/usage-limit.middleware.tsserver/src/module/ats/ats.controller.tsserver/src/module/ats/cover-letter.controller.tsserver/src/module/ats/resume-gen.controller.tsserver/src/module/dsa/dsa.service.tsserver/src/module/job-agent/job-agent.controller.tsserver/src/module/student/student.controller.tsserver/src/module/student/student.external-application.service.test.tsserver/src/module/student/student.service.tsserver/src/module/upload/upload.controller.ts
💤 Files with no reviewable changes (4)
- server/src/module/upload/upload.controller.ts
- server/src/module/job-agent/job-agent.controller.ts
- server/src/module/student/student.service.ts
- server/src/module/dsa/dsa.service.ts
What
Fix race condition in
usageLimitmiddleware that allows concurrent requests to bypass the daily usage cap.Root cause
The middleware counted existing usage logs and then called
next(), while the actual log entry was created later by the controller. This gap meant concurrent requests all read the same count before any log was written, so all of them passed the check.Changes
usage-limit.middleware.ts: Wrapped count + create in a single$transactionwithSELECT ... FOR UPDATEon the user row, serializing concurrent requestsusageLog.create()calls from 8 controllers/services that run behindusageLimit(ats, cover-letter, resume-gen, student, job-agent, dsa, upload)req.usageInfo.used + 1toreq.usageInfo.usedsince the count now includes the current requestVerification
usageLog.createcalls removed from controllers that use the middlewareCI failures
N/A
Before
Concurrent requests all read count=5 before any wrote; all 10 requests passed a limit of 5.
After
Each request atomically increments the count; request #6 correctly gets 429.
Closes #1426
Summary by CodeRabbit
Release Notes
Bug Fixes
Refactor