fix: resolve security, logic, and data integrity issues#10
fix: resolve security, logic, and data integrity issues#10Astr-oIT wants to merge 6 commits intoJavaScript-Mastery-Pro:mainfrom
Conversation
📝 WalkthroughWalkthroughMultiple API route handlers and database connection utility were refactored to improve error handling consistency, add validation for past deadlines and future dates, couple authorization checks to database queries, simplify request parsing with Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 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.
Actionable comments posted: 5
🧹 Nitpick comments (1)
lib/mongodb.ts (1)
5-6: Replace the untyped global cache with a properly typed version.Lines 5-6 use
(global as any).mongoose, which removes type guarantees forconnandpromise. This allows future edits to accidentally store the wrong cache shape. Define aMongooseCachetype and narrowglobalThisinstead to preserve both type safety and current runtime behavior.♻️ Proposed typed cache refactor
+type MongooseCache = { + conn: typeof mongoose | null + promise: Promise<typeof mongoose> | null +} + +const globalForMongoose = globalThis as typeof globalThis & { + mongoose?: MongooseCache +} + -let cached = (global as any).mongoose -if (!cached) cached = (global as any).mongoose = { conn: null, promise: null } +const cached = + globalForMongoose.mongoose ?? + (globalForMongoose.mongoose = { conn: null, promise: null })🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/mongodb.ts` around lines 5 - 6, Replace the untyped global cache usage by declaring a MongooseCache type (with conn and promise fields) and narrow globalThis to that shape before using it: create a MongooseCache interface/type, assert or augment globalThis as { mongoose?: MongooseCache }, then read/write the cache via globalThis.mongoose instead of (global as any).mongoose so cached, conn, and promise are properly typed; update the existing variables (cached, conn, promise) to use the new MongooseCache type and preserve the current runtime initialization logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/api/assignments/`[id]/route.ts:
- Around line 14-20: The update handler currently assumes req.json() returns an
object and uses the `in` operator on `body`, which throws for non-object values;
fix by ensuring `body` is an object (e.g., check typeof body === 'object' &&
body !== null) before iterating `ALLOWED_FIELDS` to build `sanitized`; also
enforce the same `deadline` validation used in the POST flow (reject `deadline`
if it's a past date) when `sanitized.deadline` is present; finally, call
`Assignment.findOneAndUpdate` with the option `runValidators: true` (alongside
`{ new: true }`) so Mongoose schema validators are applied during updates.
In `@app/api/assignments/route.ts`:
- Around line 69-71: The deadline validation rejects same-day ISO date strings
and lets invalid dates through; update the schema to use z.coerce.date() for
parsed.data.deadline so invalid/malformed dates fail validation, and change the
comparison in the route (the block checking parsed.data.deadline) to compare
date-only ISO strings (e.g., deadline.toISOString().slice(0,10) >= new
Date().toISOString().slice(0,10)) instead of comparing Date objects directly so
a "YYYY-MM-DD" deadline for today is accepted; ensure you return the existing
400 error for invalid/parsed failures.
In `@app/api/attendance/route.ts`:
- Line 15: The POST-only route removed the exported GET handler that four
consumers (StudentsClient.tsx, AttendanceClient.tsx twice for history and
heatmap, and OverviewClient.tsx for dashboard stats) still call; restore an
exported async function GET(req: NextRequest) that performs the attendance read
logic (parsing query params like studentId/date/range as the clients expect),
invokes the same attendance retrieval service/DB used elsewhere, and returns a
proper NextResponse/JSON payload with status codes and CORS/headers consistent
with POST so those components continue to work. Ensure the exported symbol name
is GET and that its response shape matches what StudentsClient.tsx:165,
AttendanceClient.tsx:103/131, and OverviewClient.tsx:198 expect.
- Around line 28-34: The current date validation accepts any non-empty string
and then uses lexicographic comparison in isFuture (used with parsed and
isBulk), which misclassifies datetimes or malformed strings; update the Zod
schema's date field (replace the existing date: z.string().min(1)) to use an ISO
date validator (e.g., z.iso.date() per the review) so only YYYY-MM-DD strings
pass validation, then keep the existing isFuture check (or, optionally, replace
it by parsing validated strings to Date for clarity) so parsed.data dates are
guaranteed to be valid ISO dates before the comparison in isFuture.
In `@app/api/students/`[id]/route.ts:
- Around line 14-20: The request body may be a JSON primitive which makes the
`in` operator throw and also `findOneAndUpdate` is missing validators; after
parsing `req.json()` ensure the body is an object (typeof body === 'object' &&
body !== null && !Array.isArray(body') ) and return a 400 with "No valid fields"
if it's not an object or has no allowed keys; build `sanitizedBody` from
`ALLOWED_UPDATE_FIELDS` only when body is a valid object. Then call
`connectDB()` and update the student using `Student.findOneAndUpdate({ _id: id,
teacherId: userId }, { $set: sanitizedBody }, { new: true, runValidators: true
})` (include runValidators:true so model validation runs).
---
Nitpick comments:
In `@lib/mongodb.ts`:
- Around line 5-6: Replace the untyped global cache usage by declaring a
MongooseCache type (with conn and promise fields) and narrow globalThis to that
shape before using it: create a MongooseCache interface/type, assert or augment
globalThis as { mongoose?: MongooseCache }, then read/write the cache via
globalThis.mongoose instead of (global as any).mongoose so cached, conn, and
promise are properly typed; update the existing variables (cached, conn,
promise) to use the new MongooseCache type and preserve the current runtime
initialization logic.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: e6ae80d3-1b4b-4fcd-a630-5dcd4d02c228
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (6)
app/api/assignments/[id]/route.tsapp/api/assignments/route.tsapp/api/attendance/route.tsapp/api/grades/route.tsapp/api/students/[id]/route.tslib/mongodb.ts
| const body = await req.json().catch(() => ({})) | ||
| const sanitized: any = {} | ||
| ALLOWED_FIELDS.forEach(k => { if (k in body) sanitized[k] = body[k] }) | ||
| if (Object.keys(sanitized).length === 0) return NextResponse.json({ error: 'No valid fields' }, { status: 400 }) | ||
|
|
||
| await connectDB() | ||
|
|
||
| let body | ||
| try { | ||
| body = await req.json() | ||
| } catch { | ||
| return NextResponse.json({ error: 'Invalid JSON in request body' }, { status: 400 }) | ||
| } | ||
|
|
||
| // Sanitize: only allow whitelisted fields | ||
| const sanitizedBody: Record<string, unknown> = {} | ||
| for (const key of ALLOWED_UPDATE_FIELDS) { | ||
| if (key in body) { | ||
| sanitizedBody[key] = body[key] | ||
| } | ||
| } | ||
|
|
||
| const assignment = await Assignment.findOneAndUpdate( | ||
| { _id: id }, | ||
| sanitizedBody, | ||
| { new: true } | ||
| ) | ||
| if (!assignment) return NextResponse.json({ error: 'Not found' }, { status: 404 }) | ||
| return NextResponse.json(assignment) | ||
| } catch (error) { | ||
| if (error instanceof Error) { | ||
| console.error('PUT /api/assignments/[id] error:', error.message) | ||
| } | ||
| return NextResponse.json({ error: 'Internal server error' }, { status: 500 }) | ||
| } | ||
| const updated = await Assignment.findOneAndUpdate({ _id: id, teacherId: userId }, { $set: sanitized }, { new: true }) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Confirms primitive JSON bodies can throw during `in` checks, and checks whether validators are enabled on assignment updates.
node - <<'NODE'
for (const value of [null, 1, true, 'text']) {
try {
console.log(typeof value, '=>', 'deadline' in value)
} catch (error) {
console.log(typeof value, '=> throws:', error.message)
}
}
NODE
rg -nUP 'Assignment\.findOneAndUpdate\((?s:.*?)runValidators' 'app/api/assignments/[id]/route.ts' || trueRepository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 385
🏁 Script executed:
rg -n "ALLOWED_FIELDS" app/api/assignments/[id]/route.tsRepository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 292
🏁 Script executed:
cat -n app/api/assignments/[id]/route.ts | head -50Repository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 2419
🏁 Script executed:
cat -n app/api/assignments/route.ts | head -80Repository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 3563
Validate assignment update payloads and enforce deadline consistency.
The in operator on line 16 throws when req.json() successfully parses to a non-object value (e.g., null, a string, or a number). Additionally, deadline is allowed in updates without the past-date validation applied in POST /api/assignments (lines 69–72 of route.ts), and findOneAndUpdate omits runValidators: true, bypassing Mongoose schema validation.
Proposed fix
- const body = await req.json().catch(() => ({}))
- const sanitized: any = {}
+ const body = await req.json().catch(() => null)
+ if (!body || typeof body !== 'object' || Array.isArray(body)) {
+ return NextResponse.json({ error: 'Invalid JSON body' }, { status: 400 })
+ }
+
+ const sanitized: Record<string, unknown> = {}
ALLOWED_FIELDS.forEach(k => { if (k in body) sanitized[k] = body[k] })
if (Object.keys(sanitized).length === 0) return NextResponse.json({ error: 'No valid fields' }, { status: 400 })
+
+ if (typeof sanitized.deadline === 'string') {
+ if (new Date(sanitized.deadline) < new Date()) {
+ return NextResponse.json({ error: 'Deadline cannot be in the past' }, { status: 400 })
+ }
+ }
await connectDB()
- const updated = await Assignment.findOneAndUpdate({ _id: id, teacherId: userId }, { $set: sanitized }, { new: true })
+ const updated = await Assignment.findOneAndUpdate(
+ { _id: id, teacherId: userId },
+ { $set: sanitized },
+ { new: true, runValidators: true }
+ )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/api/assignments/`[id]/route.ts around lines 14 - 20, The update handler
currently assumes req.json() returns an object and uses the `in` operator on
`body`, which throws for non-object values; fix by ensuring `body` is an object
(e.g., check typeof body === 'object' && body !== null) before iterating
`ALLOWED_FIELDS` to build `sanitized`; also enforce the same `deadline`
validation used in the POST flow (reject `deadline` if it's a past date) when
`sanitized.deadline` is present; finally, call `Assignment.findOneAndUpdate`
with the option `runValidators: true` (alongside `{ new: true }`) so Mongoose
schema validators are applied during updates.
| // FIX: Deadline validation (Prevent past dates) | ||
| if (new Date(parsed.data.deadline) < new Date()) { | ||
| return NextResponse.json({ error: 'Deadline cannot be in the past' }, { status: 400 }); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Demonstrates why timestamp comparison rejects same-day date-only strings
# and why invalid strings need schema-level validation.
node - <<'NODE'
const today = new Date().toISOString().slice(0, 10)
console.log({ today, parsedAsDate: new Date(today).toISOString(), rejectedByTimestampCompare: new Date(today) < new Date() })
const invalid = new Date('not-a-date')
console.log({ invalidDateIsNaN: Number.isNaN(invalid.getTime()), invalidComparisonResult: invalid < new Date() })
NODERepository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 248
🏁 Script executed:
# Search for AssignmentSchema definition and the deadline validation code
cd /repo && find . -type f -name "*.ts" -o -name "*.js" | head -20Repository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 139
🏁 Script executed:
# Look for AssignmentSchema and deadline validation
rg "AssignmentSchema" --type tsRepository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 419
🏁 Script executed:
# Check the specific file and lines mentioned
cat -n app/api/assignments/route.ts | head -100Repository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 3563
🏁 Script executed:
# Check if z.date() or similar Zod date validation exists
rg "z\.(date|iso)" --type ts -A 2Repository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 66
Fix deadline validation to handle date-only strings and invalid dates correctly.
The current validation has two problems:
- Same-day deadlines are rejected:
new Date('YYYY-MM-DD')parses at midnight UTC, so deadlines with today's date are treated as past times after midnight. - Invalid dates bypass validation:
Invalid Date < new Date()returnsfalse, allowing malformed dates to reach the database and cause casting errors.
Use z.coerce.date() in the schema and compare dates as ISO strings:
Proposed fix
const AssignmentSchema = z.object({
title: z.string().min(1),
description: z.string().optional(),
subject: z.string().min(1),
class: z.string().min(1),
- deadline: z.string().min(1),
+ deadline: z.coerce.date(),
maxMarks: z.number().min(1).default(100),
status: z.enum(['active', 'closed']).default('active'),
kanbanStatus: z.enum(['todo', 'in_progress', 'submitted']).default('todo'),
})- // FIX: Deadline validation (Prevent past dates)
- if (new Date(parsed.data.deadline) < new Date()) {
+ // Deadline validation (compare date-only values)
+ const today = new Date().toISOString().split('T')[0]
+ const deadlineDate = parsed.data.deadline.toISOString().split('T')[0]
+ if (deadlineDate < today) {
return NextResponse.json({ error: 'Deadline cannot be in the past' }, { status: 400 });
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/api/assignments/route.ts` around lines 69 - 71, The deadline validation
rejects same-day ISO date strings and lets invalid dates through; update the
schema to use z.coerce.date() for parsed.data.deadline so invalid/malformed
dates fail validation, and change the comparison in the route (the block
checking parsed.data.deadline) to compare date-only ISO strings (e.g.,
deadline.toISOString().slice(0,10) >= new Date().toISOString().slice(0,10))
instead of comparing Date objects directly so a "YYYY-MM-DD" deadline for today
is accepted; ensure you return the existing 400 error for invalid/parsed
failures.
| } | ||
| } | ||
|
|
||
| export async function POST(req: NextRequest) { |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Confirms remaining GET consumers and whether the route still exports GET.
rg -n -C2 'fetch\((`|")/api/attendance'
rg -nP 'export\s+async\s+function\s+GET\b' 'app/api/attendance/route.ts' || trueRepository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 2428
Restore GET /api/attendance before merging.
The route handler no longer exports GET, but four consumers still depend on it: StudentsClient.tsx:165 (student attendance), AttendanceClient.tsx:103 (history), AttendanceClient.tsx:131 (heatmap), and OverviewClient.tsx:198 (dashboard stats). Removing it without providing an alternative will break all attendance read operations.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/api/attendance/route.ts` at line 15, The POST-only route removed the
exported GET handler that four consumers (StudentsClient.tsx,
AttendanceClient.tsx twice for history and heatmap, and OverviewClient.tsx for
dashboard stats) still call; restore an exported async function GET(req:
NextRequest) that performs the attendance read logic (parsing query params like
studentId/date/range as the clients expect), invokes the same attendance
retrieval service/DB used elsewhere, and returns a proper NextResponse/JSON
payload with status codes and CORS/headers consistent with POST so those
components continue to work. Ensure the exported symbol name is GET and that its
response shape matches what StudentsClient.tsx:165,
AttendanceClient.tsx:103/131, and OverviewClient.tsx:198 expect.
| const today = new Date().toISOString().split('T')[0] | ||
| const data = parsed.data as any | ||
|
|
||
| // BUG FIX: Prevent future dating | ||
| const isFuture = (d: string) => d > today | ||
| if (isBulk ? data.some((r: any) => isFuture(r.date)) : isFuture(data.date)) { | ||
| return NextResponse.json({ error: "Cannot mark attendance for future dates" }, { status: 400 }) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Demonstrates that raw string comparison rejects same-day datetime strings.
node - <<'NODE'
const today = '2026-04-18'
for (const value of ['2026-04-18', '2026-04-18T12:00:00Z', 'not-a-date']) {
console.log({ value, isFutureByStringCompare: value > today })
}
NODERepository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 259
🏁 Script executed:
cat -n app/api/attendance/route.ts | head -60Repository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 2633
🏁 Script executed:
# Check Zod version and available date validation methods
fd 'package.json' | head -5 | xargs cat | grep -i zodRepository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 103
🏁 Script executed:
# Verify if z.string().date() is available in Zod 4.3.6
# Check Zod documentation or examples in the codebase
rg -i "z\.string.*\.date\|z\.date" | head -20Repository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 66
🏁 Script executed:
# Check Zod 4.3.6 API - look for how to validate date strings
# Try searching for common Zod patterns or check if there are examples elsewhere
rg "z\." app/ | grep -i "date\|parse\|refine" | head -20Repository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 271
🏁 Script executed:
# Let's also verify the exact Zod version and its capabilities
# by checking if z.string().date() or similar exists
node <<'EOF'
try {
const z = require('zod');
// Check available methods on z
console.log('z.string() methods:', Object.getOwnPropertyNames(z.ZodString.prototype).filter(m => m.includes('date')));
// Try to use z.string().date()
const schema = z.string().date();
console.log('z.string().date() exists:', !!schema);
} catch(e) {
console.log('Error:', e.message);
}
EOFRepository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 159
🏁 Script executed:
# Search for any date validation patterns already used in the codebase
rg "regex|refine|transform" app/ | grep -i date | head -10Repository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 66
🏁 Script executed:
# Check if there are any tests or other examples of date validation
fd -e ts -e tsx -e js | xargs grep -l "date" | head -10Repository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 376
🏁 Script executed:
# Let's check Zod's actual API in version 4.3.6 by looking at its usage patterns
# Search for any validation patterns that might give us clues about date handling
rg "z\.string" app/ -A 2 | head -30Repository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 1728
🏁 Script executed:
# Check if there's a package-lock.json that might give us exact Zod version info
cat package-lock.json | grep -A 10 '"zod"' | head -20Repository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 664
🌐 Web query:
Zod 4.3.6 date validation string.date() API
💡 Result:
In Zod 4.3.6, there is no string.date method on z.string. Date validation for strings uses top-level factory functions under the z.iso namespace, such as z.iso.date for ISO date strings in YYYY-MM-DD format. These are regex-based validations for common date formats: - z.iso.date: Validates YYYY-MM-DD format. Example: const date = z.iso.date; date.parse("2020-01-01"); // ✅ date.parse("2020-1-1"); // ❌ date.parse("2020-01-32"); // ❌ - z.iso.datetime: Validates ISO 8601 datetime strings (with options for precision, offset). Example: const datetime = z.iso.datetime; datetime.parse("2020-01-01T06:15:00Z"); // ✅ - z.iso.time: Validates HH:MM[:SS[.s+]] format. To coerce strings to Date objects, use z.coerce.date or z.string.pipe(z.coerce.date). For bidirectional string-to-Date conversion, use z.codec(z.iso.datetime, z.date, { decode: (iso) => new Date(iso), encode: (date) => date.toISOString }). Zod 4.3.6 was released January 4, 2026, and these APIs are unchanged from earlier v4 releases. No z.string.date API exists; date validations are via z.iso.* factory functions, not chainable string methods.
Citations:
- 1: https://v4.zod.dev/api?id=dates
- 2: https://github.com/colinhacks/zod/releases
- 3: https://v4.zod.dev/?id=dates
- 4: https://v4.zod.dev/api?id=strings
- 5: https://v4.zod.dev/?id=strings
- 6: z.iso.date() not recognized in Zod v4.3.6 (TypeScript) colinhacks/zod#5783
Use Zod's ISO date validator to prevent string-comparison vulnerabilities.
The schema accepts any non-empty string for date (line 11), but the future-date check uses lexicographic string comparison (line 32). This causes false positives: '2026-04-18T12:00:00Z' > '2026-04-18' is true (same-day datetime flagged as future), and 'not-a-date' > '2026-04-18' is true (malformed string flagged as future). Attendance records may be incorrectly rejected or accepted.
Replace date: z.string().min(1) with date: z.iso.date() to enforce YYYY-MM-DD format at validation time, ensuring the string comparison logic receives only valid dates.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/api/attendance/route.ts` around lines 28 - 34, The current date
validation accepts any non-empty string and then uses lexicographic comparison
in isFuture (used with parsed and isBulk), which misclassifies datetimes or
malformed strings; update the Zod schema's date field (replace the existing
date: z.string().min(1)) to use an ISO date validator (e.g., z.iso.date() per
the review) so only YYYY-MM-DD strings pass validation, then keep the existing
isFuture check (or, optionally, replace it by parsing validated strings to Date
for clarity) so parsed.data dates are guaranteed to be valid ISO dates before
the comparison in isFuture.
| const body = await req.json().catch(() => ({})) | ||
| const sanitizedBody: any = {} | ||
| ALLOWED_UPDATE_FIELDS.forEach(k => { if (k in body) sanitizedBody[k] = body[k] }) | ||
| if (Object.keys(sanitizedBody).length === 0) return NextResponse.json({ error: 'No valid fields' }, { status: 400 }) | ||
| await connectDB() | ||
| const student = await Student.findOneAndUpdate( | ||
| { _id: id }, | ||
| sanitizedBody, | ||
| { new: true } | ||
| ) | ||
| if (!student) return NextResponse.json({ error: 'Not found' }, { status: 404 }) | ||
| const student = await Student.findOneAndUpdate({ _id: id, teacherId: userId }, { $set: sanitizedBody }, { new: true }) | ||
| if (!student) return NextResponse.json({ error: 'Not found or unauthorized' }, { status: 404 }) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Confirms valid JSON primitives can throw with `in`, and checks whether the route enables Mongoose update validators.
node - <<'NODE'
for (const value of [null, 1, true, 'text']) {
try {
// Mirrors: if (k in body)
console.log(typeof value, '=>', 'name' in value)
} catch (error) {
console.log(typeof value, '=> throws:', error.message)
}
}
NODE
rg -nUP 'Student\.findOneAndUpdate\((?s:.*?)runValidators' 'app/api/students/[id]/route.ts' || trueRepository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 369
🏁 Script executed:
# Check if the file exists and read lines 14-20
if [ -f "app/api/students/[id]/route.ts" ]; then
echo "=== File exists, reading lines 1-25 for context ==="
head -25 "app/api/students/[id]/route.ts" | cat -n
else
echo "File not found at standard path, searching..."
fd "route.ts" --path "*students*" -x head -25 {} \; -x cat -n {} \;
fiRepository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 1780
🏁 Script executed:
# Also check the entire file for Student.findOneAndUpdate usage
rg -n "Student\.findOneAndUpdate" app/api/students/Repository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 240
Reject non-object bodies and run validators on student updates.
The in operator throws when body is a valid JSON primitive (null, number, boolean, or string), causing a 500 error instead of 400 for malformed payloads. Additionally, findOneAndUpdate persists updates without runValidators: true, allowing invalid values in allowed fields to bypass model validation.
🛡️ Proposed fix
- const body = await req.json().catch(() => ({}))
- const sanitizedBody: any = {}
+ const body = await req.json().catch(() => null)
+ if (!body || typeof body !== 'object' || Array.isArray(body)) {
+ return NextResponse.json({ error: 'Invalid JSON body' }, { status: 400 })
+ }
+
+ const sanitizedBody: Record<string, unknown> = {}
ALLOWED_UPDATE_FIELDS.forEach(k => { if (k in body) sanitizedBody[k] = body[k] })
if (Object.keys(sanitizedBody).length === 0) return NextResponse.json({ error: 'No valid fields' }, { status: 400 })
await connectDB()
- const student = await Student.findOneAndUpdate({ _id: id, teacherId: userId }, { $set: sanitizedBody }, { new: true })
+ const student = await Student.findOneAndUpdate(
+ { _id: id, teacherId: userId },
+ { $set: sanitizedBody },
+ { new: true, runValidators: true }
+ )📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const body = await req.json().catch(() => ({})) | |
| const sanitizedBody: any = {} | |
| ALLOWED_UPDATE_FIELDS.forEach(k => { if (k in body) sanitizedBody[k] = body[k] }) | |
| if (Object.keys(sanitizedBody).length === 0) return NextResponse.json({ error: 'No valid fields' }, { status: 400 }) | |
| await connectDB() | |
| const student = await Student.findOneAndUpdate( | |
| { _id: id }, | |
| sanitizedBody, | |
| { new: true } | |
| ) | |
| if (!student) return NextResponse.json({ error: 'Not found' }, { status: 404 }) | |
| const student = await Student.findOneAndUpdate({ _id: id, teacherId: userId }, { $set: sanitizedBody }, { new: true }) | |
| if (!student) return NextResponse.json({ error: 'Not found or unauthorized' }, { status: 404 }) | |
| const body = await req.json().catch(() => null) | |
| if (!body || typeof body !== 'object' || Array.isArray(body)) { | |
| return NextResponse.json({ error: 'Invalid JSON body' }, { status: 400 }) | |
| } | |
| const sanitizedBody: Record<string, unknown> = {} | |
| ALLOWED_UPDATE_FIELDS.forEach(k => { if (k in body) sanitizedBody[k] = body[k] }) | |
| if (Object.keys(sanitizedBody).length === 0) return NextResponse.json({ error: 'No valid fields' }, { status: 400 }) | |
| await connectDB() | |
| const student = await Student.findOneAndUpdate( | |
| { _id: id, teacherId: userId }, | |
| { $set: sanitizedBody }, | |
| { new: true, runValidators: true } | |
| ) | |
| if (!student) return NextResponse.json({ error: 'Not found or unauthorized' }, { status: 404 }) |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/api/students/`[id]/route.ts around lines 14 - 20, The request body may be
a JSON primitive which makes the `in` operator throw and also `findOneAndUpdate`
is missing validators; after parsing `req.json()` ensure the body is an object
(typeof body === 'object' && body !== null && !Array.isArray(body') ) and return
a 400 with "No valid fields" if it's not an object or has no allowed keys; build
`sanitizedBody` from `ALLOWED_UPDATE_FIELDS` only when body is a valid object.
Then call `connectDB()` and update the student using `Student.findOneAndUpdate({
_id: id, teacherId: userId }, { $set: sanitizedBody }, { new: true,
runValidators: true })` (include runValidators:true so model validation runs).
Summary by CodeRabbit
Release Notes
Bug Fixes
New Features
Improvements
Removals