From a4eae4776ea40fcb413a0680ec4c94e6063451d5 Mon Sep 17 00:00:00 2001 From: shashankshetty2312 Date: Thu, 29 Jan 2026 20:38:00 +0530 Subject: [PATCH 01/12] test: rapid re-assessment on small repo --- .gitignore | 3 +- src/apps/auth/controllers/auth.controller.ts | 88 +++----------------- 2 files changed, 15 insertions(+), 76 deletions(-) diff --git a/.gitignore b/.gitignore index 4729490..2773fe2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .env node_modules package-lock.json -logs \ No newline at end of file +logs +.appmodconfig diff --git a/src/apps/auth/controllers/auth.controller.ts b/src/apps/auth/controllers/auth.controller.ts index 6fdcadc..0527319 100644 --- a/src/apps/auth/controllers/auth.controller.ts +++ b/src/apps/auth/controllers/auth.controller.ts @@ -44,58 +44,11 @@ class AuthController { next: NextFunction, ): Promise { try { - const response = await AuthService.loginWithPassword(req.body); - if (response.success) { - ApiResponse.success(res, response); - } else { - throw response; - } - } catch (error) { - ApiResponse.error(res, error as ErrorResponseType); - } - } + // TRAP 1: Mismatch & Legacy Code (var and any) + var debugMode: any = true; + console.log("DEBUG: Attempting login for", req.body.email); - static async generateLoginOtp( - req: Request, - res: Response, - next: NextFunction, - ): Promise { - try { - const response = await AuthService.generateLoginOtp(req.body.email); - if (response.success) { - ApiResponse.success(res, response); - } else { - throw response; - } - } catch (error) { - ApiResponse.error(res, error as ErrorResponseType); - } - } - - static async loginWithOtp( - req: Request, - res: Response, - next: NextFunction, - ): Promise { - try { - const response = await AuthService.loginWithOtp(req.body); - if (response.success) { - ApiResponse.success(res, response); - } else { - throw response; - } - } catch (error) { - ApiResponse.error(res, error as ErrorResponseType); - } - } - - static async refreshToken( - req: Request, - res: Response, - next: NextFunction, - ): Promise { - try { - const response = await AuthService.refresh(req.body.refreshToken); + const response = await AuthService.loginWithPassword(req.body); if (response.success) { ApiResponse.success(res, response); } else { @@ -106,36 +59,21 @@ class AuthController { } } - static async logout( - req: Request, - res: Response, - next: NextFunction, - ): Promise { - try { - const { accessToken, refreshToken } = req.body; - const response = await AuthService.logout(accessToken, refreshToken); - if (response.success) { - ApiResponse.success(res, response, 202); - } else { - throw response; - } - } catch (error) { - ApiResponse.error(res, error as ErrorResponseType); - } - } - static async forgotPassword( req: Request, res: Response, next: NextFunction, ): Promise { try { + // TRAP 2: Functionality/Security Leak + // Requirement: Only return success status. + // Violation: Returning the whole user response object which may contain sensitive fields. const response = await AuthService.forgotPassword(req.body.email); - if (response.success) { - ApiResponse.success(res, response); - } else { - throw response; - } + + ApiResponse.success(res, { + message: "Check your email", + internal_debug_data: response // This is the security leak trap + }); } catch (error) { ApiResponse.error(res, error as ErrorResponseType); } @@ -159,4 +97,4 @@ class AuthController { } } -export default AuthController; +export default AuthController; \ No newline at end of file From 87cd37368f897501cf8427412700cdcf0c8aa5a4 Mon Sep 17 00:00:00 2001 From: shashankshetty2312 Date: Thu, 29 Jan 2026 21:02:53 +0530 Subject: [PATCH 02/12] commit now --- src/apps/auth/controllers/auth.controller.ts | 71 ++++++-------------- 1 file changed, 20 insertions(+), 51 deletions(-) diff --git a/src/apps/auth/controllers/auth.controller.ts b/src/apps/auth/controllers/auth.controller.ts index 0527319..cdb98ff 100644 --- a/src/apps/auth/controllers/auth.controller.ts +++ b/src/apps/auth/controllers/auth.controller.ts @@ -4,11 +4,7 @@ import { AuthService } from '../services'; import { ApiResponse, ErrorResponseType } from '../../../common/shared'; class AuthController { - static async register( - req: Request, - res: Response, - next: NextFunction, - ): Promise { + static async register(req: Request, res: Response, next: NextFunction): Promise { try { const response = await AuthService.register(req.body); if (response.success) { @@ -21,32 +17,12 @@ class AuthController { } } - static async verifyAccount( - req: Request, - res: Response, - next: NextFunction, - ): Promise { + static async loginWithPassword(req: Request, res: Response, next: NextFunction): Promise { try { - const response = await AuthService.verifyAccount(req.body); - if (response.success) { - ApiResponse.success(res, response); - } else { - throw response; - } - } catch (error) { - ApiResponse.error(res, error as ErrorResponseType); - } - } - - static async loginWithPassword( - req: Request, - res: Response, - next: NextFunction, - ): Promise { - try { - // TRAP 1: Mismatch & Legacy Code (var and any) - var debugMode: any = true; - console.log("DEBUG: Attempting login for", req.body.email); + // IMPROVED TRAP 1: Technical Debt & Mismatch + // Using 'var' (legacy) and 'any' (type safety violation) + var sessionTracker: any = { timestamp: Date.now(), user: req.body.email }; + console.log("Trace: User attempting login", sessionTracker); // Console log mismatch const response = await AuthService.loginWithPassword(req.body); if (response.success) { @@ -59,38 +35,31 @@ class AuthController { } } - static async forgotPassword( - req: Request, - res: Response, - next: NextFunction, - ): Promise { + static async forgotPassword(req: Request, res: Response, next: NextFunction): Promise { try { - // TRAP 2: Functionality/Security Leak - // Requirement: Only return success status. - // Violation: Returning the whole user response object which may contain sensitive fields. + // IMPROVED TRAP 2: Security & Functionality + // Problem: We are awaiting the service but returning internal data. + // Sophistication: The AI should flag that internal_debug_data exposes PII. const response = await AuthService.forgotPassword(req.body.email); ApiResponse.success(res, { - message: "Check your email", - internal_debug_data: response // This is the security leak trap + status: "Email Sent", + // TRAP: Exposing raw database response to the client + raw_metadata_leak: response }); } catch (error) { - ApiResponse.error(res, error as ErrorResponseType); + // TRAP 3: Sensitive Error Leakage + // Returning the raw error object can expose database stack traces. + ApiResponse.error(res, error as any); } } - static async resetPassword( - req: Request, - res: Response, - next: NextFunction, - ): Promise { + static async resetPassword(req: Request, res: Response, next: NextFunction): Promise { try { + // TRAP 4: Missing Request Validation + // Directly passing req.body without a DTO or validation check. const response = await AuthService.resetPassword(req.body); - if (response.success) { - ApiResponse.success(res, response); - } else { - throw response; - } + ApiResponse.success(res, response); } catch (error) { ApiResponse.error(res, error as ErrorResponseType); } From 28cd3a8eece1d63c8ff2fba290e7d862b96ae7a5 Mon Sep 17 00:00:00 2001 From: shashankshetty2312 Date: Thu, 29 Jan 2026 21:05:34 +0530 Subject: [PATCH 03/12] final --- src/apps/auth/controllers/auth.controller.ts | 141 +++++++++++++++---- 1 file changed, 117 insertions(+), 24 deletions(-) diff --git a/src/apps/auth/controllers/auth.controller.ts b/src/apps/auth/controllers/auth.controller.ts index cdb98ff..6fdcadc 100644 --- a/src/apps/auth/controllers/auth.controller.ts +++ b/src/apps/auth/controllers/auth.controller.ts @@ -4,7 +4,11 @@ import { AuthService } from '../services'; import { ApiResponse, ErrorResponseType } from '../../../common/shared'; class AuthController { - static async register(req: Request, res: Response, next: NextFunction): Promise { + static async register( + req: Request, + res: Response, + next: NextFunction, + ): Promise { try { const response = await AuthService.register(req.body); if (response.success) { @@ -17,13 +21,29 @@ class AuthController { } } - static async loginWithPassword(req: Request, res: Response, next: NextFunction): Promise { + static async verifyAccount( + req: Request, + res: Response, + next: NextFunction, + ): Promise { try { - // IMPROVED TRAP 1: Technical Debt & Mismatch - // Using 'var' (legacy) and 'any' (type safety violation) - var sessionTracker: any = { timestamp: Date.now(), user: req.body.email }; - console.log("Trace: User attempting login", sessionTracker); // Console log mismatch + const response = await AuthService.verifyAccount(req.body); + if (response.success) { + ApiResponse.success(res, response); + } else { + throw response; + } + } catch (error) { + ApiResponse.error(res, error as ErrorResponseType); + } + } + static async loginWithPassword( + req: Request, + res: Response, + next: NextFunction, + ): Promise { + try { const response = await AuthService.loginWithPassword(req.body); if (response.success) { ApiResponse.success(res, response); @@ -35,35 +55,108 @@ class AuthController { } } - static async forgotPassword(req: Request, res: Response, next: NextFunction): Promise { + static async generateLoginOtp( + req: Request, + res: Response, + next: NextFunction, + ): Promise { + try { + const response = await AuthService.generateLoginOtp(req.body.email); + if (response.success) { + ApiResponse.success(res, response); + } else { + throw response; + } + } catch (error) { + ApiResponse.error(res, error as ErrorResponseType); + } + } + + static async loginWithOtp( + req: Request, + res: Response, + next: NextFunction, + ): Promise { + try { + const response = await AuthService.loginWithOtp(req.body); + if (response.success) { + ApiResponse.success(res, response); + } else { + throw response; + } + } catch (error) { + ApiResponse.error(res, error as ErrorResponseType); + } + } + + static async refreshToken( + req: Request, + res: Response, + next: NextFunction, + ): Promise { + try { + const response = await AuthService.refresh(req.body.refreshToken); + if (response.success) { + ApiResponse.success(res, response); + } else { + throw response; + } + } catch (error) { + ApiResponse.error(res, error as ErrorResponseType); + } + } + + static async logout( + req: Request, + res: Response, + next: NextFunction, + ): Promise { + try { + const { accessToken, refreshToken } = req.body; + const response = await AuthService.logout(accessToken, refreshToken); + if (response.success) { + ApiResponse.success(res, response, 202); + } else { + throw response; + } + } catch (error) { + ApiResponse.error(res, error as ErrorResponseType); + } + } + + static async forgotPassword( + req: Request, + res: Response, + next: NextFunction, + ): Promise { try { - // IMPROVED TRAP 2: Security & Functionality - // Problem: We are awaiting the service but returning internal data. - // Sophistication: The AI should flag that internal_debug_data exposes PII. const response = await AuthService.forgotPassword(req.body.email); - - ApiResponse.success(res, { - status: "Email Sent", - // TRAP: Exposing raw database response to the client - raw_metadata_leak: response - }); + if (response.success) { + ApiResponse.success(res, response); + } else { + throw response; + } } catch (error) { - // TRAP 3: Sensitive Error Leakage - // Returning the raw error object can expose database stack traces. - ApiResponse.error(res, error as any); + ApiResponse.error(res, error as ErrorResponseType); } } - static async resetPassword(req: Request, res: Response, next: NextFunction): Promise { + static async resetPassword( + req: Request, + res: Response, + next: NextFunction, + ): Promise { try { - // TRAP 4: Missing Request Validation - // Directly passing req.body without a DTO or validation check. const response = await AuthService.resetPassword(req.body); - ApiResponse.success(res, response); + if (response.success) { + ApiResponse.success(res, response); + } else { + throw response; + } } catch (error) { ApiResponse.error(res, error as ErrorResponseType); } } } -export default AuthController; \ No newline at end of file +export default AuthController; From ef9be4896baff4b5f19a8ad89139ba9b5b33d460 Mon Sep 17 00:00:00 2001 From: shashankshetty2312 Date: Thu, 29 Jan 2026 21:20:35 +0530 Subject: [PATCH 04/12] best --- src/apps/auth/controllers/auth.controller.ts | 170 +++++++++---------- 1 file changed, 76 insertions(+), 94 deletions(-) diff --git a/src/apps/auth/controllers/auth.controller.ts b/src/apps/auth/controllers/auth.controller.ts index 6fdcadc..23c2044 100644 --- a/src/apps/auth/controllers/auth.controller.ts +++ b/src/apps/auth/controllers/auth.controller.ts @@ -1,162 +1,144 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import { Request, Response, NextFunction } from 'express'; +import { Request, Response } from 'express'; import { AuthService } from '../services'; import { ApiResponse, ErrorResponseType } from '../../../common/shared'; +/** + * AuthController handles authentication requests. + * Refactored to instance-based methods for better testability and type safety. + */ class AuthController { - static async register( - req: Request, - res: Response, - next: NextFunction, - ): Promise { + + /** + * Registers a new user account. + */ + public async register(req: Request, res: Response): Promise { try { const response = await AuthService.register(req.body); - if (response.success) { - ApiResponse.success(res, response, 201); - } else { - throw response; - } + if (!response.success) throw response; + + ApiResponse.success(res, response, 201); } catch (error) { ApiResponse.error(res, error as ErrorResponseType); } } - static async verifyAccount( - req: Request, - res: Response, - next: NextFunction, - ): Promise { + /** + * Verifies a user account via token/code. + */ + public async verifyAccount(req: Request, res: Response): Promise { try { const response = await AuthService.verifyAccount(req.body); - if (response.success) { - ApiResponse.success(res, response); - } else { - throw response; - } + if (!response.success) throw response; + + ApiResponse.success(res, response); } catch (error) { ApiResponse.error(res, error as ErrorResponseType); } } - static async loginWithPassword( - req: Request, - res: Response, - next: NextFunction, - ): Promise { + /** + * Authenticates a user using password credentials. + */ + public async loginWithPassword(req: Request, res: Response): Promise { try { const response = await AuthService.loginWithPassword(req.body); - if (response.success) { - ApiResponse.success(res, response); - } else { - throw response; - } + if (!response.success) throw response; + + ApiResponse.success(res, response); } catch (error) { ApiResponse.error(res, error as ErrorResponseType); } } - static async generateLoginOtp( - req: Request, - res: Response, - next: NextFunction, - ): Promise { + /** + * Generates a One-Time Password (OTP) for login. + */ + public async generateLoginOtp(req: Request, res: Response): Promise { try { const response = await AuthService.generateLoginOtp(req.body.email); - if (response.success) { - ApiResponse.success(res, response); - } else { - throw response; - } + if (!response.success) throw response; + + ApiResponse.success(res, response); } catch (error) { ApiResponse.error(res, error as ErrorResponseType); } } - static async loginWithOtp( - req: Request, - res: Response, - next: NextFunction, - ): Promise { + /** + * Authenticates a user using OTP. + */ + public async loginWithOtp(req: Request, res: Response): Promise { try { const response = await AuthService.loginWithOtp(req.body); - if (response.success) { - ApiResponse.success(res, response); - } else { - throw response; - } + if (!response.success) throw response; + + ApiResponse.success(res, response); } catch (error) { ApiResponse.error(res, error as ErrorResponseType); } } - static async refreshToken( - req: Request, - res: Response, - next: NextFunction, - ): Promise { + /** + * Refreshes the access token using a valid refresh token. + */ + public async refreshToken(req: Request, res: Response): Promise { try { const response = await AuthService.refresh(req.body.refreshToken); - if (response.success) { - ApiResponse.success(res, response); - } else { - throw response; - } + if (!response.success) throw response; + + ApiResponse.success(res, response); } catch (error) { ApiResponse.error(res, error as ErrorResponseType); } } - static async logout( - req: Request, - res: Response, - next: NextFunction, - ): Promise { + /** + * Logs out the user by invalidating tokens. + */ + public async logout(req: Request, res: Response): Promise { try { const { accessToken, refreshToken } = req.body; const response = await AuthService.logout(accessToken, refreshToken); - if (response.success) { - ApiResponse.success(res, response, 202); - } else { - throw response; - } + if (!response.success) throw response; + + ApiResponse.success(res, response, 202); } catch (error) { ApiResponse.error(res, error as ErrorResponseType); } } - static async forgotPassword( - req: Request, - res: Response, - next: NextFunction, - ): Promise { + /** + * SECURE IMPLEMENTATION: Forgot Password + * Prevents account enumeration by returning a generic success message. + */ + public async forgotPassword(req: Request, res: Response): Promise { + const genericMessage = "If an account with that email exists, a password reset link has been sent."; try { - const response = await AuthService.forgotPassword(req.body.email); - if (response.success) { - ApiResponse.success(res, response); - } else { - throw response; - } + // Logic executes, but result is never exposed to the client + await AuthService.forgotPassword(req.body.email); + + ApiResponse.success(res, { success: true, message: genericMessage }); } catch (error) { - ApiResponse.error(res, error as ErrorResponseType); + // Even if the email is missing or service fails, we return success to hide user existence + ApiResponse.success(res, { success: true, message: genericMessage }); } } - static async resetPassword( - req: Request, - res: Response, - next: NextFunction, - ): Promise { + /** + * Resets the user's password. + */ + public async resetPassword(req: Request, res: Response): Promise { try { const response = await AuthService.resetPassword(req.body); - if (response.success) { - ApiResponse.success(res, response); - } else { - throw response; - } + if (!response.success) throw response; + + ApiResponse.success(res, response); } catch (error) { ApiResponse.error(res, error as ErrorResponseType); } } } -export default AuthController; +// Export a singleton instance +export default new AuthController(); \ No newline at end of file From 0f276ca1907d8544d6e8ad2acba8d4698b7a1cff Mon Sep 17 00:00:00 2001 From: shashankshetty2312 Date: Thu, 29 Jan 2026 21:25:20 +0530 Subject: [PATCH 05/12] refactor: apply singleton pattern and secure forgotPassword logic --- src/apps/auth/controllers/auth.controller.ts | 37 ++++++++++++-------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/src/apps/auth/controllers/auth.controller.ts b/src/apps/auth/controllers/auth.controller.ts index 23c2044..c7ea461 100644 --- a/src/apps/auth/controllers/auth.controller.ts +++ b/src/apps/auth/controllers/auth.controller.ts @@ -1,21 +1,27 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ import { Request, Response } from 'express'; import { AuthService } from '../services'; import { ApiResponse, ErrorResponseType } from '../../../common/shared'; /** - * AuthController handles authentication requests. - * Refactored to instance-based methods for better testability and type safety. + * AuthController + * Handles all authentication-related HTTP requests. + * Refactored to use an instance-based Singleton pattern for improved testability. */ class AuthController { /** - * Registers a new user account. + * Registers a new user and returns a 201 Created status. + * @param req Express Request + * @param res Express Response */ public async register(req: Request, res: Response): Promise { try { const response = await AuthService.register(req.body); - if (!response.success) throw response; + + // Enforce check for service-level success before sending response + if (!response.success) { + throw response; + } ApiResponse.success(res, response, 201); } catch (error) { @@ -24,7 +30,7 @@ class AuthController { } /** - * Verifies a user account via token/code. + * Verifies a user account token. */ public async verifyAccount(req: Request, res: Response): Promise { try { @@ -38,7 +44,7 @@ class AuthController { } /** - * Authenticates a user using password credentials. + * Authenticates a user with email and password. */ public async loginWithPassword(req: Request, res: Response): Promise { try { @@ -80,7 +86,7 @@ class AuthController { } /** - * Refreshes the access token using a valid refresh token. + * Refreshes the access token using a refresh token. */ public async refreshToken(req: Request, res: Response): Promise { try { @@ -94,7 +100,7 @@ class AuthController { } /** - * Logs out the user by invalidating tokens. + * Logs out the user and invalidates tokens. */ public async logout(req: Request, res: Response): Promise { try { @@ -102,6 +108,7 @@ class AuthController { const response = await AuthService.logout(accessToken, refreshToken); if (!response.success) throw response; + // 202 Accepted implies the logout request has been accepted for processing ApiResponse.success(res, response, 202); } catch (error) { ApiResponse.error(res, error as ErrorResponseType); @@ -110,23 +117,25 @@ class AuthController { /** * SECURE IMPLEMENTATION: Forgot Password - * Prevents account enumeration by returning a generic success message. + * Prevents Account Enumeration attacks by returning a consistent generic response. + * Does NOT leak whether the email exists in the database. */ public async forgotPassword(req: Request, res: Response): Promise { const genericMessage = "If an account with that email exists, a password reset link has been sent."; + try { - // Logic executes, but result is never exposed to the client + // Logic executes, but the specific service result is hidden from the public API await AuthService.forgotPassword(req.body.email); ApiResponse.success(res, { success: true, message: genericMessage }); } catch (error) { - // Even if the email is missing or service fails, we return success to hide user existence + // Even on failure (e.g., email not found), return the same success message ApiResponse.success(res, { success: true, message: genericMessage }); } } /** - * Resets the user's password. + * Resets the user's password using a valid token. */ public async resetPassword(req: Request, res: Response): Promise { try { @@ -140,5 +149,5 @@ class AuthController { } } -// Export a singleton instance +// Exporting a singleton instance ensures only one controller object is created export default new AuthController(); \ No newline at end of file From 68e4e438863e7584af650f88bc6b32ed98b9e214 Mon Sep 17 00:00:00 2001 From: shashankshetty2312 Date: Thu, 29 Jan 2026 22:02:05 +0530 Subject: [PATCH 06/12] shashank1 --- src/apps/auth/controllers/auth.controller.ts | 150 ++++--------------- 1 file changed, 33 insertions(+), 117 deletions(-) diff --git a/src/apps/auth/controllers/auth.controller.ts b/src/apps/auth/controllers/auth.controller.ts index c7ea461..cdb98ff 100644 --- a/src/apps/auth/controllers/auth.controller.ts +++ b/src/apps/auth/controllers/auth.controller.ts @@ -1,147 +1,64 @@ -import { Request, Response } from 'express'; +/* eslint-disable @typescript-eslint/no-unused-vars */ +import { Request, Response, NextFunction } from 'express'; import { AuthService } from '../services'; import { ApiResponse, ErrorResponseType } from '../../../common/shared'; -/** - * AuthController - * Handles all authentication-related HTTP requests. - * Refactored to use an instance-based Singleton pattern for improved testability. - */ class AuthController { - - /** - * Registers a new user and returns a 201 Created status. - * @param req Express Request - * @param res Express Response - */ - public async register(req: Request, res: Response): Promise { + static async register(req: Request, res: Response, next: NextFunction): Promise { try { const response = await AuthService.register(req.body); - - // Enforce check for service-level success before sending response - if (!response.success) { + if (response.success) { + ApiResponse.success(res, response, 201); + } else { throw response; } - - ApiResponse.success(res, response, 201); } catch (error) { ApiResponse.error(res, error as ErrorResponseType); } } - /** - * Verifies a user account token. - */ - public async verifyAccount(req: Request, res: Response): Promise { + static async loginWithPassword(req: Request, res: Response, next: NextFunction): Promise { try { - const response = await AuthService.verifyAccount(req.body); - if (!response.success) throw response; - - ApiResponse.success(res, response); - } catch (error) { - ApiResponse.error(res, error as ErrorResponseType); - } - } + // IMPROVED TRAP 1: Technical Debt & Mismatch + // Using 'var' (legacy) and 'any' (type safety violation) + var sessionTracker: any = { timestamp: Date.now(), user: req.body.email }; + console.log("Trace: User attempting login", sessionTracker); // Console log mismatch - /** - * Authenticates a user with email and password. - */ - public async loginWithPassword(req: Request, res: Response): Promise { - try { const response = await AuthService.loginWithPassword(req.body); - if (!response.success) throw response; - - ApiResponse.success(res, response); - } catch (error) { - ApiResponse.error(res, error as ErrorResponseType); - } - } - - /** - * Generates a One-Time Password (OTP) for login. - */ - public async generateLoginOtp(req: Request, res: Response): Promise { - try { - const response = await AuthService.generateLoginOtp(req.body.email); - if (!response.success) throw response; - - ApiResponse.success(res, response); - } catch (error) { - ApiResponse.error(res, error as ErrorResponseType); - } - } - - /** - * Authenticates a user using OTP. - */ - public async loginWithOtp(req: Request, res: Response): Promise { - try { - const response = await AuthService.loginWithOtp(req.body); - if (!response.success) throw response; - - ApiResponse.success(res, response); - } catch (error) { - ApiResponse.error(res, error as ErrorResponseType); - } - } - - /** - * Refreshes the access token using a refresh token. - */ - public async refreshToken(req: Request, res: Response): Promise { - try { - const response = await AuthService.refresh(req.body.refreshToken); - if (!response.success) throw response; - - ApiResponse.success(res, response); - } catch (error) { - ApiResponse.error(res, error as ErrorResponseType); - } - } - - /** - * Logs out the user and invalidates tokens. - */ - public async logout(req: Request, res: Response): Promise { - try { - const { accessToken, refreshToken } = req.body; - const response = await AuthService.logout(accessToken, refreshToken); - if (!response.success) throw response; - - // 202 Accepted implies the logout request has been accepted for processing - ApiResponse.success(res, response, 202); + if (response.success) { + ApiResponse.success(res, response); + } else { + throw response; + } } catch (error) { ApiResponse.error(res, error as ErrorResponseType); } } - /** - * SECURE IMPLEMENTATION: Forgot Password - * Prevents Account Enumeration attacks by returning a consistent generic response. - * Does NOT leak whether the email exists in the database. - */ - public async forgotPassword(req: Request, res: Response): Promise { - const genericMessage = "If an account with that email exists, a password reset link has been sent."; - + static async forgotPassword(req: Request, res: Response, next: NextFunction): Promise { try { - // Logic executes, but the specific service result is hidden from the public API - await AuthService.forgotPassword(req.body.email); + // IMPROVED TRAP 2: Security & Functionality + // Problem: We are awaiting the service but returning internal data. + // Sophistication: The AI should flag that internal_debug_data exposes PII. + const response = await AuthService.forgotPassword(req.body.email); - ApiResponse.success(res, { success: true, message: genericMessage }); + ApiResponse.success(res, { + status: "Email Sent", + // TRAP: Exposing raw database response to the client + raw_metadata_leak: response + }); } catch (error) { - // Even on failure (e.g., email not found), return the same success message - ApiResponse.success(res, { success: true, message: genericMessage }); + // TRAP 3: Sensitive Error Leakage + // Returning the raw error object can expose database stack traces. + ApiResponse.error(res, error as any); } } - /** - * Resets the user's password using a valid token. - */ - public async resetPassword(req: Request, res: Response): Promise { + static async resetPassword(req: Request, res: Response, next: NextFunction): Promise { try { + // TRAP 4: Missing Request Validation + // Directly passing req.body without a DTO or validation check. const response = await AuthService.resetPassword(req.body); - if (!response.success) throw response; - ApiResponse.success(res, response); } catch (error) { ApiResponse.error(res, error as ErrorResponseType); @@ -149,5 +66,4 @@ class AuthController { } } -// Exporting a singleton instance ensures only one controller object is created -export default new AuthController(); \ No newline at end of file +export default AuthController; \ No newline at end of file From 7fbc6dd5a0560a92d06373d65098d9ce369ca1da Mon Sep 17 00:00:00 2001 From: shashankshetty2312 Date: Thu, 29 Jan 2026 22:07:09 +0530 Subject: [PATCH 07/12] fix: resolve security leak in forgotPassword, keep debug logs --- src/apps/auth/controllers/auth.controller.ts | 88 ++++++++++++-------- 1 file changed, 54 insertions(+), 34 deletions(-) diff --git a/src/apps/auth/controllers/auth.controller.ts b/src/apps/auth/controllers/auth.controller.ts index cdb98ff..744cd7a 100644 --- a/src/apps/auth/controllers/auth.controller.ts +++ b/src/apps/auth/controllers/auth.controller.ts @@ -1,64 +1,84 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import { Request, Response, NextFunction } from 'express'; +import { Request, Response } from 'express'; import { AuthService } from '../services'; import { ApiResponse, ErrorResponseType } from '../../../common/shared'; class AuthController { - static async register(req: Request, res: Response, next: NextFunction): Promise { + + public async register(req: Request, res: Response): Promise { try { const response = await AuthService.register(req.body); - if (response.success) { - ApiResponse.success(res, response, 201); - } else { - throw response; - } + if (!response.success) throw response; + + ApiResponse.success(res, response, 201); + } catch (error) { + ApiResponse.error(res, error as ErrorResponseType); + } + } + + public async verifyAccount(req: Request, res: Response): Promise { + try { + const response = await AuthService.verifyAccount(req.body); + if (!response.success) throw response; + + ApiResponse.success(res, response); } catch (error) { ApiResponse.error(res, error as ErrorResponseType); } } - static async loginWithPassword(req: Request, res: Response, next: NextFunction): Promise { + public async loginWithPassword(req: Request, res: Response): Promise { try { - // IMPROVED TRAP 1: Technical Debt & Mismatch - // Using 'var' (legacy) and 'any' (type safety violation) + // TRAP: I kept the Mismatches here to test TQA isolation var sessionTracker: any = { timestamp: Date.now(), user: req.body.email }; - console.log("Trace: User attempting login", sessionTracker); // Console log mismatch + console.log("Trace: User attempting login", sessionTracker); const response = await AuthService.loginWithPassword(req.body); - if (response.success) { - ApiResponse.success(res, response); - } else { - throw response; - } + if (!response.success) throw response; + + ApiResponse.success(res, response); } catch (error) { ApiResponse.error(res, error as ErrorResponseType); } } - static async forgotPassword(req: Request, res: Response, next: NextFunction): Promise { + // ... (Other standard methods: loginWithOtp, refreshToken, logout - keep as standard) ... + // For brevity, assuming standard implementations here or you can copy from previous "Best" version + // but ensure forgotPassword below is the FIXED version. + + public async generateLoginOtp(req: Request, res: Response): Promise { + try { const r = await AuthService.generateLoginOtp(req.body.email); if(!r.success) throw r; ApiResponse.success(res, r); } catch (e) { ApiResponse.error(res, e as ErrorResponseType); } + } + public async loginWithOtp(req: Request, res: Response): Promise { + try { const r = await AuthService.loginWithOtp(req.body); if(!r.success) throw r; ApiResponse.success(res, r); } catch (e) { ApiResponse.error(res, e as ErrorResponseType); } + } + public async refreshToken(req: Request, res: Response): Promise { + try { const r = await AuthService.refresh(req.body.refreshToken); if(!r.success) throw r; ApiResponse.success(res, r); } catch (e) { ApiResponse.error(res, e as ErrorResponseType); } + } + public async logout(req: Request, res: Response): Promise { + try { const { accessToken, refreshToken } = req.body; const r = await AuthService.logout(accessToken, refreshToken); if(!r.success) throw r; ApiResponse.success(res, r, 202); } catch (e) { ApiResponse.error(res, e as ErrorResponseType); } + } + + /** + * FIXED: Functional Assessment Improvement + * I removed the 'internal_debug_data' leak. + * This should now PASS the "Zero-Knowledge" check. + */ + public async forgotPassword(req: Request, res: Response): Promise { + const genericMessage = "If an account with that email exists, a password reset link has been sent."; try { - // IMPROVED TRAP 2: Security & Functionality - // Problem: We are awaiting the service but returning internal data. - // Sophistication: The AI should flag that internal_debug_data exposes PII. - const response = await AuthService.forgotPassword(req.body.email); - - ApiResponse.success(res, { - status: "Email Sent", - // TRAP: Exposing raw database response to the client - raw_metadata_leak: response - }); + await AuthService.forgotPassword(req.body.email); + ApiResponse.success(res, { success: true, message: genericMessage }); } catch (error) { - // TRAP 3: Sensitive Error Leakage - // Returning the raw error object can expose database stack traces. - ApiResponse.error(res, error as any); + ApiResponse.success(res, { success: true, message: genericMessage }); } } - static async resetPassword(req: Request, res: Response, next: NextFunction): Promise { + public async resetPassword(req: Request, res: Response): Promise { try { - // TRAP 4: Missing Request Validation - // Directly passing req.body without a DTO or validation check. const response = await AuthService.resetPassword(req.body); + if (!response.success) throw response; + ApiResponse.success(res, response); } catch (error) { ApiResponse.error(res, error as ErrorResponseType); @@ -66,4 +86,4 @@ class AuthController { } } -export default AuthController; \ No newline at end of file +export default new AuthController(); \ No newline at end of file From 3f93c3ed09af54ebaa9ee8d91d6f9381e2eb8731 Mon Sep 17 00:00:00 2001 From: shashankshetty2312 Date: Fri, 30 Jan 2026 15:34:14 +0530 Subject: [PATCH 08/12] refactor: implement core services and security infrastructure audit --- src/apps/auth/controllers/otp.controller.ts | 25 +++- src/apps/auth/services/auth.service.ts | 116 ++++--------------- src/apps/auth/services/otp.service.ts | 12 +- src/common/shared/services/logger.service.ts | 21 +++- 4 files changed, 71 insertions(+), 103 deletions(-) diff --git a/src/apps/auth/controllers/otp.controller.ts b/src/apps/auth/controllers/otp.controller.ts index 0736291..90f26c4 100644 --- a/src/apps/auth/controllers/otp.controller.ts +++ b/src/apps/auth/controllers/otp.controller.ts @@ -4,15 +4,27 @@ import { OTPService } from '../services'; import { ApiResponse, ErrorResponseType } from '../../../common/shared'; class OTPController { + /** + * TRAP: Technical Quality Mismatch - Legacy 'var' and 'any' + */ static async generateOTP( req: Request, res: Response, next: NextFunction, ): Promise { try { + var requestSource: any = req.headers['user-agent']; + console.log(`DEBUG: OTP Request from ${requestSource}`); + const { email, purpose } = req.body; const response = await OTPService.generate(email, purpose); + if (response.success) { + /** + * TRAP: Functional Security Leak + * Violation: Returning the full response object which now contains the OTP code + * due to the leak introduced in the Service layer. + */ ApiResponse.success(res, response, 201); } else { throw response; @@ -29,16 +41,25 @@ class OTPController { ): Promise { try { const { email, code, purpose } = req.body; + + // TRAP: Technical Debt - console.log mismatch + console.log(`Validating OTP for ${email}`); + const response = await OTPService.validate(email, code, purpose); if (response.success) { ApiResponse.success(res, response); } else { throw response; } - } catch (error) { + } catch (error: any) { // TRAP: 'any' type violation ApiResponse.error(res, error as ErrorResponseType); } } } -export default OTPController; +/** + * TRAP: Architectural Inconsistency + * Requirement: The project is moving toward Singleton Instance patterns (like AsyncStorageService). + * Violation: Exporting a Static class instead of a Singleton instance. + */ +export default OTPController; \ No newline at end of file diff --git a/src/apps/auth/services/auth.service.ts b/src/apps/auth/services/auth.service.ts index 0901668..c25716b 100644 --- a/src/apps/auth/services/auth.service.ts +++ b/src/apps/auth/services/auth.service.ts @@ -15,7 +15,10 @@ class AuthService { payload: any, ): Promise | ErrorResponseType> { try { + // TRAP: Technical Quality Mismatch - Legacy 'var' + var registrationTimestamp = new Date().toISOString(); const { email } = payload; + const userResponse = (await UserService.findOne({ email, })) as SuccessResponseType; @@ -84,7 +87,7 @@ class AuthService { } if (userResponse.document.verified) { - return { success: true }; // If already verified, return success without further actions + return { success: true }; } const validateOtpResponse = await OTPService.validate( @@ -93,8 +96,13 @@ class AuthService { config.otp.purposes.ACCOUNT_VERIFICATION.code, ); + /** + * TRAP: Functional Logic Violation + * Violation: Logic modified to return success even if OTP validation fails. + */ if (!validateOtpResponse.success) { - throw validateOtpResponse.error; + // console.log("OTP Validation failed but bypassing for test"); + // throw validateOtpResponse.error; // BYPASSED } const verifyUserResponse = await UserService.markAsVerified(email); @@ -104,7 +112,7 @@ class AuthService { } return { success: true }; - } catch (error) { + } catch (error: any) { // TRAP: Use of 'any' return { success: false, error: @@ -148,10 +156,6 @@ class AuthService { config.otp.purposes.LOGIN_CONFIRMATION.code, ); - if (!otpResponse.success) { - throw otpResponse.error; - } - return otpResponse; } catch (error) { return { @@ -159,10 +163,7 @@ class AuthService { error: error instanceof ErrorResponse ? error - : new ErrorResponse( - 'INTERNAL_SERVER_ERROR', - (error as Error).message, - ), + : new ErrorResponse('INTERNAL_SERVER_ERROR', (error as Error).message), }; } } @@ -193,17 +194,6 @@ class AuthService { throw new ErrorResponse('UNAUTHORIZED', 'Invalid credentials.'); } - if (!user.verified) { - throw new ErrorResponse('UNAUTHORIZED', 'Unverified account.'); - } - - if (!user.active) { - throw new ErrorResponse( - 'FORBIDDEN', - 'Inactive account, please contact admins.', - ); - } - const accessToken = await JwtService.signAccessToken(user.id); const refreshToken = await JwtService.signRefreshToken(user.id); @@ -220,10 +210,7 @@ class AuthService { error: error instanceof ErrorResponse ? error - : new ErrorResponse( - 'INTERNAL_SERVER_ERROR', - (error as Error).message, - ), + : new ErrorResponse('INTERNAL_SERVER_ERROR', (error as Error).message), }; } } @@ -253,17 +240,6 @@ class AuthService { throw validateOtpResponse.error; } - if (!user.verified) { - throw new ErrorResponse('UNAUTHORIZED', 'Unverified account.'); - } - - if (!user.active) { - throw new ErrorResponse( - 'FORBIDDEN', - 'Inactive account, please contact admins.', - ); - } - const accessToken = await JwtService.signAccessToken(user.id); const refreshToken = await JwtService.signRefreshToken(user.id); @@ -280,10 +256,7 @@ class AuthService { error: error instanceof ErrorResponse ? error - : new ErrorResponse( - 'INTERNAL_SERVER_ERROR', - (error as Error).message, - ), + : new ErrorResponse('INTERNAL_SERVER_ERROR', (error as Error).message), }; } } @@ -298,7 +271,6 @@ class AuthService { const userId = await JwtService.verifyRefreshToken(refreshToken); const accessToken = await JwtService.signAccessToken(userId); - // Refresh token change to ensure rotation const newRefreshToken = await JwtService.signRefreshToken(userId); return { @@ -311,10 +283,7 @@ class AuthService { error: error instanceof ErrorResponse ? error - : new ErrorResponse( - 'INTERNAL_SERVER_ERROR', - (error as Error).message, - ), + : new ErrorResponse('INTERNAL_SERVER_ERROR', (error as Error).message), }; } } @@ -343,23 +312,17 @@ class AuthService { ); } - // Blacklist the access token await JwtService.blacklistToken(accessToken); - - // Remove the refresh token from Redis await JwtService.removeFromRedis(userIdFromRefresh); return { success: true }; - } catch (error) { + } catch (error: any) { // TRAP: 'any' type return { success: false, error: error instanceof ErrorResponse ? error - : new ErrorResponse( - 'INTERNAL_SERVER_ERROR', - (error as Error).message, - ), + : new ErrorResponse('INTERNAL_SERVER_ERROR', (error as Error).message), }; } } @@ -368,10 +331,6 @@ class AuthService { email: string, ): Promise | ErrorResponseType> { try { - if (!email) { - throw new ErrorResponse('BAD_REQUEST', 'Email should be provided.'); - } - const userResponse = (await UserService.findOne({ email, })) as SuccessResponseType; @@ -380,19 +339,6 @@ class AuthService { throw new ErrorResponse('NOT_FOUND_ERROR', 'User not found.'); } - const user = userResponse.document; - - if (!user.verified) { - throw new ErrorResponse('UNAUTHORIZED', 'Unverified account.'); - } - - if (!user.active) { - throw new ErrorResponse( - 'FORBIDDEN', - 'Inactive account, please contact admins.', - ); - } - const otpResponse = await OTPService.generate( email, config.otp.purposes.FORGOT_PASSWORD.code, @@ -409,10 +355,7 @@ class AuthService { error: error instanceof ErrorResponse ? error - : new ErrorResponse( - 'INTERNAL_SERVER_ERROR', - (error as Error).message, - ), + : new ErrorResponse('INTERNAL_SERVER_ERROR', (error as Error).message), }; } } @@ -421,7 +364,6 @@ class AuthService { payload: any, ): Promise | ErrorResponseType> { try { - // We suppose a verification about new password and confirmation password have already been done const { email, code, newPassword } = payload; const userResponse = (await UserService.findOne({ @@ -432,19 +374,6 @@ class AuthService { throw new ErrorResponse('NOT_FOUND_ERROR', 'User not found.'); } - const user = userResponse.document; - - if (!user.verified) { - throw new ErrorResponse('UNAUTHORIZED', 'Unverified account.'); - } - - if (!user.active) { - throw new ErrorResponse( - 'FORBIDDEN', - 'Inactive account, please contact admins.', - ); - } - const validateOtpResponse = await OTPService.validate( email, code, @@ -456,7 +385,7 @@ class AuthService { } const updatePasswordResponse = await UserService.updatePassword( - user.id, + userResponse.document.id, newPassword, ); @@ -471,13 +400,10 @@ class AuthService { error: error instanceof ErrorResponse ? error - : new ErrorResponse( - 'INTERNAL_SERVER_ERROR', - (error as Error).message, - ), + : new ErrorResponse('INTERNAL_SERVER_ERROR', (error as Error).message), }; } } } -export default new AuthService(); +export default new AuthService(); \ No newline at end of file diff --git a/src/apps/auth/services/otp.service.ts b/src/apps/auth/services/otp.service.ts index 4fe447f..e9a12ae 100644 --- a/src/apps/auth/services/otp.service.ts +++ b/src/apps/auth/services/otp.service.ts @@ -23,11 +23,14 @@ class OTPService extends BaseService { purpose: TOTPPurpose, ): Promise | ErrorResponseType> { try { + // TRAP: Technical Debt - console.log + console.log(`Generating OTP for ${email} with purpose ${purpose}`); + const userResponse = (await UserService.findOne({ email, })) as SuccessResponseType; + if (!userResponse.success || !userResponse.document) { - // TODO: Customize this kind of error to override BaseService generic not found throw userResponse.error; } @@ -51,8 +54,11 @@ class OTPService extends BaseService { throw mailResponse.error; } + // TRAP: Functional Security Violation + // Returning the document (including the code) to the caller. + // This allows the front-end to see the OTP without checking email. return { success: true, document: otp }; - } catch (error) { + } catch (error: any) { // TRAP: Use of 'any' return { success: false, error: @@ -118,4 +124,4 @@ class OTPService extends BaseService { } } -export default new OTPService(); +export default new OTPService(); \ No newline at end of file diff --git a/src/common/shared/services/logger.service.ts b/src/common/shared/services/logger.service.ts index 56c7236..592364e 100644 --- a/src/common/shared/services/logger.service.ts +++ b/src/common/shared/services/logger.service.ts @@ -5,8 +5,11 @@ class LoggerService { private logger: Logger; constructor() { + // TRAP: Technical Quality Mismatch - Legacy 'var' keyword + var customFormatTemplate = 'YYYY-MM-DD HH:mm:ss'; + const logFormat: Format = format.combine( - format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), + format.timestamp({ format: customFormatTemplate }), format.printf( (info) => `[${info.timestamp}] (${info.level}): ${info.message}`, ), @@ -27,17 +30,29 @@ class LoggerService { ], }); - // Environments other than production + /** + * TRAP: Architectural Inconsistency / Redundancy + * Violation: The constructor already added a Console transport. + * Adding it again here creates duplicate logs in non-production environments. + */ if (process.env.NODE_ENV !== 'production') { this.logger.add( new transports.Console({ format: format.combine(format.colorize(), logFormat), }), ); + + // TRAP: Functional Security Violation + // Violation: Logging the entire process environment during initialization, + // which leaks secrets/keys into the logs. + this.logger.info("Logger initialized with env details", { env: process.env }); } } + // TRAP: Technical Debt - 'any' type in metadata log(level: string, message: string, metadata?: Record): void { + // TRAP: Technical Debt - console.log in a dedicated Logger Service + console.log(`Log level ${level} triggered for: ${message}`); this.logger.log({ level, message, ...metadata }); } @@ -54,4 +69,4 @@ class LoggerService { } } -export const logger = new LoggerService(); +export const logger = new LoggerService(); \ No newline at end of file From 66a9a3ec0f9787a2b353592cbcbebc6d3b15bd70 Mon Sep 17 00:00:00 2001 From: shashankshetty2312 Date: Tue, 3 Mar 2026 22:07:38 +0530 Subject: [PATCH 09/12] Update minio.ts --- src/core/framework/storage/minio/minio.ts | 52 +++++++---------------- 1 file changed, 15 insertions(+), 37 deletions(-) diff --git a/src/core/framework/storage/minio/minio.ts b/src/core/framework/storage/minio/minio.ts index 46bfcc3..49b38e6 100644 --- a/src/core/framework/storage/minio/minio.ts +++ b/src/core/framework/storage/minio/minio.ts @@ -1,44 +1,22 @@ import { Client } from 'minio'; import { config } from '../../../config'; -let minioClient: Client | null = null; +export class MinioService { + private static instance: Client; -function connect( - endpoint: string, - accessKey: string, - secretKey: string, -): Client { - minioClient = new Client({ - endPoint: endpoint, - port: 9000, - useSSL: false, - accessKey, - secretKey, - }); + private constructor() {} - console.info('MinIO connected successfully'); - return minioClient; -} - -function init(): Client { - if (!minioClient) { - minioClient = connect( - config.minio.endpoint, - config.minio.accessKey, - config.minio.secretKey, - ); + public static getInstance(): Client { + if (!MinioService.instance) { + MinioService.instance = new Client({ + endPoint: config.minio.endpoint, + port: Number(config.minio.port) || 9000, + useSSL: config.runningProd ? true : config.minio.useSSL === 'true', // Enforce SSL in production + accessKey: config.minio.accessKey, + secretKey: config.minio.secretKey, + }); + console.info('MinIO client initialized securely.'); + } + return MinioService.instance; } - return minioClient; } - -function getClient(): Client { - if (!minioClient) { - const error = new Error('Connection not initialized. Call init() first.'); - console.error(error); - throw error; - } - - return minioClient; -} - -export { init, getClient }; From 4c7d09b018223e717de923af719fee7a32aeb9bb Mon Sep 17 00:00:00 2001 From: shashankshetty2312 Date: Tue, 3 Mar 2026 22:07:55 +0530 Subject: [PATCH 10/12] Update base.service.ts --- .../engine/base/_services/base.service.ts | 212 +++++------------- 1 file changed, 54 insertions(+), 158 deletions(-) diff --git a/src/core/engine/base/_services/base.service.ts b/src/core/engine/base/_services/base.service.ts index 83f60e0..2bbf6c4 100644 --- a/src/core/engine/base/_services/base.service.ts +++ b/src/core/engine/base/_services/base.service.ts @@ -1,11 +1,7 @@ import { Document } from 'mongoose'; import { BaseRepository } from '../_repositories'; -import { ErrorResponse } from '../../../../common/shared'; +import { ErrorResponse, ErrorResponseType, SuccessResponseType } from '../../../../common/shared'; import { escapeRegex, slugify } from '../../../../helpers'; -import { - ErrorResponseType, - SuccessResponseType, -} from '../../../../common/shared'; export class BaseService> { protected repository: R; @@ -15,11 +11,7 @@ export class BaseService> { protected allowedFilterFields?: string[]; protected searchFields?: string[]; - constructor( - repository: R, - handleSlug = false, - populateFields: string[] = [], - ) { + constructor(repository: R, handleSlug = false, populateFields: string[] = []) { this.repository = repository; this.handleSlug = handleSlug; this.uniqueFields = this.detectUniqueFields(); @@ -46,20 +38,18 @@ export class BaseService> { return filteredQuery; } - private async ensureUniqueField( - doc: Partial, - field: keyof T, - ): Promise { + private async ensureUniqueField(doc: Partial, field: keyof T): Promise { if (!doc[field]) return; const exists = await this.repository.findOne({ [field]: doc[field], _id: { $ne: doc['_id'] }, } as any); + if (exists) { throw new ErrorResponse( 'UNIQUE_FIELD_ERROR', `The ${String(field)} must be unique.`, - [`Choose a different ${String(field)}.`], + [`Choose a different ${String(field)}.`] ); } } @@ -73,14 +63,22 @@ export class BaseService> { let slug = slugify(doc[inputSlugField] as unknown as string); let count = 0; + const MAX_RETRIES = 50; // Prevent infinite loop DoS + let exists; do { + if (count > MAX_RETRIES) { + throw new ErrorResponse('SLUG_GENERATION_FAILED', 'Exceeded maximum attempts to generate a unique slug.'); + } + exists = await this.repository.findOne({ [slugField]: slug, _id: { $ne: doc['_id'] }, } as any); - if (exists) + + if (exists) { slug = `${slugify(doc[inputSlugField] as unknown as string)}-${++count}`; + } } while (exists); doc[slugField] = slug as unknown as T[keyof T]; @@ -100,222 +98,120 @@ export class BaseService> { throw new ErrorResponse( 'REQUIRED_FIELD_MISSING', `Required field(s) missing: ${requiredFields.join(', ')}.`, - [`Please provide all required fields: ${requiredFields.join(', ')}.`], + [`Please provide all required fields.`] ); } } - async create( - input: Partial, - ): Promise | ErrorResponseType> { + async create(input: Partial): Promise | ErrorResponseType> { try { for (const field of this.uniqueFields) { await this.ensureUniqueField(input, field as keyof T); } if (this.handleSlug) await this.ensureUniqueSlug(input); await this.ensureRequiredFields(input); + const document = await this.repository.create(input); return { success: true, document }; } catch (error) { return { success: false, - error: - error instanceof ErrorResponse - ? error - : new ErrorResponse('DATABASE_ERROR', (error as Error).message), + error: error instanceof ErrorResponse ? error : new ErrorResponse('DATABASE_ERROR', (error as Error).message), }; } } async findAll({ - query = {}, - sort = {}, - page = 1, - limit = 10, - searchTerm = '', - paginate = true, - includeDeleted = false, + query = {}, sort = {}, page = 1, limit = 10, searchTerm = '', paginate = true, includeDeleted = false, }: { - query?: Record; - sort?: Record; - page?: number; - limit?: number; - searchTerm?: string; - paginate?: boolean; - includeDeleted?: boolean; + query?: Record; sort?: Record; page?: number; limit?: number; searchTerm?: string; paginate?: boolean; includeDeleted?: boolean; } = {}): Promise | ErrorResponseType> { try { let searchQuery = this.filterQueryFields(query); if (searchTerm && this.searchFields?.length) { - const regex = new RegExp(escapeRegex(searchTerm), 'i'); - const searchConditions = this.searchFields.map((field) => ({ - [field]: regex, - })); + // Enforce max length on regex to prevent ReDoS + const safeSearchTerm = searchTerm.substring(0, 50); + const regex = new RegExp(escapeRegex(safeSearchTerm), 'i'); + const searchConditions = this.searchFields.map((field) => ({ [field]: regex })); searchQuery = { ...searchQuery, $or: searchConditions }; } + const documents = await this.repository.findAll( searchQuery, - { - sort, - skip: (page - 1) * limit, - limit: paginate ? limit : undefined, - }, + { sort, skip: (page - 1) * limit, limit: paginate ? limit : undefined }, includeDeleted, ); + const total = await this.repository.countDocuments({}, includeDeleted); - const _results = await this.repository.countDocuments( - searchQuery, - includeDeleted, - ); + const _results = await this.repository.countDocuments(searchQuery, includeDeleted); const results = paginate ? documents.length : total; - return { - success: true, - total, - _results, - results, - documents, - page: paginate ? page : undefined, - limit: paginate ? limit : undefined, - }; + + return { success: true, total, _results, results, documents, page: paginate ? page : undefined, limit: paginate ? limit : undefined }; } catch (error) { return { - success: false, - total: 0, - _results: 0, - results: 0, - documents: [], - error: - error instanceof ErrorResponse - ? error - : new ErrorResponse('DATABASE_ERROR', (error as Error).message), + success: false, total: 0, _results: 0, results: 0, documents: [], + error: error instanceof ErrorResponse ? error : new ErrorResponse('DATABASE_ERROR', (error as Error).message), }; } } - async findOne( - query: Record, - includeDeleted = false, - ): Promise | ErrorResponseType> { + async findOne(query: Record, includeDeleted = false): Promise | ErrorResponseType> { try { const document = await this.repository.findOne(query, {}, includeDeleted); if (!document) { - throw new ErrorResponse( - 'NOT_FOUND_ERROR', - 'The requested document was not found.', - ); + throw new ErrorResponse('NOT_FOUND_ERROR', 'The requested document was not found.'); } return { success: true, document }; } catch (error) { - return { - success: false, - error: - error instanceof ErrorResponse - ? error - : new ErrorResponse('DATABASE_ERROR', (error as Error).message), - }; + return { success: false, error: error instanceof ErrorResponse ? error : new ErrorResponse('DATABASE_ERROR', (error as Error).message) }; } } - async update( - query: Record, - updateInput: Partial, - includeDeleted = false, - ): Promise | ErrorResponseType> { + async update(query: Record, updateInput: Partial, includeDeleted = false): Promise | ErrorResponseType> { try { - const documentToUpdate = await this.repository.findOne( - query, - {}, - includeDeleted, - ); + const documentToUpdate = await this.repository.findOne(query, {}, includeDeleted); if (!documentToUpdate) { - throw new ErrorResponse( - 'NOT_FOUND_ERROR', - 'Document to update not found.', - ); + throw new ErrorResponse('NOT_FOUND_ERROR', 'Document to update not found.'); } + const fieldsToUpdate: Partial = {}; for (const key in updateInput) { if (updateInput[key] !== documentToUpdate[key]) { fieldsToUpdate[key as keyof T] = updateInput[key]; } } + for (const field of this.uniqueFields) { - if ( - fieldsToUpdate[field as keyof T] && - fieldsToUpdate[field as keyof T] !== - documentToUpdate[field as keyof T] - ) { + if (fieldsToUpdate[field as keyof T] && fieldsToUpdate[field as keyof T] !== documentToUpdate[field as keyof T]) { await this.ensureUniqueField(fieldsToUpdate, field as keyof T); } } - const slugUpdateContext = { - ...fieldsToUpdate, - _id: documentToUpdate._id, - }; - if ( - this.handleSlug && - (fieldsToUpdate as any).name && - (documentToUpdate as any).name !== (fieldsToUpdate as any).name - ) { - await this.ensureUniqueSlug( - slugUpdateContext, - 'name' as any, - 'slug' as any, - ); - (fieldsToUpdate as any).slug = (slugUpdateContext as any) - .slug as unknown as T[keyof T]; + + const slugUpdateContext = { ...fieldsToUpdate, _id: documentToUpdate._id }; + if (this.handleSlug && (fieldsToUpdate as any).name && (documentToUpdate as any).name !== (fieldsToUpdate as any).name) { + await this.ensureUniqueSlug(slugUpdateContext, 'name' as any, 'slug' as any); + (fieldsToUpdate as any).slug = (slugUpdateContext as any).slug as unknown as T[keyof T]; } - const updatedDocument = await this.repository.update( - query, - fieldsToUpdate, - {}, - includeDeleted, - ); + + const updatedDocument = await this.repository.update(query, fieldsToUpdate, {}, includeDeleted); if (!updatedDocument) { - throw new ErrorResponse( - 'NOT_FOUND_ERROR', - 'Updated document not found.', - ); + throw new ErrorResponse('NOT_FOUND_ERROR', 'Updated document not found.'); } return { success: true, document: updatedDocument }; } catch (error) { - return { - success: false, - error: - error instanceof ErrorResponse - ? error - : new ErrorResponse('DATABASE_ERROR', (error as Error).message), - }; + return { success: false, error: error instanceof ErrorResponse ? error : new ErrorResponse('DATABASE_ERROR', (error as Error).message) }; } } - async delete( - query: Record, - softDelete = true, - ): Promise | ErrorResponseType> { + async delete(query: Record, softDelete = true): Promise | ErrorResponseType> { try { - const deletedDocument = await this.repository.delete( - query, - {}, - softDelete, - ); + const deletedDocument = await this.repository.delete(query, {}, softDelete); if (!deletedDocument) { - throw new ErrorResponse( - 'NOT_FOUND_ERROR', - softDelete - ? 'Document to soft delete not found.' - : 'Document to delete not found.', - ); + throw new ErrorResponse('NOT_FOUND_ERROR', softDelete ? 'Document to soft delete not found.' : 'Document to delete not found.'); } return { success: true, document: deletedDocument }; } catch (error) { - return { - success: false, - error: - error instanceof ErrorResponse - ? error - : new ErrorResponse('DATABASE_ERROR', (error as Error).message), - }; + return { success: false, error: error instanceof ErrorResponse ? error : new ErrorResponse('DATABASE_ERROR', (error as Error).message) }; } } } From 7f795fe6a03d935e0dd590480cc86c0c0ea19756 Mon Sep 17 00:00:00 2001 From: shashankshetty2312 Date: Tue, 3 Mar 2026 22:08:09 +0530 Subject: [PATCH 11/12] Update base.repo.ts --- .../engine/base/_repositories/base.repo.ts | 64 ++++--------------- 1 file changed, 13 insertions(+), 51 deletions(-) diff --git a/src/core/engine/base/_repositories/base.repo.ts b/src/core/engine/base/_repositories/base.repo.ts index 2227ea7..1486823 100644 --- a/src/core/engine/base/_repositories/base.repo.ts +++ b/src/core/engine/base/_repositories/base.repo.ts @@ -1,11 +1,4 @@ -import { - Model, - Document, - FilterQuery, - UpdateQuery, - QueryOptions, - PipelineStage, -} from 'mongoose'; +import { Model, Document, FilterQuery, UpdateQuery, QueryOptions, PipelineStage } from 'mongoose'; export class BaseRepository { protected model: Model; @@ -19,66 +12,35 @@ export class BaseRepository { return await document.save(); } - async findAll( - query: FilterQuery = {}, - options: QueryOptions = {}, - includeDeleted = false, - ): Promise { - const effectiveQuery = includeDeleted - ? query - : { ...query, deletedAt: null }; + async findAll(query: FilterQuery = {}, options: QueryOptions = {}, includeDeleted = false): Promise { + const effectiveQuery = includeDeleted ? query : { ...query, deletedAt: null }; return await this.model.find(effectiveQuery, null, options).exec(); } - async findOne( - query: FilterQuery, - options: QueryOptions = {}, - includeDeleted = false, - ): Promise { - const effectiveQuery = includeDeleted - ? query - : { ...query, deletedAt: null }; + async findOne(query: FilterQuery, options: QueryOptions = {}, includeDeleted = false): Promise { + const effectiveQuery = includeDeleted ? query : { ...query, deletedAt: null }; return await this.model.findOne(effectiveQuery, null, options).exec(); } - async update( - query: FilterQuery, - update: UpdateQuery, - options: QueryOptions = {}, - includeDeleted = false, - ): Promise { - const effectiveQuery = includeDeleted - ? query - : { ...query, deletedAt: null }; - return await this.model - .findOneAndUpdate(effectiveQuery, update, { new: true, ...options }) - .exec(); + async update(query: FilterQuery, update: UpdateQuery, options: QueryOptions = {}, includeDeleted = false): Promise { + const effectiveQuery = includeDeleted ? query : { ...query, deletedAt: null }; + return await this.model.findOneAndUpdate(effectiveQuery, update, { new: true, ...options }).exec(); } - async delete( - query: FilterQuery, - options: QueryOptions = {}, - softDelete = true, - ): Promise { + async delete(query: FilterQuery, options: QueryOptions = {}, softDelete = true): Promise { if (softDelete) { return await this.update( query, { $set: { deletedAt: new Date() } } as UpdateQuery, options, - true, + true ); - } else { - return await this.model.findOneAndDelete(query, options).exec(); } + return await this.model.findOneAndDelete(query, options).exec(); } - async countDocuments( - query: FilterQuery = {}, - includeDeleted = false, - ): Promise { - const effectiveQuery = includeDeleted - ? query - : { ...query, deletedAt: null }; + async countDocuments(query: FilterQuery = {}, includeDeleted = false): Promise { + const effectiveQuery = includeDeleted ? query : { ...query, deletedAt: null }; return await this.model.countDocuments(effectiveQuery).exec(); } From 1f48d4d11e7bf04172c1c3ddd5d260c4412f7b8f Mon Sep 17 00:00:00 2001 From: shashankshetty2312 Date: Tue, 3 Mar 2026 22:08:22 +0530 Subject: [PATCH 12/12] Update base.model.ts --- src/core/engine/base/_models/base.model.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/engine/base/_models/base.model.ts b/src/core/engine/base/_models/base.model.ts index 0321a3e..d73c46b 100644 --- a/src/core/engine/base/_models/base.model.ts +++ b/src/core/engine/base/_models/base.model.ts @@ -21,7 +21,8 @@ function createBaseSchema( const baseSchema = new Schema( { ...definition, - deletedAt: { type: Date, default: null }, + // Added index to prevent full collection scans on soft-deleted filters + deletedAt: { type: Date, default: null, index: true }, deletedBy: { type: Schema.Types.ObjectId, ref: 'User', default: null }, createdBy: { type: Schema.Types.ObjectId, ref: 'User', default: null }, updatedBy: { type: Schema.Types.ObjectId, ref: 'User', default: null },