-
Notifications
You must be signed in to change notification settings - Fork 23
fix: complete bug hunt patch set (authentication, APIs, UI guards) #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
e61d2a6
c424525
66c3847
2499be1
4359359
147e951
8203822
81a0c65
79c2997
2c1ac34
0e7a273
1803a9f
bac0c62
35d9d1b
bd4f8ec
2916629
76d57ed
6fdc891
9fe5c6b
921a9ac
4c2d878
952b002
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,7 +4,18 @@ import mongoose from 'mongoose' | |
| import { connectDB } from '@/lib/mongodb' | ||
| import { Grade } from '@/models/Grade' | ||
|
|
||
| const ALLOWED_UPDATE_FIELDS = ['marks', 'maxMarks', 'grade'] | ||
| const ALLOWED_UPDATE_FIELDS = ['marks', 'maxMarks'] | ||
|
|
||
| function calcGrade(marks: number, max: number): string { | ||
| const pct = (marks / max) * 100 | ||
| if (pct > 90) return 'A+' | ||
| if (pct >= 80) return 'A' | ||
| if (pct >= 70) return 'B+' | ||
| if (pct >= 60) return 'B' | ||
| if (pct >= 50) return 'C' | ||
| if (pct >= 40) return 'D' | ||
| return 'F' | ||
| } | ||
|
|
||
| export async function PUT(req: NextRequest, ctx: { params: Promise<{ id: string }> }) { | ||
| const { userId } = await auth() | ||
|
|
@@ -34,10 +45,32 @@ export async function PUT(req: NextRequest, ctx: { params: Promise<{ id: string | |
| } | ||
|
|
||
| await connectDB() | ||
| const existing = await Grade.findOne({ _id: id, teacherId: userId }).lean() | ||
| if (!existing) { | ||
| return NextResponse.json({ error: 'Not found' }, { status: 404 }) | ||
| } | ||
|
|
||
| const nextMarks = | ||
| typeof sanitizedBody.marks === 'number' ? sanitizedBody.marks : existing.marks | ||
| const nextMax = | ||
| typeof sanitizedBody.maxMarks === 'number' | ||
| ? sanitizedBody.maxMarks | ||
| : existing.maxMarks | ||
|
|
||
| if (nextMarks > nextMax) { | ||
| return NextResponse.json( | ||
| { error: 'marks must be less than or equal to maxMarks' }, | ||
| { status: 400 }, | ||
| ) | ||
| } | ||
|
|
||
| const updatePayload: Record<string, unknown> = { ...sanitizedBody } | ||
| updatePayload.grade = calcGrade(nextMarks, nextMax) | ||
|
|
||
| const grade = await Grade.findOneAndUpdate( | ||
| { _id: id }, | ||
| sanitizedBody, | ||
| { new: true } | ||
| { _id: id, teacherId: userId }, | ||
| { $set: updatePayload }, | ||
| { new: true }, | ||
|
Comment on lines
+53
to
+73
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Description: Inspect grade update paths for explicit Mongoose update validation.
rg -n -C3 'Grade\.findOneAndUpdate|runValidators|marks must be less than or equal to maxMarks' --type=tsRepository: JavaScript-Mastery-Pro/discord-challenge Length of output: 3404 🏁 Script executed: cat -n app/api/grades/[id]/route.ts | head -100Repository: JavaScript-Mastery-Pro/discord-challenge Length of output: 3928 🏁 Script executed: # Check the Zod schema definition for grades
rg -n -B5 -A10 'marks|maxMarks' app/api/grades/\[id\]/route.ts | head -60Repository: JavaScript-Mastery-Pro/discord-challenge Length of output: 1548 🏁 Script executed: # Check the complete Mongoose Grade schema and validators
cat -n models/Grade.tsRepository: JavaScript-Mastery-Pro/discord-challenge Length of output: 2735 🏁 Script executed: # Check if there are other PUT/PATCH handlers for grades that might have similar issues
rg -n 'Grade\.findOneAndUpdate|runValidators' app/api/grades/ --type=tsRepository: JavaScript-Mastery-Pro/discord-challenge Length of output: 238 Add explicit validation for Currently, non-number values pass through Proposed fix+ if (
+ 'marks' in sanitizedBody &&
+ (typeof sanitizedBody.marks !== 'number' || sanitizedBody.marks < 0)
+ ) {
+ return NextResponse.json(
+ { error: 'marks must be a non-negative number' },
+ { status: 400 },
+ )
+ }
+
+ if (
+ 'maxMarks' in sanitizedBody &&
+ (typeof sanitizedBody.maxMarks !== 'number' || sanitizedBody.maxMarks < 1)
+ ) {
+ return NextResponse.json(
+ { error: 'maxMarks must be a positive number' },
+ { status: 400 },
+ )
+ }
+
const nextMarks =
typeof sanitizedBody.marks === 'number' ? sanitizedBody.marks : existing.marks
const nextMax =
typeof sanitizedBody.maxMarks === 'number'
? sanitizedBody.maxMarks
: existing.maxMarks🤖 Prompt for AI Agents |
||
| ) | ||
| if (!grade) return NextResponse.json({ error: 'Not found' }, { status: 404 }) | ||
| return NextResponse.json(grade) | ||
|
|
@@ -55,8 +88,13 @@ export async function DELETE(_req: NextRequest, ctx: { params: Promise<{ id: str | |
|
|
||
| try { | ||
| const { id } = await ctx.params | ||
|
|
||
| if (!mongoose.Types.ObjectId.isValid(id)) { | ||
| return NextResponse.json({ error: 'Invalid id' }, { status: 400 }) | ||
| } | ||
|
|
||
| await connectDB() | ||
| const deleted = await Grade.findOneAndDelete({ _id: id }) | ||
| const deleted = await Grade.findOneAndDelete({ _id: id, teacherId: userId }) | ||
|
|
||
| if (!deleted) { | ||
| return NextResponse.json({ error: 'Grade not found' }, { status: 404 }) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Harden against non-object JSON bodies.
req.json()happily resolves fornull, arrays, strings, etc. On those inputs,key in body(line 33) throwsTypeErrorfor primitives and silently iterates array indices, andbody.body(line 38) works unexpectedly. It's caught by the outer try and surfaces as a 500 instead of a clean 400.🛠️ Suggested fix
let body try { body = await req.json() } catch { return NextResponse.json({ error: 'Invalid JSON request body' }, { status: 400 }) } + if (body === null || typeof body !== 'object' || Array.isArray(body)) { + return NextResponse.json({ error: 'Request body must be a JSON object' }, { status: 400 }) + }🤖 Prompt for AI Agents