From a7f82b12155aa5b94be82dca10a6f063499bc0bf Mon Sep 17 00:00:00 2001 From: soufiane Date: Fri, 5 Dec 2025 15:03:36 +0100 Subject: [PATCH] fix: replace vulnerable email regex with safe alternatives MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Update isValidEmail in helpers.js with secure non-backtracking regex - Use isValidEmail helper in auth.controller.js - Use isValidEmail helper in contact.controller.js - Replace regex with Zod .email() in newsletter.validation.js - Fixes 5 SonarQube Security Hotspots for DoS via backtracking 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/controllers/auth.controller.js | 7 +++---- src/controllers/contact.controller.js | 6 +++--- src/utils/helpers.js | 6 +++++- src/validations/newsletter.validation.js | 6 ++++-- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/controllers/auth.controller.js b/src/controllers/auth.controller.js index 7ec0aef0..3e9687cd 100644 --- a/src/controllers/auth.controller.js +++ b/src/controllers/auth.controller.js @@ -4,7 +4,7 @@ import bcrypt from 'bcrypt'; import { pool } from '../../db.js'; import { AppError, asyncHandler } from '../middleware/errorHandler.js'; -import { generateToken, generateJWT, getTokenExpiry, isExpired } from '../utils/helpers.js'; +import { generateToken, generateJWT, getTokenExpiry, isExpired, isValidEmail } from '../utils/helpers.js'; import { sendResetPasswordEmail, sendWelcomeEmail } from '../services/email.service.js'; import dns from 'dns'; import { promisify } from 'util'; @@ -330,9 +330,8 @@ export const checkEmail = asyncHandler(async (req, res, next) => { return next(new AppError('Email requis', 400)); } - // Validation format email - const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; - if (!emailRegex.test(email)) { + // Validation format email (utilise isValidEmail pour éviter DoS) + if (!isValidEmail(email)) { return res.json({ success: true, isValid: false, diff --git a/src/controllers/contact.controller.js b/src/controllers/contact.controller.js index c963f773..d9d157f4 100644 --- a/src/controllers/contact.controller.js +++ b/src/controllers/contact.controller.js @@ -2,6 +2,7 @@ * Contrôleur pour gérer les messages de contact */ import { sendContactEmail } from '../services/email.service.js'; +import { isValidEmail } from '../utils/helpers.js'; /** * POST /api/contact @@ -19,9 +20,8 @@ export const submitContactForm = async (req, res) => { }); } - // Validation email - const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; - if (!emailRegex.test(email)) { + // Validation email (utilise isValidEmail pour éviter DoS) + if (!isValidEmail(email)) { return res.status(400).json({ success: false, message: 'Adresse email invalide' diff --git a/src/utils/helpers.js b/src/utils/helpers.js index acef8709..e0bbe8cb 100644 --- a/src/utils/helpers.js +++ b/src/utils/helpers.js @@ -45,9 +45,13 @@ export const generateTicketCode = () => { /** * Valide le format d'un email + * Utilise une regex sécurisée (non-backtracking) pour éviter les attaques DoS */ export const isValidEmail = (email) => { - const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + // Limite la longueur pour éviter les attaques + if (!email || email.length > 254) return false; + // Regex simple et sécurisée (non-backtracking) + const emailRegex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/; return emailRegex.test(email); }; diff --git a/src/validations/newsletter.validation.js b/src/validations/newsletter.validation.js index 60abc01e..74421b1c 100644 --- a/src/validations/newsletter.validation.js +++ b/src/validations/newsletter.validation.js @@ -10,7 +10,8 @@ export const subscribeSchema = z.object({ .string({ required_error: 'L\'email est requis', }) - .regex(/^[^\s@]+@[^\s@]+\.[^\s@]+$/, 'Format d\'email invalide'), + .email('Format d\'email invalide') + .max(254, 'Email trop long'), }), }); @@ -21,7 +22,8 @@ export const unsubscribeSchema = z.object({ .string({ required_error: 'L\'email est requis', }) - .regex(/^[^\s@]+@[^\s@]+\.[^\s@]+$/, 'Format d\'email invalide'), + .email('Format d\'email invalide') + .max(254, 'Email trop long'), }), });