Skip to content

feat: add bug CRUD routes, controller and model#3

Open
Aarvayn wants to merge 1 commit intomainfrom
feature/bug-crud
Open

feat: add bug CRUD routes, controller and model#3
Aarvayn wants to merge 1 commit intomainfrom
feature/bug-crud

Conversation

@Aarvayn
Copy link
Copy Markdown
Collaborator

@Aarvayn Aarvayn commented Apr 20, 2026

Added CRUD routes for bugs (GET, POST, PUT, DELETE)
Connected to MongoDB using Mongoose

Summary by CodeRabbit

Release Notes

  • New Features

    • Added bug submission and management system with create, read, update, and delete capabilities. Bugs include title, description, difficulty level, tech stack, tags, rewards, and deadlines with automatic solution tracking and status management (Open, Solved, Closed).
  • Chores

    • Updated backend configuration and dependencies for improved stability and performance.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 20, 2026

📝 Walkthrough

Walkthrough

This pull request establishes the initial backend infrastructure for a Node.js/Express API serving bug bounty management features. It introduces server configuration, MongoDB connection setup, a Mongoose Bug model, REST route handlers with CRUD operations, centralized error handling, and project metadata updates.

Changes

Cohort / File(s) Summary
Server Infrastructure
server.js, config/dbConnection.js, constants.js
Added Express server entrypoint with middleware setup (JSON parsing), MongoDB connection initialization, and HTTP status code constants for error responses.
Data Model
Models/bug.js
Defined Mongoose Bug schema with fields for title, description, tech stack, tags, difficulty/status enums, reward, owner/solution references, GitHub metadata, deadline, and tracking flags (isRewardLocked, isAbusive, refundProcessed). Indexes enabled on difficulty, status, and ownerId.
Route Handlers & Middleware
controllers/bugController.js, routes/bugsRoutes.js, middleware/errorHandler.js
Implemented five CRUD controller functions (list, create, get, update, delete) wrapped with async error handling, wired to REST routes (GET/POST /, GET/PUT/DELETE /:id), and added centralized error middleware with status-code-based response formatting.
Project Configuration
package.json, .gitignore
Updated package metadata (name, description, author, license to MIT), changed entrypoint to server.js, adjusted scripts for node/nodemon execution, added express-async-handler dependency, removed cors, bumped dotenv and mongoose versions, and added node_modules and .env to gitignore.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A burrow of bugs, now organized and neat,
With MongoDB schemas and routes so sweet,
Express handlers catching errors in flight,
The backend awakens—bug graveyard takes flight!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: adding bug CRUD routes, controller, and model. It is concise, specific, and clearly reflects the primary purpose of the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/bug-crud

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (3)
core-services/routes/bugsRoutes.js (1)

5-13: Optional: chain handlers on the same router.route() and rename deleteBugs.

Each path currently calls router.route(...) twice. You can collapse the duplication, and deleteBugs deletes a single bug by :id, so the plural name is misleading — prefer deleteBug for consistency with getBug/updateBug.

Proposed refactor
-const {getBugs,createBug,getBug,updateBug,deleteBugs} = require('../controllers/bugController');
-
-router.route('/').get(getBugs);
-
-router.route('/').post(createBug);
-
-router.route('/:id').get(getBug);
-
-router.route('/:id').put(updateBug);
-
-router.route('/:id').delete(deleteBugs);
+const {getBugs, createBug, getBug, updateBug, deleteBug} = require('../controllers/bugController');
+
+router.route('/').get(getBugs).post(createBug);
+router.route('/:id').get(getBug).put(updateBug).delete(deleteBug);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core-services/routes/bugsRoutes.js` around lines 5 - 13, Routes are
duplicated by calling router.route('/') and router.route('/:id') twice and the
delete handler is misnamed; consolidate handlers by chaining methods on the same
router.route call (e.g., router.route('/').get(getBugs).post(createBug) and
router.route('/:id').get(getBug).put(updateBug).delete(deleteBug)) and rename
the exported/defined handler function from deleteBugs to deleteBug (update any
imports/exports/usages accordingly) so naming matches getBug/updateBug and
avoids duplicate route declarations.
core-services/controllers/bugController.js (1)

76-87: Rename deleteBugsdeleteBug (single-resource handler).

The route is DELETE /api/bugs/:id and the handler deletes exactly one document, but the exported name is plural, which is inconsistent with getBug/updateBug and misleading at call sites. Rename here and in routes/bugsRoutes.js and the module exports.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core-services/controllers/bugController.js` around lines 76 - 87, Rename the
exported handler function deleteBugs to deleteBug to match single-resource
semantics and naming of getBug/updateBug: change the function name declaration
from deleteBugs to deleteBug, update all imports/usages where the route
registers the handler (the route that currently references deleteBugs) to use
deleteBug, and update the module exports to export deleteBug instead of
deleteBugs; ensure no other references to deleteBugs remain.
core-services/Models/bug.js (1)

