Skip to content

fix(security): harden auth endpoints against NoSQL injection and add JWT warning#3268

Merged
PierreBrisorgueil merged 3 commits intomasterfrom
fix/security-hardening
Mar 18, 2026
Merged

fix(security): harden auth endpoints against NoSQL injection and add JWT warning#3268
PierreBrisorgueil merged 3 commits intomasterfrom
fix/security-hardening

Conversation

@PierreBrisorgueil
Copy link
Copy Markdown
Contributor

@PierreBrisorgueil PierreBrisorgueil commented Mar 18, 2026

Summary

  • OAuth key allowlist: restrict req.body.key in oauthCallback to ['id', 'sub', 'email'] to prevent arbitrary property injection on provider data lookups
  • String coercion on forgot/reset/validateResetToken: coerce email and token inputs to String() before passing to DB queries, preventing NoSQL injection via object payloads (e.g. {"$gt": ""})
  • Query shape validation in UserRepository.search(): reject any search input containing object values to block operator injection at the repository layer
  • JWT secret startup warning: log a prominent warning when the app starts with the default JWT secret, prompting operators to set DEVKIT_NODE_jwt_secret in production

Test plan

  • All 214 existing unit tests pass without modification
  • Manual test: POST to /api/auth/oauth/google with key: "__proto__" returns 422
  • Manual test: POST to /api/auth/forgot with email: {"$gt": ""} is coerced to string
  • Manual test: POST to /api/auth/reset with token: {"$gt": ""} is coerced to string
  • Verify JWT warning appears in logs when using default secret

Summary by CodeRabbit

Release Notes

  • New Features

    • JWT secret validation now alerts when using default configuration values
    • OAuth authentication flow restricted to authorized provider keys only
  • Bug Fixes

    • Implemented input validation in user search to reject invalid parameters
    • Enhanced password reset handlers with proper string conversion for tokens and emails

Copilot AI review requested due to automatic review settings March 18, 2026 08:14
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 18, 2026

Warning

Rate limit exceeded

@PierreBrisorgueil has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 13 minutes and 26 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: d27eb2ae-9a8c-4b46-a048-c003e8a3ac4d

📥 Commits

Reviewing files that changed from the base of the PR and between 1cbcc41 and 1af7297.

📒 Files selected for processing (1)
  • lib/helpers/config.js

Walkthrough

This PR introduces input validation and sanitization across multiple modules: JWT secret validation on startup, string conversion for authentication tokens and emails, OAuth provider key validation, and object input rejection in user search operations.

Changes

Cohort / File(s) Summary
Configuration Validation
config/index.js, lib/helpers/config.js
Added new validateJwtSecret() function that warns when JWT secret matches a default value; integrated into config initialization when NODE_ENV is not 'test'.
Auth Flow Security
modules/auth/controllers/auth.controller.js, modules/auth/controllers/auth.password.controller.js
Added early validation guard in oauthCallback to restrict client-side app-auth provider keys to ['id', 'sub', 'email']; converted token and email inputs to strings in password reset operations (forgot, validateResetToken, reset).
Data Input Validation
modules/users/repositories/users.repository.js
Added runtime check in search function to reject object-type inputs before database query execution.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

Fix

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and accurately summarizes the main security improvements across the changeset: NoSQL injection hardening and JWT secret warning.
Description check ✅ Passed The description covers all critical sections: comprehensive summary of changes, scope (modules impacted), validation checklist, and test plan. Guardrails section is present with security considerations addressed.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/security-hardening
📝 Coding Plan
  • Generate coding plan for human review comments

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

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR aims to harden authentication-related flows against NoSQL/operator injection and add an operational warning when running with the default JWT secret.

Changes:

  • Added input coercion/allowlisting in auth password + OAuth callback flows to reduce injection risk.
  • Added repository-layer validation in UserRepository.search() to reject object-valued query filters.
  • Added a startup warning when config.jwt.secret is still set to the default value.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
modules/users/repositories/users.repository.js Adds object-value rejection to search() filters to prevent operator injection.
modules/auth/controllers/auth.password.controller.js Coerces email/token to strings before DB lookups in forgot/reset flows.
modules/auth/controllers/auth.controller.js Restricts OAuth req.body.key to an allowlist to prevent arbitrary providerData path injection.
lib/helpers/config.js Introduces validateJwtSecret() to warn when default JWT secret is used.
config/index.js Calls validateJwtSecret() during config initialization (non-test env).

Comment thread modules/users/repositories/users.repository.js Outdated
Comment thread lib/helpers/config.js
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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
modules/auth/controllers/auth.controller.js (1)

315-321: ⚠️ Potential issue | 🟡 Minor

Add missing @returns for modified oauthCallback JSDoc.

oauthCallback was modified and is async; JSDoc should document its resolved return type.

