fix: resolve issues in API endpoints and dashboard components#11
fix: resolve issues in API endpoints and dashboard components#11AradhyaX wants to merge 9 commits intoJavaScript-Mastery-Pro:mainfrom
Conversation
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 54 minutes and 10 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (11)
📝 WalkthroughWalkthroughThis PR introduces schema-based validation via Zod across multiple API routes, replacing manual field sanitization. It adds authorization scoping to ensure teachers can only modify their own resources, introduces ObjectId validation for MongoDB IDs, improves error handling with precise HTTP status codes, and updates the dashboard to fetch data with pagination and defensive normalization. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 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 |
3a781c2 to
ebcdcb8
Compare
|
Email - [soularadhya@gmail.com) → Number of issues fixed (self-reported) 9 Summary of changes: Fixed authentication scoping issues in API routes |
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (2)
app/dashboard/OverviewClient.tsx (1)
333-352: Upcoming deadlines are derived from a paginated slice.
upcomingDeadlinesis computed from the 100 most-recently-created assignments (sorted desc bycreatedAtserver-side). Long-lived active assignments with imminent deadlines but oldercreatedAtwill be silently dropped once the teacher has >100 assignments. For a dashboard that highlights deadlines, consider querying the API with sort-by-deadline/active filter, or bumping?limit=high enough to cover realistic usage.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/dashboard/OverviewClient.tsx` around lines 333 - 352, upcomingDeadlines is computed from the client-side variable assignments which currently contains only the 100 most-recently-created items, so imminent active assignments older than the 100 newest createdAt entries are omitted; update the data fetching logic instead of post-filtering here: change the API request that populates assignments to include an active status filter and sort by deadline (or increase the ?limit= to a safe maximum), then use the resulting assignments array in OverviewClient.upcomingDeadlines so the top 5 nearest-deadline active assignments are always included (refer to the upcomingDeadlines computation and whatever fetch/populate function that sets assignments).app/api/grades/[id]/route.ts (1)
21-30: DuplicatecalcGrade— extract to a shared helper.Identical to the
calcGradeinapp/api/grades/route.ts(per relevant context). Any future change to the grade scale (e.g., the A+ cutoff inconsistency where> 90is A+ but>= 80is A, so exactly 90 falls into A) must be made in two places. Extract into something likelib/grade.tsand import from both routes.♻️ Suggested shared helper
// lib/grade.ts export 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' }Then in both routes:
-function calcGrade(marks: number, max: number): string { - const pct = (marks / max) * 100 - if (pct > 90) return 'A+' - ... -} +import { calcGrade } from '@/lib/grade'While you're there, please confirm whether
> 90vs>= 90for A+ is deliberate.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/api/grades/`[id]/route.ts around lines 21 - 30, Extract the duplicated calcGrade function into a shared helper (e.g., export function calcGrade in lib/grade.ts) and replace the local calcGrade implementations in both routes with an import from that helper; ensure the helper uses the normalized cutoff (change the first condition to pct >= 90 for 'A+'), export it, and update all call sites to import { calcGrade } from the new module.
🤖 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 13-15: The current Zod schema for the deadline field uses
Date.parse which is too permissive; update the deadline schema (the Zod field
named deadline in the route handlers) to use Zod's stricter validators — e.g.,
replace the refine(Date.parse) approach with z.string().datetime() if you expect
full ISO datetimes or z.string().regex(/^\d{4}-\d{2}-\d{2}$/) (or
z.string().regex for a stricter ISO date) for date-only values, keeping
.optional() as needed; apply the same change to the deadline field in both the
update route schema (the [id] route) and the create route schema so validation
is consistent.
In `@app/api/assignments/route.ts`:
- Around line 12-14: The 'deadline' Zod schema field currently uses Date.parse
in a refine which accepts malformed dates; change the validator on the deadline
field to use Zod's ISO datetime check (e.g., replace the Date.parse-based refine
with z.string().datetime(...) on the deadline field) so only strict ISO
datetimes are accepted; if you need date-only values instead, validate with a
strict YYYY-MM-DD pattern (regex) on the same 'deadline' schema entry.
In `@app/api/attendance/route.ts`:
- Around line 3-13: Replace the loose ObjectId check in StudentIdSchema with a
strict validator (use mongoose.isObjectIdOrHexString(value) or a 24-hex regex)
so only 24-hex strings are accepted; then reuse that StudentIdSchema in
AttendanceSchema and in the GET query validation (the GET guard that currently
duplicates validation) to avoid divergent checks—update the refine predicate on
StudentIdSchema and replace the duplicated GET validation logic to reference
StudentIdSchema.
In `@app/api/grades/`[id]/route.ts:
- Around line 60-82: The code has a TOCTOU between Grade.findOne and
Grade.findOneAndUpdate: compute merged marks/maxMarks from parsed.data and
existing then validate, but a concurrent update can violate marks <= maxMarks
before findOneAndUpdate runs; either add a schema-level validator on the Grade
model that enforces marks <= maxMarks so runValidators: true on
Grade.findOneAndUpdate will catch violations, or make the update atomic by
moving the validation into the findOneAndUpdate filter (use a compound query or
$expr to require (parsed.data.marks ?? existing.marks) <= (parsed.data.maxMarks
?? existing.maxMarks) in the query), and ensure calcGrade is computed from the
values in the same atomic update; update the code paths that call Grade.findOne,
Grade.findOneAndUpdate and any reliance on the pre('save') hook (which does not
run for findOneAndUpdate) to use the chosen approach.
In `@app/api/grades/route.ts`:
- Around line 3-13: The StudentIdSchema currently uses
mongoose.Types.ObjectId.isValid which is too permissive; update the refine
predicate to use mongoose.isObjectIdOrHexString instead to ensure only ObjectId
instances or 24-character hex strings pass, and make the same replacement where
studentId validation occurs later in this file (the other validate calls around
the GradeSchema / any validation at lines ~48-50); keep the same error message
('Invalid studentId') and schema names (StudentIdSchema, GradeSchema) so only
the predicate changes.
In `@app/dashboard/assignments/AssignmentsClient.tsx`:
- Line 347: The code currently sets deadline using the raw ISO substring
"deadline: a.deadline.slice(0, 10)" which uses the UTC calendar date and will
shift for non-UTC users; replace that slice with a timezone-aware local-date
formatter by parsing the ISO into a Date (new Date(a.deadline)) and building a
YYYY-MM-DD string from the Date's local getters (getFullYear(), getMonth()+1,
getDate()) so the <input type="date"> is prefilled with the user's local date;
alternatively, if you intend deadlines to be UTC end-to-end, make that explicit
here and on submit by normalizing to UTC rather than converting to local time.
---
Nitpick comments:
In `@app/api/grades/`[id]/route.ts:
- Around line 21-30: Extract the duplicated calcGrade function into a shared
helper (e.g., export function calcGrade in lib/grade.ts) and replace the local
calcGrade implementations in both routes with an import from that helper; ensure
the helper uses the normalized cutoff (change the first condition to pct >= 90
for 'A+'), export it, and update all call sites to import { calcGrade } from the
new module.
In `@app/dashboard/OverviewClient.tsx`:
- Around line 333-352: upcomingDeadlines is computed from the client-side
variable assignments which currently contains only the 100 most-recently-created
items, so imminent active assignments older than the 100 newest createdAt
entries are omitted; update the data fetching logic instead of post-filtering
here: change the API request that populates assignments to include an active
status filter and sort by deadline (or increase the ?limit= to a safe maximum),
then use the resulting assignments array in OverviewClient.upcomingDeadlines so
the top 5 nearest-deadline active assignments are always included (refer to the
upcomingDeadlines computation and whatever fetch/populate function that sets
assignments).
🪄 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: 88f45c36-b957-40e3-89c5-a7c0ad890148
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (11)
app/api/announcements/[id]/route.tsapp/api/assignments/[id]/route.tsapp/api/assignments/route.tsapp/api/attendance/route.tsapp/api/grades/[id]/route.tsapp/api/grades/route.tsapp/api/profile/route.tsapp/api/students/[id]/route.tsapp/api/students/route.tsapp/dashboard/OverviewClient.tsxapp/dashboard/assignments/AssignmentsClient.tsx
| deadline: z.string().min(1).refine((value) => !Number.isNaN(Date.parse(value)), { | ||
| message: 'Invalid deadline', | ||
| }).optional(), |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
node - <<'NODE'
for (const value of ['2026-02-31', '2026-13-01', '1', '2026-04-18', '2026-04-18T00:00:00.000Z']) {
const ms = Date.parse(value)
console.log(value, Number.isNaN(ms) ? 'invalid' : new Date(ms).toISOString())
}
NODERepository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 251
🏁 Script executed:
# Check if the file exists and read the relevant section
fd -t f "route.ts" | grep "assignments\[id\]" | head -5Repository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 66
🏁 Script executed:
# Find package.json and check Zod version
fd -t f "package.json" | head -1 | xargs cat | jq '.dependencies, .devDependencies' 2>/dev/null || cat package.json | grep -i zod || echo "Checking for package.lock files..."Repository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 512
🏁 Script executed:
# List potential package lock files to check versions
fd -t f "package-lock.json|pnpm-lock.yaml|yarn.lock" | head -1Repository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 101
🏁 Script executed:
# Search more broadly for the route file
find . -name "route.ts" -o -name "route.js" 2>/dev/null | grep -i assignmentRepository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 150
🏁 Script executed:
# Check Zod 4.3.6 API for date validation - web search will help with this
# But first let's see if the file exists anywhere
fd -type f "route.ts" 2>/dev/null | head -20Repository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 66
🏁 Script executed:
# Check app/api directory structure
find app -type d 2>/dev/null | head -20Repository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 483
🏁 Script executed:
# Read the actual file to verify the code and schema
cat -n app/api/assignments/\[id\]/route.ts | head -60Repository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 2613
🏁 Script executed:
# Check Zod 4.3.6 documentation for correct date validation API
# Since it's Zod 4.3.6, let's verify what date validators are available
# Let's also look for how zod is imported and used in the file
grep -n "import.*zod\|z\." app/api/assignments/\[id\]/route.ts | head -20Repository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 583
🌐 Web query:
Zod 4.3.6 date validation api z.iso.date() z.iso.datetime()
💡 Result:
In Zod 4.3.6, the date validation API includes z.iso.date for validating ISO 8601 date strings in YYYY-MM-DD format and z.iso.datetime for ISO 8601 datetime strings. Usage examples: import { z } from "zod"; const dateSchema = z.iso.date; dateSchema.parse("2020-01-01"); // ✅ valid dateSchema.parse("2020-1-1"); // ❌ invalid const datetimeSchema = z.iso.datetime; datetimeSchema.parse("2020-01-01T06:15:00Z"); // ✅ valid datetimeSchema.parse("2020-01-01T06:15:00.123Z"); // ✅ valid (with milliseconds) These are string validators using regex-based checks for ISO formats. They were introduced in Zod v4 (stable release announced recently, with v4.3.6 being a patch release). Ensure standard import import { z } from "zod"; and Zod v4+ installation. TypeScript recognizes these in v4.3.6 when properly installed. Note: z.date validates Date objects, while z.iso.date and z.iso.datetime validate strings. For Date coercion from strings, use z.coerce.date or codecs like z.codec(z.iso.datetime, z.date).
Citations:
- 1: https://v4.zod.dev/api?id=iso-dates
- 2: https://v4.zod.dev/api?id=iso-datetimes
- 3: https://zod.dev/v4
- 4: https://zod.dev/api
- 5: z.iso.date() not recognized in Zod v4.3.6 (TypeScript) colinhacks/zod#5783
🏁 Script executed:
# Let's also check if there's a create route that uses similar validation
cat -n app/api/assignments/route.ts | head -40Repository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 1777
Tighten deadline validation beyond Date.parse.
Date.parse accepts permissive inputs: date rollover (e.g., '2026-02-31' → Mar 3), numeric strings ('1' → 2001-01-01), and other ambiguous formats. Use Zod's stricter ISO validators.
🛡️ Proposed fix
- deadline: z.string().min(1).refine((value) => !Number.isNaN(Date.parse(value)), {
- message: 'Invalid deadline',
- }).optional(),
+ deadline: z.iso.date().optional(),If this endpoint intentionally accepts full datetimes too:
- deadline: z.string().min(1).refine((value) => !Number.isNaN(Date.parse(value)), {
- message: 'Invalid deadline',
- }).optional(),
+ deadline: z.union([z.iso.date(), z.iso.datetime()]).optional(),This also applies to the create route at app/api/assignments/route.ts (lines 12–14).
📝 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.
| deadline: z.string().min(1).refine((value) => !Number.isNaN(Date.parse(value)), { | |
| message: 'Invalid deadline', | |
| }).optional(), | |
| deadline: z.string().datetime().optional(), |
| deadline: z.string().min(1).refine((value) => !Number.isNaN(Date.parse(value)), { | |
| message: 'Invalid deadline', | |
| }).optional(), | |
| deadline: z.union([ | |
| z.string().date(), | |
| z.string().datetime(), | |
| ]).optional(), |
🤖 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 13 - 15, The current Zod
schema for the deadline field uses Date.parse which is too permissive; update
the deadline schema (the Zod field named deadline in the route handlers) to use
Zod's stricter validators — e.g., replace the refine(Date.parse) approach with
z.string().datetime() if you expect full ISO datetimes or
z.string().regex(/^\d{4}-\d{2}-\d{2}$/) (or z.string().regex for a stricter ISO
date) for date-only values, keeping .optional() as needed; apply the same change
to the deadline field in both the update route schema (the [id] route) and the
create route schema so validation is consistent.
| deadline: z.string().min(1).refine((value) => !Number.isNaN(Date.parse(value)), { | ||
| message: 'Invalid deadline', | ||
| }), |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
node - <<'NODE'
for (const value of ['2026-02-31', '2026-13-01', '1', '2026-04-18', '2026-04-18T00:00:00.000Z']) {
const ms = Date.parse(value)
console.log(value, Number.isNaN(ms) ? 'invalid' : new Date(ms).toISOString())
}
NODERepository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 251
🏁 Script executed:
cat package.json | grep -A 2 -B 2 "zod"Repository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 191
🏁 Script executed:
find . -type f \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" \) -exec grep -l "z\\..*date" {} \; | head -10Repository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 113
🏁 Script executed:
cat app/api/assignments/route.tsRepository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 3209
🏁 Script executed:
cat app/api/attendance/route.tsRepository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 5226
🌐 Web query:
Zod 4.3.6 ISO date datetime validation API
💡 Result:
In Zod version 4.3.6, the ISO date and datetime validation API is available via the z.iso namespace on the main z object. Key methods: ISO Date (validates YYYY-MM-DD format): import { z } from 'zod'; const dateSchema = z.iso.date; dateSchema.parse('2020-01-01'); // ✅ valid dateSchema.parse('2020-1-1'); // ❌ invalid (must be padded) dateSchema.parse('2020-01-32'); // ❌ invalid day ISO Datetime (validates ISO 8601 format, default no timezone offsets except Z): const datetimeSchema = z.iso.datetime; datetimeSchema.parse('2020-01-01T06:15:00Z'); // ✅ valid datetimeSchema.parse('2020-01-01T06:15:00.123Z'); // ✅ valid (subseconds) datetimeSchema.parse('2020-01-01T06:15:00+02:00'); // ❌ invalid (offsets not allowed by default) Other related: - z.iso.time: HH:MM[:SS[.s+]] (no offsets) - z.iso.duration: ISO 8601 duration These are regex-based string validations (not semantic date checks like min/max dates; use .refine with date libraries for that). For Date objects: use z.date. To convert ISO string to Date: const stringToDate = z.codec( z.iso.datetime, z.date, { decode: (isoString) => new Date(isoString), encode: (date) => date.toISOString, } ); stringToDate.parse('2024-01-15T10:30:00.000Z'); // returns Date object Official docs: https://zod.dev/ (Zod 4 stable since 2025). Version 4.3.6 released ~Jan 2026 per npm.
Citations:
- 1: https://zod.dev/api?id=iso-dates
- 2: https://v4.zod.dev/api?id=iso-dates
- 3: https://www.zod.dev/api?id=iso-datetimes
- 4: https://zod.dev/api
- 5: https://www.npmjs.com/package/zod
- 6: https://zod.dev/
- 7: https://www.npmjs.com/package/zod/v/4.3.6
Use strict ISO validation for deadline.
Date.parse is permissive and accepts malformed dates. For example, '2026-02-31' parses successfully (rolling over to March 3rd) and '1' parses as year 2001. Use Zod's ISO datetime validator to reject invalid deadlines before persistence.
🛡️ Proposed fix
- deadline: z.string().min(1).refine((value) => !Number.isNaN(Date.parse(value)), {
- message: 'Invalid deadline',
- }),
+ deadline: z.iso.datetime(),If dates without time components are acceptable:
- deadline: z.string().min(1).refine((value) => !Number.isNaN(Date.parse(value)), {
- message: 'Invalid deadline',
- }),
+ deadline: z.union([z.iso.date(), z.iso.datetime()]),📝 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.
| deadline: z.string().min(1).refine((value) => !Number.isNaN(Date.parse(value)), { | |
| message: 'Invalid deadline', | |
| }), | |
| deadline: z.string().datetime(), |
| deadline: z.string().min(1).refine((value) => !Number.isNaN(Date.parse(value)), { | |
| message: 'Invalid deadline', | |
| }), | |
| deadline: z.union([z.string().date(), z.string().datetime()]), |
🤖 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 12 - 14, The 'deadline' Zod schema
field currently uses Date.parse in a refine which accepts malformed dates;
change the validator on the deadline field to use Zod's ISO datetime check
(e.g., replace the Date.parse-based refine with z.string().datetime(...) on the
deadline field) so only strict ISO datetimes are accepted; if you need date-only
values instead, validate with a strict YYYY-MM-DD pattern (regex) on the same
'deadline' schema entry.
| import mongoose from 'mongoose' | ||
| import { connectDB } from '@/lib/mongodb' | ||
| import { Attendance } from '@/models/Attendance' | ||
| import { z } from 'zod' | ||
|
|
||
| const StudentIdSchema = z.string().refine((value) => mongoose.Types.ObjectId.isValid(value), { | ||
| message: 'Invalid studentId', | ||
| }) | ||
|
|
||
| const AttendanceSchema = z.object({ | ||
| studentId: z.string().min(1), | ||
| studentId: StudentIdSchema, |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
In Mongoose 9, does mongoose.Types.ObjectId.isValid accept 12-character non-hex strings, and is mongoose.isObjectIdOrHexString the recommended stricter validator?
💡 Result:
Yes, in Mongoose 9, mongoose.Types.ObjectId.isValid accepts 12-character non-hex strings as valid, consistent with BSON ObjectId behavior where any 12-byte string is technically valid. For example, mongoose.Types.ObjectId.isValid("zzzzzzzzzzzz") returns true. This has been the case across versions, including Mongoose 9, as no breaking changes to this behavior appear in the v9 changelog or migration guide (though v9 makes isValidObjectId stricter for numbers). Yes, mongoose.isObjectIdOrHexString is the recommended stricter validator. It returns true only if the value is a Mongoose ObjectId instance or a 24-character hex string (matching /^[0-9a-fA-F]{24}$/), and false for 12-char non-hex strings like "zzzzzzzzzzzz" or numbers. It was introduced in Mongoose 6.2.5 to address common use cases needing hex validation, and maintainers like @vkarpov15 recommend it over isValid for verifying 24-hex strings (e.g., in issues #15510, #14200, #4083). The official docs note that by default any 12-char string is valid for ObjectId casting, and isObjectIdOrHexString provides the stricter check.
Citations:
- 1: Why does Types.ObjectId.isValid(123) return true? Automattic/mongoose#4083
- 2: Best way to validate an ObjectId Automattic/mongoose#1959
- 3: Types.ObjectId.isValid(123) returns true! Automattic/mongoose#15510
- 4: [6.x behavior change due to change in
bson] Mongoose Types.ObjectId.isValid is now more lax, and now returns true for objects that are "new ObjectId"-able Automattic/mongoose#11419 - 5: Bug: check string if is
ObjectIdor not Automattic/mongoose#14200 - 6: https://mongoosejs.com/docs/api/schemaobjectid.html
- 7: https://mongoosejs.com/docs/migrating_to_9.html
- 8: https://stackoverflow.com/questions/13850819/can-i-determine-if-a-string-is-a-mongodb-objectid
- 9: https://biorate.github.io/core/functions/mongodb.mongoose.isObjectIdOrHexString.html
- 10: https://nesin.io/blog/verify-string-valid-mongodb-objectid-mongoose
🏁 Script executed:
# Check if the file exists and examine the relevant sections
cat -n app/api/attendance/route.ts | head -50Repository: JavaScript-Mastery-Pro/discord-challenge
Length of output: 2098
Use strict ObjectId validation for studentId.
mongoose.Types.ObjectId.isValid accepts any 12-byte string as valid, including 12-character non-hex strings, which can be persisted as bogus attendance studentIds. Use mongoose.isObjectIdOrHexString (stricter, 24-hex only) or a regex pattern instead. Reuse the schema in both the POST validation and GET query guard.
🛡️ Proposed fix
-import mongoose from 'mongoose'
import { connectDB } from '@/lib/mongodb'
import { Attendance } from '@/models/Attendance'
import { z } from 'zod'
-const StudentIdSchema = z.string().refine((value) => mongoose.Types.ObjectId.isValid(value), {
+const StudentIdSchema = z.string().regex(/^[0-9a-fA-F]{24}$/, {
message: 'Invalid studentId',
})- if (studentId && !mongoose.Types.ObjectId.isValid(studentId)) {
+ if (studentId && !StudentIdSchema.safeParse(studentId).success) {
return NextResponse.json(
{ error: "Invalid studentId" },
{ status: 400 },
);Also applies to lines 38–43 (GET validation).
📝 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.
| import mongoose from 'mongoose' | |
| import { connectDB } from '@/lib/mongodb' | |
| import { Attendance } from '@/models/Attendance' | |
| import { z } from 'zod' | |
| const StudentIdSchema = z.string().refine((value) => mongoose.Types.ObjectId.isValid(value), { | |
| message: 'Invalid studentId', | |
| }) | |
| const AttendanceSchema = z.object({ | |
| studentId: z.string().min(1), | |
| studentId: StudentIdSchema, | |
| import { connectDB } from '@/lib/mongodb' | |
| import { Attendance } from '@/models/Attendance' | |
| import { z } from 'zod' | |
| const StudentIdSchema = z.string().regex(/^[0-9a-fA-F]{24}$/, { | |
| message: 'Invalid studentId', | |
| }) | |
| const AttendanceSchema = z.object({ | |
| studentId: StudentIdSchema, |
🤖 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 3 - 13, Replace the loose ObjectId
check in StudentIdSchema with a strict validator (use
mongoose.isObjectIdOrHexString(value) or a 24-hex regex) so only 24-hex strings
are accepted; then reuse that StudentIdSchema in AttendanceSchema and in the GET
query validation (the GET guard that currently duplicates validation) to avoid
divergent checks—update the refine predicate on StudentIdSchema and replace the
duplicated GET validation logic to reference StudentIdSchema.
| await connectDB() | ||
| const existing = await Grade.findOne({ _id: id, teacherId: userId }) | ||
| if (!existing) return NextResponse.json({ error: 'Not found' }, { status: 404 }) | ||
|
|
||
| const marks = parsed.data.marks ?? existing.marks | ||
| const maxMarks = parsed.data.maxMarks ?? existing.maxMarks | ||
| if (marks > maxMarks) { | ||
| return NextResponse.json( | ||
| { error: { fieldErrors: { marks: ['marks must be less than or equal to maxMarks'] } } }, | ||
| { status: 400 }, | ||
| ) | ||
| } | ||
|
|
||
| const grade = await Grade.findOneAndUpdate( | ||
| { _id: id }, | ||
| sanitizedBody, | ||
| { new: true } | ||
| { _id: id, teacherId: userId }, | ||
| { | ||
| $set: { | ||
| ...parsed.data, | ||
| grade: calcGrade(marks, maxMarks), | ||
| }, | ||
| }, | ||
| { new: true, runValidators: true, context: 'query' } | ||
| ) |
There was a problem hiding this comment.
Minor TOCTOU between findOne and findOneAndUpdate.
The marks/maxMarks validation loads the existing doc (line 61), computes the merged values (64-65), validates (66-71), then issues findOneAndUpdate (73-82). A concurrent update landing in between could violate marks <= maxMarks on the final document. The model's pre('save') hook doesn't fire for findOneAndUpdate, so runValidators: true is your only safety net here, and the Grade schema (per relevant context) relies on the save hook for that invariant.
If concurrent grade edits are plausible, either (a) add a schema-level Mongoose validator that compares this.marks and this.maxMarks (so runValidators catches it), or (b) do a single atomic update using $expr / findOneAndUpdate with a compound filter that re-asserts the invariant. Otherwise this is an acceptable, narrow race.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/api/grades/`[id]/route.ts around lines 60 - 82, The code has a TOCTOU
between Grade.findOne and Grade.findOneAndUpdate: compute merged marks/maxMarks
from parsed.data and existing then validate, but a concurrent update can violate
marks <= maxMarks before findOneAndUpdate runs; either add a schema-level
validator on the Grade model that enforces marks <= maxMarks so runValidators:
true on Grade.findOneAndUpdate will catch violations, or make the update atomic
by moving the validation into the findOneAndUpdate filter (use a compound query
or $expr to require (parsed.data.marks ?? existing.marks) <=
(parsed.data.maxMarks ?? existing.maxMarks) in the query), and ensure calcGrade
is computed from the values in the same atomic update; update the code paths
that call Grade.findOne, Grade.findOneAndUpdate and any reliance on the
pre('save') hook (which does not run for findOneAndUpdate) to use the chosen
approach.
| import mongoose from 'mongoose' | ||
| import { connectDB } from '@/lib/mongodb' | ||
| import { Grade } from '@/models/Grade' | ||
| import { z } from 'zod' | ||
|
|
||
| const StudentIdSchema = z.string().refine((value) => mongoose.Types.ObjectId.isValid(value), { | ||
| message: 'Invalid studentId', | ||
| }) | ||
|
|
||
| const GradeSchema = z.object({ | ||
| studentId: z.string().min(1), | ||
| studentId: StudentIdSchema, |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
In Mongoose 9, does mongoose.Types.ObjectId.isValid accept 12-character non-hex strings, and is mongoose.isObjectIdOrHexString the recommended stricter validator?
💡 Result:
Yes, in Mongoose 9, mongoose.Types.ObjectId.isValid accepts 12-character non-hex strings as valid, because it follows BSON ObjectId semantics where any 12-byte sequence (including 12-char strings) is valid. It returns true for strings like "zzzzzzzzzzzz" or "testtesttest". Yes, mongoose.isObjectIdOrHexString is the recommended stricter validator. It returns true only for ObjectId instances or exactly 24-character hex strings (matching /^[0-9a-fA-F]{24}$/), and false for 12-char strings, numbers, or other values. This is explicitly recommended by Mongoose maintainers in multiple GitHub issues for cases needing hex string validation, and the feature was added in Mongoose 6.2.5 with no breaking changes noted in Mongoose 9 changelogs.
Citations:
- 1: Why does Types.ObjectId.isValid(123) return true? Automattic/mongoose#4083
- 2: Best way to validate an ObjectId Automattic/mongoose#1959
- 3: Types.ObjectId.isValid(123) returns true! Automattic/mongoose#15510
- 4: Types.ObjectId.isValid misbehavior Automattic/mongoose#11226
- 5: Bug: check string if is
ObjectIdor not Automattic/mongoose#14200 - 6: https://mongoosejs.com/docs/api/schemaobjectid.html
- 7: https://mongoosejs.com/docs/6.x/docs/api.html
- 8: https://github.com/Automattic/mongoose/blob/master/CHANGELOG.md
Replace mongoose.Types.ObjectId.isValid with mongoose.isObjectIdOrHexString for strict student ID validation.
mongoose.Types.ObjectId.isValid is too permissive and accepts 12-character non-hex strings (e.g., "zzzzzzzzzzzz"), which would pass validation and create grade records for malformed student IDs. mongoose.isObjectIdOrHexString is the recommended strict validator from Mongoose maintainers and only accepts ObjectId instances or exactly 24-character hex strings.
🛡️ Proposed fix
const StudentIdSchema = z.string().refine((value) => mongoose.isObjectIdOrHexString(value), {
message: 'Invalid studentId',
})- if (studentId && !mongoose.Types.ObjectId.isValid(studentId)) {
+ if (studentId && !mongoose.isObjectIdOrHexString(studentId)) {
return NextResponse.json({ error: 'Invalid studentId' }, { status: 400 })
}Also applies to: 48-50
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/api/grades/route.ts` around lines 3 - 13, The StudentIdSchema currently
uses mongoose.Types.ObjectId.isValid which is too permissive; update the refine
predicate to use mongoose.isObjectIdOrHexString instead to ensure only ObjectId
instances or 24-character hex strings pass, and make the same replacement where
studentId validation occurs later in this file (the other validate calls around
the GradeSchema / any validation at lines ~48-50); keep the same error message
('Invalid studentId') and schema names (StudentIdSchema, GradeSchema) so only
the predicate changes.
| subject: a.subject, | ||
| class: a.class, | ||
| deadline: a.deadline, | ||
| deadline: a.deadline.slice(0, 10), |
There was a problem hiding this comment.
Timezone shift when prefilling deadline from ISO string.
The API returns deadline as a full ISO 8601 UTC string (per models/Assignment.ts deadline: Date serialized via .lean()), so a.deadline.slice(0, 10) returns the UTC calendar date, not the user's local date. For users not on UTC, a deadline created locally as e.g. 2024-01-15 can be stored as 2024-01-14T18:30:00.000Z (IST) or 2024-01-15T05:00:00.000Z (EST), and prefilling the <input type="date"> via slice will show a day-off value, which then round-trips back to the server and silently shifts the deadline on every edit.
Use a timezone-aware format instead:
🛠️ Proposed fix
- deadline: a.deadline.slice(0, 10),
+ deadline: (() => {
+ const d = new Date(a.deadline);
+ const y = d.getFullYear();
+ const m = String(d.getMonth() + 1).padStart(2, '0');
+ const day = String(d.getDate()).padStart(2, '0');
+ return `${y}-${m}-${day}`;
+ })(),Or, if the intent is strictly to treat deadlines as UTC dates end-to-end, make that explicit both here (slicing OK) and when submitting from the modal (by normalizing to UTC on the server).
📝 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.
| deadline: a.deadline.slice(0, 10), | |
| deadline: (() => { | |
| const d = new Date(a.deadline); | |
| const y = d.getFullYear(); | |
| const m = String(d.getMonth() + 1).padStart(2, '0'); | |
| const day = String(d.getDate()).padStart(2, '0'); | |
| return `${y}-${m}-${day}`; | |
| })(), |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/dashboard/assignments/AssignmentsClient.tsx` at line 347, The code
currently sets deadline using the raw ISO substring "deadline:
a.deadline.slice(0, 10)" which uses the UTC calendar date and will shift for
non-UTC users; replace that slice with a timezone-aware local-date formatter by
parsing the ISO into a Date (new Date(a.deadline)) and building a YYYY-MM-DD
string from the Date's local getters (getFullYear(), getMonth()+1, getDate()) so
the <input type="date"> is prefilled with the user's local date; alternatively,
if you intend deadlines to be UTC end-to-end, make that explicit here and on
submit by normalizing to UTC rather than converting to local time.
Email - soularadhya@gmail.com
→ Your GitHub PR link https://github.com/AradhyaX/discord-challenge/pull/new/fix/bughunt-aradhya
→ Number of issues fixed (self-reported) 12
→ Short summary of your changes
Secured API Authorization: Fixed authorization loopholes on all PUT/DELETE API routes by enforcing teacherId: userId checks to ensure only resource owners can modify or delete items.
Strict Data Validation: Replaced weak parameter whitelisting with strict Zod schema validation across all endpoints (announcements, assignments, attendance, grades, and students).
React Performance Fixes: Resolved React hooks cascading render bugs in the dashboard components (Navbar and OverviewClient).
Summary by CodeRabbit
Bug Fixes
New Features
Improvements