5-27: Add bounds and URL validation for public input fields.

title, description, techStack, tags, and GitHub metadata are user-controlled and currently unbounded. Add reasonable maxlength/array limits and URL validation for repoUrl/issueUrl to reduce abuse and bad links entering the database.

Also applies to: 63-69

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core-services/Models/bug.js` around lines 5 - 27, The schema fields title,
description, techStack, tags (and GitHub fields repoUrl/issueUrl) are currently
unbounded; add sensible limits and URL validation: set maxlength on title (e.g.,
200) and description (e.g., 2000), enforce array-size limits on techStack and
tags with a validator (e.g., max 20 items) and set per-item maxlength (e.g.,
50), and add URL validation for repoUrl and issueUrl using either a regex match
or validator.isURL in their schema definitions (with an appropriate error
message). Update the model entries for title, description, techStack, tags,
repoUrl and issueUrl to include these maxlength/validate constraints so bad or
overly large inputs are rejected.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@core-services/config/dbConnection.js`:
- Around line 14-18: The catch block for mongoose.connect currently uses
console.log(err) and doesn't handle later connection events; change
console.log(err) to console.error(err) in the existing catch so the initial
handshake error goes to stderr, and add listeners on mongoose.connection (e.g.,
mongoose.connection.on('error', handler) and
mongoose.connection.on('disconnected', handler)) to log runtime connection
errors/disconnects and exit or trigger reconnection logic as appropriate so the
process does not silently run with a broken DB; locate the existing
mongoose.connect call and the catch block and attach the connection listeners
there (use the same logging mechanism, e.g., console.error or your
processLogger, and call process.exit(1) or other cleanup inside the handlers).

In `@core-services/controllers/bugController.js`:
- Around line 40-49: Validate req.params.id with mongoose.Types.ObjectId.isValid
before calling Bug.findById in getBug, updateBug, and deleteBugs; if the id is
invalid, set an appropriate status (e.g., res.status(404) or 400) and throw a
new Error ('Bug not found' or 'Invalid id') so the errorHandler gets the status,
and only call Bug.findById when the id is valid to avoid Mongoose CastError.
- Around line 55-70: In updateBug, avoid mass-assignment and the redundant
pre-fetch: build a whitelist object from req.body containing only allowed
updatable fields (e.g., title, description, reward, tags — explicitly exclude
status, ownerId, isAbusive, isRewardLocked, refundProcessed and any keys
starting with $), then call Bug.findByIdAndUpdate(req.params.id,
whitelistObject, { new: true, runValidators: true, context: 'query' }) and check
the returned updatedBug for null to return 404; remove the initial Bug.findById
call and ensure validators are enforced by passing runValidators: true (and
context: 'query' if using mongoose validators that need it).

In `@core-services/middleware/errorHandler.js`:
- Around line 1-32: The errorHandler middleware currently lets unmapped errors
hang, leaks stacks, and never explicitly sets the HTTP status; update the
errorHandler function to: determine the statusCode as res.statusCode &&
res.statusCode !== 200 ? res.statusCode : constants.SERVER_ERROR (so unmapped
errors become 500), create a lookup map (e.g., statusTitles = {
[constants.VALIDATION_ERROR]: 'Validation Failed', ... }) instead of the switch,
call res.status(statusCode) before sending a response, and send res.json with {
title: statusTitles[statusCode] || 'Error', message: err.message,
...(process.env.NODE_ENV !== 'production' && { stackTrace: err.stack }) } so
stack traces are only included outside production and every path returns a JSON
response; keep the middleware signature errorHandler(err, req, res, next) and
ensure the default path returns a 500 JSON response instead of logging and
falling through.

