From e57ecbb89bfc4a8513f7a2346989bc87cf1dea6e Mon Sep 17 00:00:00 2001 From: Tharuneshwar V Date: Sat, 20 Jan 2024 20:34:11 +0530 Subject: [PATCH 01/11] Add express-validator package --- backend/middleware/validator.js | 10 ++++++++++ package-lock.json | 21 +++++++++++++++++++++ package.json | 1 + 3 files changed, 32 insertions(+) create mode 100644 backend/middleware/validator.js diff --git a/backend/middleware/validator.js b/backend/middleware/validator.js new file mode 100644 index 0000000..5dbce52 --- /dev/null +++ b/backend/middleware/validator.js @@ -0,0 +1,10 @@ +import { validationResult } from "express-validator" + +const validator = (req, res, next) => { + const result = validationResult(req); + if(result.isEmpty()) + return next(); + res.status(400).json({ errors: result.array() }); +} + +export default validator; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index dd4c46a..3305ee3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "cookie-parser": "^1.4.6", "cors": "^2.8.5", "express": "^4.18.2", + "express-validator": "^7.0.1", "jsonwebtoken": "^9.0.2", "mongoose": "^8.0.3", "multer": "^1.4.5-lts.1", @@ -731,6 +732,18 @@ "node": ">= 0.10.0" } }, + "node_modules/express-validator": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/express-validator/-/express-validator-7.0.1.tgz", + "integrity": "sha512-oB+z9QOzQIE8FnlINqyIFA8eIckahC6qc8KtqLdLJcU3/phVyuhXH3bA4qzcrhme+1RYaCSwrq+TlZ/kAKIARA==", + "dependencies": { + "lodash": "^4.17.21", + "validator": "^13.9.0" + }, + "engines": { + "node": ">= 8.0.0" + } + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -2232,6 +2245,14 @@ "node": ">= 0.4.0" } }, + "node_modules/validator": { + "version": "13.11.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz", + "integrity": "sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/package.json b/package.json index 059401a..9b578de 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "cookie-parser": "^1.4.6", "cors": "^2.8.5", "express": "^4.18.2", + "express-validator": "^7.0.1", "jsonwebtoken": "^9.0.2", "mongoose": "^8.0.3", "multer": "^1.4.5-lts.1", From 43fd9f32ab9c6f9d07e8b82433918c38d7d824e1 Mon Sep 17 00:00:00 2001 From: Tharuneshwar V Date: Sat, 20 Jan 2024 20:36:22 +0530 Subject: [PATCH 02/11] add validation for user routes --- backend/routes/userRoutes.js | 38 ++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/backend/routes/userRoutes.js b/backend/routes/userRoutes.js index d6242e3..7427121 100644 --- a/backend/routes/userRoutes.js +++ b/backend/routes/userRoutes.js @@ -1,5 +1,4 @@ import express from 'express'; -const router = express.Router(); import { loginUser, registerUser, @@ -13,10 +12,37 @@ import { admins } from '../controllers/userController.js'; import { protect, admin } from '../middleware/authMiddleware.js'; +import validateRequest from '../middleware/validator.js'; +import {body, param} from 'express-validator'; + +const router = express.Router(); +const validator = { + checkLogin: [ + body('email').trim().notEmpty().withMessage('Email is Required').bail().isEmail().withMessage("Please enter a valid email address"), + body('password').trim().isString().notEmpty().withMessage('Password is Empty') + ], + checkNewUser: [ + body('email').trim().notEmpty().withMessage('Email is Required').bail().isEmail().withMessage("Please enter a valid email address"), + body('password').trim().isString().notEmpty().withMessage('Password is Empty'), + body('name').trim().notEmpty().withMessage('Name is Required').escape() + ], + checkGetUserById: [ + param('id').exists().withMessage('Id is required').isMongoId().withMessage('Invalid Id') + ], + checkUpdateUser: [ + body('email').trim().notEmpty().withMessage('Email is Required').bail().isEmail().withMessage("Please enter a valid email address"), + body('name').trim().notEmpty().withMessage('Name is Required').escape(), + body('isAdmin').isBoolean().withMessage('isAdmin value should be true/false'), + param('id').exists().withMessage('Id is required').isMongoId().withMessage('Invalid Id') + ] +} + +router.route('/') + .post(validator.checkNewUser, validateRequest, registerUser) + .get(protect, admin, getUsers); -router.route('/').post(registerUser).get(protect, admin, getUsers); router.route('/admins').get(protect, admin, admins); -router.post('/login', loginUser); +router.post('/login', validator.checkLogin, validateRequest, loginUser); router.post('/logout', protect, logoutUser); @@ -27,8 +53,8 @@ router router .route('/:id') - .get(protect, admin, getUserById) - .put(protect, admin, updateUser) - .delete(protect, admin, deleteUser); + .get(validator.checkGetUserById, validateRequest, protect, admin, getUserById) + .put(validator.checkUpdateUser, validateRequest, protect, admin, updateUser) + .delete(validator.checkGetUserById, validateRequest, protect, admin, deleteUser); export default router; From bd0bb39e319cc7e5d5b55457e1ec76e866b70efc Mon Sep 17 00:00:00 2001 From: Tharuneshwar V Date: Sun, 21 Jan 2024 20:45:05 +0530 Subject: [PATCH 03/11] Add validation for userRoutes --- backend/routes/userRoutes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/routes/userRoutes.js b/backend/routes/userRoutes.js index 7427121..3418101 100644 --- a/backend/routes/userRoutes.js +++ b/backend/routes/userRoutes.js @@ -49,7 +49,7 @@ router.post('/logout', protect, logoutUser); router .route('/profile') .get(protect, getUserProfile) - .put(protect, updateUserProfile); + .put(validator.checkNewUser, validateRequest, protect, updateUserProfile); router .route('/:id') From 88b359a1bdd74b250f9125c8f84a3f7a2912ff9f Mon Sep 17 00:00:00 2001 From: Tharuneshwar V Date: Sun, 21 Jan 2024 20:45:30 +0530 Subject: [PATCH 04/11] Add validation for upload routes --- backend/routes/uploadRoutes.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/backend/routes/uploadRoutes.js b/backend/routes/uploadRoutes.js index 7178d83..c142f5c 100644 --- a/backend/routes/uploadRoutes.js +++ b/backend/routes/uploadRoutes.js @@ -1,5 +1,7 @@ import express from 'express'; +import { check } from 'express-validator'; import multer from 'multer'; +import validateRequest from '../middleware/validator.js'; const router = express.Router(); @@ -28,7 +30,13 @@ const fileFilter = (req, file, cb) => { const upload = multer({ storage, fileFilter }).single('image'); -router.post('/', upload, (req, res) => { +const validator = { + upload: [ + check('file').notEmpty().withMessage("Invalid file") + ] +} + +router.post('/', validator.upload, validateRequest, upload, (req, res) => { console.log(req.file); res.send({ message: 'Image uploaded', From 254493eb3f55b6ca163916d63a691b99a738f333 Mon Sep 17 00:00:00 2001 From: Tharuneshwar V Date: Sun, 21 Jan 2024 20:45:49 +0530 Subject: [PATCH 05/11] Add validation for productRoutes --- backend/routes/productRoutes.js | 84 ++++++++++++++++++++++++++++++--- 1 file changed, 78 insertions(+), 6 deletions(-) diff --git a/backend/routes/productRoutes.js b/backend/routes/productRoutes.js index 67f48c0..b462983 100644 --- a/backend/routes/productRoutes.js +++ b/backend/routes/productRoutes.js @@ -1,4 +1,4 @@ -import express from 'express'; +import express, { query } from 'express'; import { getProducts, getProduct, @@ -9,16 +9,88 @@ import { getTopProducts } from '../controllers/productController.js'; import { protect, admin } from '../middleware/authMiddleware.js'; +import validateRequest from '../middleware/validator.js'; +import {body, check, param} from 'express-validator'; const router = express.Router(); -router.route('/').post(protect, admin, createProduct).get(getProducts); +const validator = { + getProducts: [ + check('limit').optional().isNumeric().withMessage('Limit parameter must be a number').custom(value => { + if(value < 0) throw new Error('Value should not be less than Zero'); + return true; + }), + check('skip').optional().isNumeric().withMessage('skip parameter must be a number').custom(value => { + if(value < 0) throw new Error('Value should not be less than Zero'); + return true; + }), + check('search').optional().trim().escape() + ], + createProduct: [ + check('name').trim().notEmpty().withMessage('Name is required').escape(), + check('image').notEmpty().withMessage('Image is required'), + check('description') + .trim() + .notEmpty() + .withMessage('Description is required') + .escape(), + check('brand').trim().notEmpty().withMessage('Brand is required').escape(), + check('category').trim().notEmpty().withMessage('Category is required').escape(), + check('price') + .notEmpty() + .withMessage('Price is required') + .isNumeric() + .withMessage('Price must be a number'), + check('countInStock') + .notEmpty() + .withMessage('Count in stock is required') + .isNumeric() + .withMessage('Count in stock must be a number') + ], + createProductReview: [ + param('id').notEmpty().withMessage('Id is required').isMongoId().withMessage('Invalid Id Format'), + body('rating').notEmpty().withMessage('Rating is Empty').bail().isNumeric().withMessage('Ratings must be number'), + body('comment').trim().escape() + ], + getProduct: [ + param('id').notEmpty().withMessage('Id is required').isMongoId().withMessage('Invalid Id Format') + ], + deleteProduct: [ + param('id').notEmpty().withMessage('Id is required').isMongoId().withMessage('Invalid Id Format') + ], + updateProduct: [ + check('name').trim().notEmpty().withMessage('Name is required').escape(), + check('image').notEmpty().withMessage('Image is required'), + check('description') + .trim() + .notEmpty() + .withMessage('Description is required') + .escape(), + check('brand').trim().notEmpty().withMessage('Brand is required').escape(), + check('category').trim().notEmpty().withMessage('Category is required').escape(), + check('price') + .notEmpty() + .withMessage('Price is required') + .isNumeric() + .withMessage('Price must be a number'), + check('countInStock') + .notEmpty() + .withMessage('Count in stock is required') + .isNumeric() + .withMessage('Count in stock must be a number'), + param('id').notEmpty().withMessage('Id is required').isMongoId().withMessage('Invalid Id Format') + ] +} + +router.route('/') + .post(validator.createProduct, validateRequest, protect, admin, createProduct) + .get(validator.getProducts, validateRequest, getProducts); router.get('/top', getTopProducts); -router.post('/reviews/:id', protect, createProductReview); +router.post('/reviews/:id', validator.createProductReview, validateRequest, protect, createProductReview); router .route('/:id') - .get(getProduct) - .put(protect, admin, updateProduct) - .delete(protect, admin, deleteProduct); + .get(validator.getProduct, validateRequest, getProduct) + .put(validator.updateProduct, validateRequest, protect, admin, updateProduct) + .delete(validator.deleteProduct, validateRequest, protect, admin, deleteProduct); export default router; From d19010de3ec3e09c2171088d65af78155d04cd24 Mon Sep 17 00:00:00 2001 From: Tharuneshwar V Date: Sun, 21 Jan 2024 20:46:10 +0530 Subject: [PATCH 06/11] Add validation for payment routes --- backend/routes/paymentRoutes.js | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/backend/routes/paymentRoutes.js b/backend/routes/paymentRoutes.js index 6ad9c0c..46fc647 100644 --- a/backend/routes/paymentRoutes.js +++ b/backend/routes/paymentRoutes.js @@ -2,13 +2,36 @@ import express from 'express'; import { protect } from '../middleware/authMiddleware.js'; import { config, order, validate } from '../controllers/paymentController.js'; +import validateRequest from '../middleware/validator.js'; +import {body, check} from 'express-validator'; const router = express.Router(); +const validator = { + order: [ + check('body').exists().withMessage('Request body is required') + ], + validate: [ + body('razorpay_order_id') + .notEmpty() + .withMessage('Razorpay order ID is required') + .trim(), + body('razorpay_payment_id') + .notEmpty() + .withMessage('Razorpay payment ID is required') + .trim(), + body('razorpay_signature') + .notEmpty() + .withMessage('Razorpay signature is required') + .trim() + .escape() + ] +} + router.get('/razorpay/config', config); -router.post('/razorpay/order', protect, order); +router.post('/razorpay/order', validator.order, validateRequest, protect, order); -router.post('/razorpay/order/validate', protect, validate); +router.post('/razorpay/order/validate', validator.validate, validateRequest, protect, validate); export default router; From 281defbbdafb882337f58aa39d54e112b499f530 Mon Sep 17 00:00:00 2001 From: Tharuneshwar V Date: Sun, 21 Jan 2024 20:46:32 +0530 Subject: [PATCH 07/11] Add validation for order routes --- backend/routes/orderRoutes.js | 52 +++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/backend/routes/orderRoutes.js b/backend/routes/orderRoutes.js index 8a0afe5..b44fb20 100644 --- a/backend/routes/orderRoutes.js +++ b/backend/routes/orderRoutes.js @@ -1,5 +1,4 @@ import express from 'express'; -const router = express.Router(); import { protect, admin } from '../middleware/authMiddleware.js'; import { addOrderItems, @@ -9,12 +8,55 @@ import { updateOrderToDeliver, getOrders } from '../controllers/orderController.js'; +import validateRequest from '../middleware/validator.js'; +import { param, check } from 'express-validator'; + +const router = express.Router(); + +const validator = { + getOrderById: [ + param('id').notEmpty().withMessage('Id is required').isMongoId().withMessage('Invalid Id Format') + ], + updateOrderToPaid: [ + param('id').notEmpty().withMessage('Id is required').isMongoId().withMessage('Invalid Id Format') + ], + updateOrderToDeliver: [ + param('id').notEmpty().withMessage('Id is required').isMongoId().withMessage('Invalid Id Format') + ], + addOrderItems: [ + check('cartItems').notEmpty().withMessage('Cart items are required'), + check('shippingAddress').notEmpty().withMessage('Shipping address is required'), + check('paymentMethod').notEmpty().withMessage('Payment method is required'), + check('itemsPrice') + .notEmpty() + .withMessage('Items price is required') + .isNumeric() + .withMessage('Items price must be a number'), + check('taxPrice') + .notEmpty() + .withMessage('Tax price is required') + .isNumeric() + .withMessage('Tax price must be a number'), + check('shippingPrice') + .notEmpty() + .withMessage('Shipping price is required') + .isNumeric() + .withMessage('Shipping price must be a number'), + check('totalPrice') + .notEmpty() + .withMessage('Total price is required') + .isNumeric() + .withMessage('Total price must be a number') + ] +} -router.route('/').post(protect, addOrderItems).get(protect, admin, getOrders); +router.route('/') + .post(validator.addOrderItems, validateRequest, protect, addOrderItems) + .get(protect, admin, getOrders); router.get('/my-orders', protect, getMyOrders); -router.get('/:id', protect, getOrderById); -router.put('/:id/pay', protect, updateOrderToPaid); -router.put('/:id/deliver', protect, admin, updateOrderToDeliver); +router.get('/:id', validator.getOrderById, validateRequest, protect, getOrderById); +router.put('/:id/pay', validator.updateOrderToPaid, validateRequest, protect, updateOrderToPaid); +router.put('/:id/deliver', validator.updateOrderToDeliver, validateRequest, protect, admin, updateOrderToDeliver); export default router; From 8d3a2618cde2a45dda820d0fda2f5944a4169cc9 Mon Sep 17 00:00:00 2001 From: Tharuneshwar V Date: Sun, 21 Jan 2024 21:40:11 +0530 Subject: [PATCH 08/11] Add validation for password reset --- backend/routes/userRoutes.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/backend/routes/userRoutes.js b/backend/routes/userRoutes.js index ed2de44..e548600 100644 --- a/backend/routes/userRoutes.js +++ b/backend/routes/userRoutes.js @@ -36,6 +36,14 @@ const validator = { body('name').trim().notEmpty().withMessage('Name is Required').escape(), body('isAdmin').isBoolean().withMessage('isAdmin value should be true/false'), param('id').exists().withMessage('Id is required').isMongoId().withMessage('Invalid Id') + ], + resetPasswordRequest: [ + body('email').trim().notEmpty().withMessage('Email is Required').bail().isEmail().withMessage("Please enter a valid email address") + ], + resetPassword: [ + body('password').trim().notEmpty().withMessage('Password is Required').escape(), + param('id').exists().withMessage('Id is required').isMongoId().withMessage('Invalid Id'), + param('token').trim().notEmpty().withMessage('Token is Required') ] } @@ -45,8 +53,8 @@ router.route('/') router.route('/admins').get(protect, admin, admins); -router.post('/reset-password/request', resetPasswordRequest); -router.post('/reset-password/reset/:id/:token', resetPassword); +router.post('/reset-password/request', validator.resetPasswordRequest, validateRequest, resetPasswordRequest); +router.post('/reset-password/reset/:id/:token', validator.resetPassword, validateRequest, resetPassword); router.post('/login', validator.checkLogin, validateRequest, loginUser); router.post('/logout', protect, logoutUser); From c97341430808645ce010df241909f4247670f18b Mon Sep 17 00:00:00 2001 From: Tharuneshwar V Date: Mon, 22 Jan 2024 09:09:23 +0530 Subject: [PATCH 09/11] Add validation for payment --- backend/routes/paymentRoutes.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/backend/routes/paymentRoutes.js b/backend/routes/paymentRoutes.js index 46fc647..75ad48b 100644 --- a/backend/routes/paymentRoutes.js +++ b/backend/routes/paymentRoutes.js @@ -9,7 +9,11 @@ const router = express.Router(); const validator = { order: [ - check('body').exists().withMessage('Request body is required') + body().custom(body => { + if (Object.keys(body).length === 0) + throw new Error('Request Body is empty') + return true; + }) ], validate: [ body('razorpay_order_id') From 6c2e240e5a79306227c666d3994815acac41bde7 Mon Sep 17 00:00:00 2001 From: Tharuneshwar V Date: Mon, 22 Jan 2024 20:16:16 +0530 Subject: [PATCH 10/11] password length validation --- backend/routes/userRoutes.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/backend/routes/userRoutes.js b/backend/routes/userRoutes.js index e548600..bf95ddf 100644 --- a/backend/routes/userRoutes.js +++ b/backend/routes/userRoutes.js @@ -25,7 +25,8 @@ const validator = { ], checkNewUser: [ body('email').trim().notEmpty().withMessage('Email is Required').bail().isEmail().withMessage("Please enter a valid email address"), - body('password').trim().isString().notEmpty().withMessage('Password is Empty'), + body('password').trim().isString().notEmpty().withMessage('Password is Empty').bail() + .isLength({ min: 6 }).withMessage('Password must be at least 6 characters'), body('name').trim().notEmpty().withMessage('Name is Required').escape() ], checkGetUserById: [ @@ -41,7 +42,8 @@ const validator = { body('email').trim().notEmpty().withMessage('Email is Required').bail().isEmail().withMessage("Please enter a valid email address") ], resetPassword: [ - body('password').trim().notEmpty().withMessage('Password is Required').escape(), + body('password').trim().notEmpty().withMessage('Password is Required').escape().bail() + .isLength({ min: 6 }).withMessage('Password must be at least 6 characters'), param('id').exists().withMessage('Id is required').isMongoId().withMessage('Invalid Id'), param('token').trim().notEmpty().withMessage('Token is Required') ] From 5daa61500fb28726d0eaa155e1135d1c32384eba Mon Sep 17 00:00:00 2001 From: Tharuneshwar V Date: Mon, 22 Jan 2024 21:25:07 +0530 Subject: [PATCH 11/11] Add validation for file upload --- backend/routes/uploadRoutes.js | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/backend/routes/uploadRoutes.js b/backend/routes/uploadRoutes.js index c142f5c..512bdb1 100644 --- a/backend/routes/uploadRoutes.js +++ b/backend/routes/uploadRoutes.js @@ -1,5 +1,4 @@ -import express from 'express'; -import { check } from 'express-validator'; +import express from 'express';import { body, check } from 'express-validator'; import multer from 'multer'; import validateRequest from '../middleware/validator.js'; @@ -15,7 +14,7 @@ const storage = multer.diskStorage({ }); const fileFilter = (req, file, cb) => { - if ( + if ( file.mimetype === 'image/png' || file.mimetype === 'image/jpg' || file.mimetype === 'image/jpeg' @@ -23,21 +22,17 @@ const fileFilter = (req, file, cb) => { // To accept the file pass `true`, like so: cb(null, true); } else { - // To reject this file pass `false`, like so: + // To reject this file pass `false`, like so: cb('Images only!'); } }; const upload = multer({ storage, fileFilter }).single('image'); -const validator = { - upload: [ - check('file').notEmpty().withMessage("Invalid file") - ] -} +router.post('/', upload, (req, res) => { + if (!req.file) + throw res.status(400).json({error: 'No file uploaded'}); -router.post('/', validator.upload, validateRequest, upload, (req, res) => { - console.log(req.file); res.send({ message: 'Image uploaded', imageUrl: `/${req.file.path}`