Suggested patch
 /**
  * `@desc` Endpoint for oautCallCallBack
  * `@param` {Object} req - Express request object
  * `@param` {Object} res - Express response object
  * `@param` {Function} next - Express next middleware function
+ * `@returns` {Promise<void>} Sends OAuth response or delegates to passport callback flow.
  */
 const oauthCallback = async (req, res, next) => {

As per coding guidelines: "**/*.js: Every new or modified function must have a JSDoc header..." and "**/*.{js,ts,jsx,tsx}: Every function must have a JSDoc header with @param and @returns documentation".

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

In `@modules/auth/controllers/auth.controller.js` around lines 315 - 321, The
oauthCallback JSDoc is missing a `@returns` entry after being converted to an
async function; update the JSDoc for oauthCallback to include a `@returns` tag
documenting the resolved promise type (for example, `@returns` {Promise<void>}
with a short description like "Resolves when the response has been sent" or
`@returns` {Promise<Express.Response>} if you prefer to indicate the response
object), ensuring the async nature is clearly documented alongside the existing
`@param` tags.
modules/auth/controllers/auth.password.controller.js (1)

65-72: ⚠️ Potential issue | 🟡 Minor

Add missing @returns in validateResetToken JSDoc.

This function was modified and is async, but its JSDoc still omits @returns.

Suggested patch
 /**
  * `@desc` Endpoint to validate Reset Token from link
  * `@param` {Object} req - Express request object
  * `@param` {Object} res - Express response object
+ * `@returns` {Promise<void>} Redirects to valid/invalid reset endpoints.
  */
 const validateResetToken = async (req, res) => {

As per coding guidelines: "**/*.js: Every new or modified function must have a JSDoc header..." and "**/*.{js,ts,jsx,tsx}: Every function must have a JSDoc header with @param and @returns documentation".

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

In `@modules/auth/controllers/auth.password.controller.js` around lines 65 - 72,
The JSDoc for the async function validateResetToken is missing a `@returns` tag;
update the comment block above validateResetToken to include a `@returns`
describing the Promise returned (e.g. `@returns` {Promise<void>} or `@returns`
{Promise<Express.Response>} with a short description like "resolves after
sending HTTP response") so the async nature and return contract are documented
alongside the existing `@param` entries for req and res.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/helpers/config.js`:
- Around line 57-61: Add a JSDoc header to the validateJwtSecret function
describing its purpose and include `@param` and `@returns` tags: document that the
single parameter is the config object (type object) and explain the expected
jwt.secret property, and state that the function returns void (or nothing).
Place the JSDoc immediately above the validateJwtSecret declaration so the
exported helper API is fully documented.

In `@modules/users/repositories/users.repository.js`:
- Around line 79-86: The search() function currently rejects any nested object
and crashes when input is undefined; remove the blanket Object.values loop and
instead guard against undefined/non-object inputs and allow nested query
operators (e.g., { email: { $in: [...] } }). In practice, change search to
normalize input: if input is undefined or typeof input !== 'object' set input =
{} (or throw only for explicitly invalid types), then call
User.find(input).exec(); keep the User.find(...) call and ensure no
Object.values(input) is used without checking input first.

---

Outside diff comments:
In `@modules/auth/controllers/auth.controller.js`:
- Around line 315-321: The oauthCallback JSDoc is missing a `@returns` entry after
being converted to an async function; update the JSDoc for oauthCallback to
include a `@returns` tag documenting the resolved promise type (for example,
`@returns` {Promise<void>} with a short description like "Resolves when the
response has been sent" or `@returns` {Promise<Express.Response>} if you prefer to
indicate the response object), ensuring the async nature is clearly documented
alongside the existing `@param` tags.

In `@modules/auth/controllers/auth.password.controller.js`:
- Around line 65-72: The JSDoc for the async function validateResetToken is
missing a `@returns` tag; update the comment block above validateResetToken to
include a `@returns` describing the Promise returned (e.g. `@returns`
{Promise<void>} or `@returns` {Promise<Express.Response>} with a short description
like "resolves after sending HTTP response") so the async nature and return
contract are documented alongside the existing `@param` entries for req and res.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 1551db4f-7a93-49ff-be4a-6511cd75f801

📥 Commits

Reviewing files that changed from the base of the PR and between be66dc9 and 1cbcc41.

📒 Files selected for processing (5)
  • config/index.js
  • lib/helpers/config.js
  • modules/auth/controllers/auth.controller.js
  • modules/auth/controllers/auth.password.controller.js
  • modules/users/repositories/users.repository.js

Comment thread lib/helpers/config.js
Comment thread modules/users/repositories/users.repository.js Outdated
…ernal queries

The repository-level object check rejected all object values including
legitimate MongoDB operators ($in, etc.) used by seed and internal code.
NoSQL injection is already prevented at controller boundaries via String()
coercion and key allowlists.
@PierreBrisorgueil PierreBrisorgueil merged commit d529d7a into master Mar 18, 2026
3 checks passed
@PierreBrisorgueil PierreBrisorgueil deleted the fix/security-hardening branch March 18, 2026 13:06
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.

2 participants