In `@core-services/Models/bug.js`:
- Around line 40-83: The updateBug handler currently passes req.body straight
into findByIdAndUpdate, allowing clients to change protected server-controlled
fields; update the updateBug function to mirror createBug's allowlist pattern:
build an updates object by extracting only permitted mutable fields from
req.body (e.g., title, description, github.repoUrl, github.filePath,
github.issueUrl, deadline, reward-related editable fields) and explicitly
exclude server-controlled fields such as status, acceptedSolutionId,
isRewardLocked, isAbusive, refundProcessed, ownerId, solutionCount before
calling Bug.findByIdAndUpdate; ensure you still call findByIdAndUpdate with {
new: true, runValidators: true } so validation and return of the updated
document continue to work.
- Around line 47-52: The ownerId field is optional in the Bug schema and
createBug doesn't set it from the authenticated user, allowing orphaned bugs;
update the Bug model's ownerId schema (symbol: ownerId) to include required:
true and keep the ObjectId ref to "User", and then modify the createBug
controller (symbol: createBug) to source ownerId from the authenticated request
context (e.g., req.user.id or req.user._id) instead of the request body—also
remove ownerId from the destructured body fields ({title, description, deadline,
reward}) so persisted bugs always have a validated owner reference for
authorization and listing.
- Around line 58-62: The solutionCount schema field lacks a minimum constraint
allowing negative values; update the solutionCount definition in Models/bug.js
(field name: solutionCount) to include a Mongoose validation of min: 0 (and keep
default: 0) so any direct updates (e.g., via updateBug) will be rejected if
negative; ensure the schema change is saved and tests/requests that set
solutionCount < 0 now fail validation.

In `@core-services/server.js`:
- Around line 7-18: Await the connectDb() promise before starting the HTTP
server and add graceful shutdown handlers: call await connectDb() inside an
async init function (or top-level promise handling) and only then call server =
app.listen(...); wrap connectDb() in try/catch so on failure you log the error
and exit with non-zero status; register SIGINT/SIGTERM handlers that close the
HTTP server (server.close()) and the DB connection (e.g.,
mongoose.connection.close() or a provided disconnect function) and then exit
cleanly; keep existing symbols (connectDb, app.listen, errorHandler, router) and
ensure server variable is stored so the shutdown handlers can close it.

---

Nitpick comments:
In `@core-services/controllers/bugController.js`:
- Around line 76-87: Rename the exported handler function deleteBugs to
deleteBug to match single-resource semantics and naming of getBug/updateBug:
change the function name declaration from deleteBugs to deleteBug, update all
imports/usages where the route registers the handler (the route that currently
references deleteBugs) to use deleteBug, and update the module exports to export
deleteBug instead of deleteBugs; ensure no other references to deleteBugs
remain.

In `@core-services/Models/bug.js`:
- Around line 5-27: The schema fields title, description, techStack, tags (and
GitHub fields repoUrl/issueUrl) are currently unbounded; add sensible limits and
URL validation: set maxlength on title (e.g., 200) and description (e.g., 2000),
enforce array-size limits on techStack and tags with a validator (e.g., max 20
items) and set per-item maxlength (e.g., 50), and add URL validation for repoUrl
and issueUrl using either a regex match or validator.isURL in their schema
definitions (with an appropriate error message). Update the model entries for
title, description, techStack, tags, repoUrl and issueUrl to include these
maxlength/validate constraints so bad or overly large inputs are rejected.

In `@core-services/routes/bugsRoutes.js`:
- Around line 5-13: Routes are duplicated by calling router.route('/') and
router.route('/:id') twice and the delete handler is misnamed; consolidate
handlers by chaining methods on the same router.route call (e.g.,
router.route('/').get(getBugs).post(createBug) and
router.route('/:id').get(getBug).put(updateBug).delete(deleteBug)) and rename
the exported/defined handler function from deleteBugs to deleteBug (update any
imports/exports/usages accordingly) so naming matches getBug/updateBug and
avoids duplicate route declarations.
🪄 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: c9f9ba99-b3e0-4888-9f39-bfdcc5e7ab51

📥 Commits

Reviewing files that changed from the base of the PR and between bb17ae6 and 00072cf.

⛔ Files ignored due to path filters (1)
  • core-services/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (9)
  • core-services/.gitignore
  • core-services/Models/bug.js
  • core-services/config/dbConnection.js
  • core-services/constants.js
  • core-services/controllers/bugController.js
  • core-services/middleware/errorHandler.js
  • core-services/package.json
  • core-services/routes/bugsRoutes.js
  • core-services/server.js

Comment on lines +14 to +18
catch (err)
{
console.log(err);
process.exit(1);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Log errors to stderr and add disconnect handling.

Two minor concerns:

  • console.log(err) writes DB connection failure to stdout; use console.error so errors land in the proper stream.
  • mongoose.connect only rejects on the initial handshake. Later disconnects, auth failures, or topology errors won't hit this catch. Attach listeners on mongoose.connection (error, disconnected) to surface runtime issues; otherwise the process will silently run with a broken DB while Mongoose buffers (and eventually times out) every query.
Proposed fix
     catch (err)
     {
-        console.log(err);
+        console.error('Database connection failed:', err);
         process.exit(1);
     }
 };
+
+mongoose.connection.on('error', (err) => console.error('Mongoose error:', err));
+mongoose.connection.on('disconnected', () => console.warn('Mongoose disconnected'));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core-services/config/dbConnection.js` around lines 14 - 18, The catch block
for mongoose.connect currently uses console.log(err) and doesn't handle later
connection events; change console.log(err) to console.error(err) in the existing
catch so the initial handshake error goes to stderr, and add listeners on
mongoose.connection (e.g., mongoose.connection.on('error', handler) and
mongoose.connection.on('disconnected', handler)) to log runtime connection
errors/disconnects and exit or trigger reconnection logic as appropriate so the
process does not silently run with a broken DB; locate the existing
mongoose.connect call and the catch block and attach the connection listeners
there (use the same logging mechanism, e.g., console.error or your
processLogger, and call process.exit(1) or other cleanup inside the handlers).

Comment on lines +40 to +49
const getBug = asyncHandler(async (req, res) =>
{
const bug = await Bug.findById(req.params.id);
if(!bug)
{
res.status(404);
throw new Error('Bug not found');
}
res.status(200).json(bug);
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Validate req.params.id as an ObjectId before findById.

getBug, updateBug, and deleteBugs all pass req.params.id straight into Bug.findById. If the caller sends something that isn't a 24-char hex string (e.g. /api/bugs/foo), Mongoose throws a CastError before your if (!bug) guard runs — res.status(...) was never set, so with the current errorHandler the default branch hits and the request hangs (see the errorHandler comment). Validate up front and return a proper 404/400.

Proposed fix (apply in getBug, updateBug, deleteBugs)
 const mongoose = require('mongoose');
 ...
 const getBug = asyncHandler(async (req, res) => 
 {
+        if (!mongoose.isValidObjectId(req.params.id))
+        {
+            res.status(404);
+            throw new Error('Bug not found');
+        }
         const bug = await Bug.findById(req.params.id);
         if(!bug)
         {
            res.status(404);
            throw new Error('Bug not found');
         }
         res.status(200).json(bug);
 });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core-services/controllers/bugController.js` around lines 40 - 49, Validate
req.params.id with mongoose.Types.ObjectId.isValid before calling Bug.findById
in getBug, updateBug, and deleteBugs; if the id is invalid, set an appropriate
status (e.g., res.status(404) or 400) and throw a new Error ('Bug not found' or
'Invalid id') so the errorHandler gets the status, and only call Bug.findById
when the id is valid to avoid Mongoose CastError.

Comment on lines +55 to +70
const updateBug = asyncHandler(async(req, res) =>
{
const bug = await Bug.findById(req.params.id);
if(!bug)
{
res.status(404);
throw new Error('Bug not found');
}
const updatedBug = await Bug.findByIdAndUpdate(
req.params.id,
req.body,
{new: true}
);

res.status(200).json(updatedBug);
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

mongoose 9 findByIdAndUpdate runValidators default behavior

💡 Result:

In Mongoose 9, the default behavior of Model.findByIdAndUpdate regarding runValidators is false. Update validators, including those triggered by runValidators: true, are off by default for findByIdAndUpdate and similar update operations. You must explicitly set { runValidators: true } in the options to enable validation on updates. This has been consistent across Mongoose versions, including 9.x, with no changes noted in the 9.0 changelog or migration guides.

Citations:


🏁 Script executed:

# Find the Bug model/schema definition
fd -t f Bug -i | grep -E '\.(js|ts)$'

Repository: call-0f-code/Bug-Graveyard

Length of output: 207


🏁 Script executed:

# Also search for Bug schema/model definition in common patterns
rg "const Bug|export.*Bug|mongoose.model.*Bug" -t js -t ts -A 5

Repository: call-0f-code/Bug-Graveyard

Length of output: 595


🏁 Script executed:

# Check the Bug schema definition in the Models directory (since that's what the controller imports)
cat -n core-services/Models/bug.js

Repository: call-0f-code/Bug-Graveyard

Length of output: 2130


🏁 Script executed:

# Also check the src models version to see if there are differences
cat -n core-services/src/models/Bug.js

Repository: call-0f-code/Bug-Graveyard

Length of output: 1892


🏁 Script executed:

# Let's check the full updateBug function and surrounding context
sed -n '45,90p' core-services/controllers/bugController.js

Repository: call-0f-code/Bug-Graveyard

Length of output: 979


🏁 Script executed:

# Also check if there are any other update operations or patterns in the controller
rg "findByIdAndUpdate|findOneAndUpdate" core-services/controllers/bugController.js -A 10 -B 2

Repository: call-0f-code/Bug-Graveyard

Length of output: 332


Harden updateBug: whitelist fields, run validators, drop redundant fetch.

Three issues here:

  1. Mass assignment / NoSQL injection risk. Passing req.body directly to findByIdAndUpdate lets a client set arbitrary fields or inject Mongo operators (e.g. {"$rename": {...}}). The schema includes sensitive fields (status, ownerId, isAbusive, isRewardLocked, refundProcessed) that should never be client-editable. Pick the allowed fields explicitly.

  2. Schema validators don't run by default on findByIdAndUpdate. Pass runValidators: true so required rules on title, description, reward and the min: 1 constraint on reward are enforced on updates.

  3. The preceding findById is redundant — findByIdAndUpdate already returns null if no document matches, so you can check the result instead and save a round trip.

Proposed fix
-const updateBug = asyncHandler(async(req, res) => 
-{
-    const bug = await Bug.findById(req.params.id);
-    if(!bug)
-    {
-        res.status(404);
-        throw new Error('Bug not found');
-    }
-    const updatedBug = await Bug.findByIdAndUpdate(
-        req.params.id,
-        req.body,
-        {new: true}
-    );
-
-    res.status(200).json(updatedBug);
-});
+const updateBug = asyncHandler(async (req, res) =>
+{
+    const { title, description, deadline, reward } = req.body;
+    const updates = { title, description, deadline, reward };
+    Object.keys(updates).forEach(k => updates[k] === undefined && delete updates[k]);
+
+    const updatedBug = await Bug.findByIdAndUpdate(
+        req.params.id,
+        updates,
+        { new: true, runValidators: true }
+    );
+    if (!updatedBug)
+    {
+        res.status(404);
+        throw new Error('Bug not found');
+    }
+    res.status(200).json(updatedBug);
+});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core-services/controllers/bugController.js` around lines 55 - 70, In
updateBug, avoid mass-assignment and the redundant pre-fetch: build a whitelist
object from req.body containing only allowed updatable fields (e.g., title,
description, reward, tags — explicitly exclude status, ownerId, isAbusive,
isRewardLocked, refundProcessed and any keys starting with $), then call
Bug.findByIdAndUpdate(req.params.id, whitelistObject, { new: true,
runValidators: true, context: 'query' }) and check the returned updatedBug for
null to return 404; remove the initial Bug.findById call and ensure validators
are enforced by passing runValidators: true (and context: 'query' if using
mongoose validators that need it).

Comment on lines +1 to +32
const{constants} = require('../constants');
const errorHandler = (err,req,res,next) =>
{
const statusCode = res.statusCode ? res.statusCode: 500;

switch (statusCode)
{
case constants.VALIDATION_ERROR:
res.json({title: "Validation Failed",message: err.message,stackTrace: err.stack});
break;

case constants.NOT_FOUND:
res.json({title: "Not Found",message: err.message,stackTrace: err.stack});
break;

case constants.UNAUTHORIZED:
res.json({title: "Not Authorized",message: err.message,stackTrace: err.stack});
break;

case constants.FORBIDDEN:
res.json({title: "Forbidden",message: err.message,stackTrace: err.stack});
break;

case constants.SERVER_ERROR:
res.json({title: "Server Error",message: err.message,stackTrace: err.stack});
break;

default:
console.log('No Error');
break;
}
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: unmapped errors leave the request hanging; stack traces leak to clients.

Several problems on this hot path:

  1. Request hang on any unmapped error. In Express, res.statusCode defaults to 200. So if any handler throws without first calling res.status(400|401|403|404|500) (e.g. a Mongoose CastError from an invalid :id in getBug/updateBug/deleteBugs, a schema validation failure in createBug, a DB timeout, a thrown TypeError, etc.), statusCode becomes 200, falls through to default, logs "No Error", and never sends a response — the client hangs until its socket timeout. This will bite you the first time anyone hits /api/bugs/not-an-object-id.
  2. Status on the wire is never explicitly set. The middleware should call res.status(statusCode) before res.json(...) so the HTTP status matches the payload regardless of what the controller set.
  3. err.stack is returned in the response body. That's an information-disclosure issue in production; gate it on NODE_ENV !== 'production'.
  4. The switch on constants is verbose and duplicated — a lookup table is clearer and removes the risk of forgetting a break.
Proposed fix
-const{constants} = require('../constants');
-const errorHandler = (err,req,res,next) =>
-{
-    const statusCode = res.statusCode ? res.statusCode: 500;
-
-    switch (statusCode) 
-    {
-        case constants.VALIDATION_ERROR:
-            res.json({title: "Validation Failed",message: err.message,stackTrace: err.stack});
-            break;
-
-        case constants.NOT_FOUND:
-            res.json({title: "Not Found",message: err.message,stackTrace: err.stack});
-            break;
-        
-        case constants.UNAUTHORIZED:
-            res.json({title: "Not Authorized",message: err.message,stackTrace: err.stack});
-            break;    
-
-        case constants.FORBIDDEN:
-            res.json({title: "Forbidden",message: err.message,stackTrace: err.stack});
-            break;
-
-        case constants.SERVER_ERROR:
-            res.json({title: "Server Error",message: err.message,stackTrace: err.stack});
-            break;
-
-        default:
-            console.log('No Error');
-            break;
-    }
-};
+const { constants } = require('../constants');
+
+const TITLES = {
+    [constants.VALIDATION_ERROR]: 'Validation Failed',
+    [constants.NOT_FOUND]: 'Not Found',
+    [constants.UNAUTHORIZED]: 'Not Authorized',
+    [constants.FORBIDDEN]: 'Forbidden',
+    [constants.SERVER_ERROR]: 'Server Error',
+};
+
+const errorHandler = (err, req, res, next) => {
+    // If no explicit error status was set by the controller, treat as 500.
+    const statusCode =
+        res.statusCode && res.statusCode !== 200 ? res.statusCode : 500;
+
+    res.status(statusCode).json({
+        title: TITLES[statusCode] || 'Error',
+        message: err.message,
+        ...(process.env.NODE_ENV !== 'production' && { stackTrace: err.stack }),
+    });
+};
 
 module.exports = errorHandler;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core-services/middleware/errorHandler.js` around lines 1 - 32, The
errorHandler middleware currently lets unmapped errors hang, leaks stacks, and
never explicitly sets the HTTP status; update the errorHandler function to:
determine the statusCode as res.statusCode && res.statusCode !== 200 ?
res.statusCode : constants.SERVER_ERROR (so unmapped errors become 500), create
a lookup map (e.g., statusTitles = { [constants.VALIDATION_ERROR]: 'Validation
Failed', ... }) instead of the switch, call res.status(statusCode) before
sending a response, and send res.json with { title: statusTitles[statusCode] ||
'Error', message: err.message, ...(process.env.NODE_ENV !== 'production' && {
stackTrace: err.stack }) } so stack traces are only included outside production
and every path returns a JSON response; keep the middleware signature
errorHandler(err, req, res, next) and ensure the default path returns a 500 JSON
response instead of logging and falling through.

Comment on lines +40 to +83
status:
{
type: String,
enum: ["OPEN", "SOLVED", "CLOSED"],
default: "OPEN",
index: true
},
ownerId:
{
type: mongoose.Schema.Types.ObjectId,
ref: "User",
index: true
},
acceptedSolutionId:
{
type: mongoose.Schema.Types.ObjectId,
ref: "Solution"
},
solutionCount:
{
type: Number,
default: 0
},
github:
{
repoUrl: String,
filePath: String,
issueUrl: String
},
deadline: Date,
isRewardLocked:
{
type: Boolean,
default: true
},
isAbusive:
{
type: Boolean,
default: false
},
refundProcessed:
{
type: Boolean,
default: false
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Inspect Bug write paths for raw request-body persistence.
# Expected: Bug.create/findByIdAndUpdate should use an explicit allowlist DTO, not req.body directly.

rg -n -C4 'Bug\.(create|findByIdAndUpdate|findOneAndUpdate|updateOne)\s*\(|req\.body' --iglob '*.js'

Repository: call-0f-code/Bug-Graveyard

Length of output: 1981


🏁 Script executed:

#!/bin/bash
# In bugController.js updateBug function, replace req.body with explicit field allowlist
# Example: const {title, description, reward} = req.body; then pass {title, description, reward} to findByIdAndUpdate

Repository: call-0f-code/Bug-Graveyard

Length of output: 52


Fix the update handler to whitelist mutable fields.

While createBug correctly whitelists fields before creation, the updateBug handler passes req.body directly to findByIdAndUpdate, allowing users to modify protected fields. Extract allowed fields from the request body before the update call, mirroring the allowlist pattern already used in create.

Fields like status, acceptedSolutionId, isRewardLocked, isAbusive, and refundProcessed must remain server-controlled and are vulnerable in the current update path.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core-services/Models/bug.js` around lines 40 - 83, The updateBug handler
currently passes req.body straight into findByIdAndUpdate, allowing clients to
change protected server-controlled fields; update the updateBug function to
mirror createBug's allowlist pattern: build an updates object by extracting only
permitted mutable fields from req.body (e.g., title, description,
github.repoUrl, github.filePath, github.issueUrl, deadline, reward-related
editable fields) and explicitly exclude server-controlled fields such as status,
acceptedSolutionId, isRewardLocked, isAbusive, refundProcessed, ownerId,
solutionCount before calling Bug.findByIdAndUpdate; ensure you still call
findByIdAndUpdate with { new: true, runValidators: true } so validation and
return of the updated document continue to work.

Comment on lines +47 to +52
ownerId:
{
type: mongoose.Schema.Types.ObjectId,
ref: "User",
index: true
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "*bugController*" -o -name "*bug.js" | grep -E "\.(js|ts)$"

Repository: call-0f-code/Bug-Graveyard

Length of output: 144


🏁 Script executed:

cd core-services && git ls-files | grep -i bug | head -20

Repository: call-0f-code/Bug-Graveyard

Length of output: 151


🏁 Script executed:

cat -n core-services/Models/bug.js

Repository: call-0f-code/Bug-Graveyard

Length of output: 2130


🏁 Script executed:

cat -n core-services/controllers/bugController.js 2>/dev/null || echo "File not found in expected location"

Repository: call-0f-code/Bug-Graveyard

Length of output: 2728


Require ownerId for created bugs.

The ownerId field is currently optional, and the controller's createBug function (line 21) does not set this value from the authenticated user—it only extracts {title, description, deadline, reward} from the request. This allows bugs to be persisted with a null ownerId, creating orphan records that cannot be reliably authorized, listed by owner, or refunded. The schema must enforce required: true, and the controller must source ownerId from the authenticated request context rather than the request body.

🛡️ Proposed schema change
     ownerId: 
     {
       type: mongoose.Schema.Types.ObjectId,
       ref: "User",
+      required: true,
       index: 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.

Suggested change
ownerId:
{
type: mongoose.Schema.Types.ObjectId,
ref: "User",
index: true
},
ownerId:
{
type: mongoose.Schema.Types.ObjectId,
ref: "User",
required: true,
index: true
},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core-services/Models/bug.js` around lines 47 - 52, The ownerId field is
optional in the Bug schema and createBug doesn't set it from the authenticated
user, allowing orphaned bugs; update the Bug model's ownerId schema (symbol:
ownerId) to include required: true and keep the ObjectId ref to "User", and then
modify the createBug controller (symbol: createBug) to source ownerId from the
authenticated request context (e.g., req.user.id or req.user._id) instead of the
request body—also remove ownerId from the destructured body fields ({title,
description, deadline, reward}) so persisted bugs always have a validated owner
reference for authorization and listing.

Comment on lines +58 to +62
solutionCount:
{
type: Number,
default: 0
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

cd core-services && cat -n Models/bug.js | head -80

Repository: call-0f-code/Bug-Graveyard

Length of output: 1925


🏁 Script executed:

cd core-services && rg "solutionCount" --type js -B 2 -A 2

Repository: call-0f-code/Bug-Graveyard

Length of output: 384


🏁 Script executed:

cd core-services && rg "\$inc|solutionCount\s*[:=]|updateOne|updateMany|findByIdAndUpdate" --type js -B 2 -A 2 | head -100

Repository: call-0f-code/Bug-Graveyard

Length of output: 673


🏁 Script executed:

cd core-services && find . -name "*[Ss]olution*" -type f | grep -E "\.(js|ts)$"

Repository: call-0f-code/Bug-Graveyard

Length of output: 94


🏁 Script executed:

cd core-services && cat src/models/Solution.js && echo "---" && find . -name "*[Ss]olution*[Cc]ontroller*" -type f

Repository: call-0f-code/Bug-Graveyard

Length of output: 887


🏁 Script executed:

cd core-services && find . -type f -name "*.js" -exec rg -l "new Solution|solution.*save|solutionCount.*\+|\$inc.*solutionCount" {} \; | head -10

Repository: call-0f-code/Bug-Graveyard

Length of output: 52


🏁 Script executed:

cd core-services && rg "solution|Solution" --type js -i | grep -i "controller\|route" | head -20

Repository: call-0f-code/Bug-Graveyard

Length of output: 52


🏁 Script executed:

cd core-services && ls -la controllers/ && ls -la src/controllers/ 2>/dev/null || echo "src/controllers not found"

Repository: call-0f-code/Bug-Graveyard

Length of output: 419


🏁 Script executed:

cd core-services && cat -n controllers/bugController.js

Repository: call-0f-code/Bug-Graveyard

Length of output: 2728


🏁 Script executed:

cd core-services && find . -name "*route*" -type f && find . -name "*middleware*" -type f

Repository: call-0f-code/Bug-Graveyard

Length of output: 52


🏁 Script executed:

cd core-services && rg -i "bug|solution" --type js | grep -i "increment\|decrement\|add\|remove" | head -20

Repository: call-0f-code/Bug-Graveyard

Length of output: 52


Add minimum value validation to prevent negative solutionCount.

The updateBug controller allows direct updates to solutionCount via request body, making it possible to set negative values. A min: 0 validation constraint should be added to the schema to prevent invalid counter states that could corrupt listing/sorting logic.

Proposed validation
     solutionCount: 
     {
       type: Number,
-      default: 0
+      default: 0,
+      min: 0
     },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core-services/Models/bug.js` around lines 58 - 62, The solutionCount schema
field lacks a minimum constraint allowing negative values; update the
solutionCount definition in Models/bug.js (field name: solutionCount) to include
a Mongoose validation of min: 0 (and keep default: 0) so any direct updates
(e.g., via updateBug) will be rejected if negative; ensure the schema change is
saved and tests/requests that set solutionCount < 0 now fail validation.

Comment thread core-services/server.js
Comment on lines +7 to +18
connectDb();
const app = express();

const port = process.env.PORT || 5000;

app.use(express.json());
app.use('/api/bugs',router);
app.use(errorHandler);

app.listen(port, () => {
console.log('server running on port', port);
}); No newline at end of file
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Await connectDb() and add graceful shutdown.

connectDb() returns a promise but is called fire-and-forget, so app.listen starts before the DB connection is established. Mongoose buffers commands briefly, but if the initial connect fails, process.exit(1) runs while the HTTP server is already accepting traffic — first requests may 500 or hang. Start the listener only after the DB is ready, and register termination handlers so containers/orchestrators can stop the process cleanly.

Proposed fix
-connectDb();
-const app = express();
+const app = express();
 
 const port = process.env.PORT || 5000;
 
 app.use(express.json());
 app.use('/api/bugs',router);
 app.use(errorHandler);
 
-app.listen(port, () => {
-    console.log('server running on port', port);
-});
+connectDb().then(() => {
+    const server = app.listen(port, () => {
+        console.log('server running on port', port);
+    });
+    const shutdown = () => server.close(() => process.exit(0));
+    process.on('SIGTERM', shutdown);
+    process.on('SIGINT', shutdown);
+});
📝 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.

Suggested change
connectDb();
const app = express();
const port = process.env.PORT || 5000;
app.use(express.json());
app.use('/api/bugs',router);
app.use(errorHandler);
app.listen(port, () => {
console.log('server running on port', port);
});
const app = express();
const port = process.env.PORT || 5000;
app.use(express.json());
app.use('/api/bugs',router);
app.use(errorHandler);
connectDb().then(() => {
const server = app.listen(port, () => {
console.log('server running on port', port);
});
const shutdown = () => server.close(() => process.exit(0));
process.on('SIGTERM', shutdown);
process.on('SIGINT', shutdown);
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core-services/server.js` around lines 7 - 18, Await the connectDb() promise
before starting the HTTP server and add graceful shutdown handlers: call await
connectDb() inside an async init function (or top-level promise handling) and
only then call server = app.listen(...); wrap connectDb() in try/catch so on
failure you log the error and exit with non-zero status; register SIGINT/SIGTERM
handlers that close the HTTP server (server.close()) and the DB connection
(e.g., mongoose.connection.close() or a provided disconnect function) and then
exit cleanly; keep existing symbols (connectDb, app.listen, errorHandler,
router) and ensure server variable is stored so the shutdown handlers can close
it.

@Aarvayn Aarvayn requested a review from Harish-Naruto April 20, 2026 14:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant