From 4e102466f497cd648b75215037a47fe0926cbfa1 Mon Sep 17 00:00:00 2001 From: tinykav Date: Sun, 21 Sep 2025 21:10:28 +0530 Subject: [PATCH 01/21] Fix: applied rate limiting to all sensitive routes --- vendorapp/backend/routes/route.js | 117 +++++++++++++++++++++--------- 1 file changed, 83 insertions(+), 34 deletions(-) diff --git a/vendorapp/backend/routes/route.js b/vendorapp/backend/routes/route.js index 17dd10e..7b6ccb9 100644 --- a/vendorapp/backend/routes/route.js +++ b/vendorapp/backend/routes/route.js @@ -1,22 +1,14 @@ const router = require("express").Router(); const authMiddleware = require("../middleware/authMiddleware.js"); -//update const rateLimit = require("express-rate-limit"); -// Rate limiter for login route -const loginLimiter = rateLimit({ - windowMs: 5 * 60 * 1000, // 5 minutes - max: 5, // limit each IP to 5 login attempts per window - message: { error: "Too many login attempts. Please try again later." }, - standardHeaders: true, // Return rate limit info in headers - legacyHeaders: false // Disable the X-RateLimit headers -}); - +// Vendor Controller const { vendorRegister, vendorLogIn } = require("../controllers/vendorController.js"); +// Service Controller const { serviceCreate, getServices, @@ -35,6 +27,7 @@ const { getAddedToInvoiceServices } = require("../controllers/serviceController.js"); +// Couple Controller const { coupleRegister, coupleLogIn, @@ -42,46 +35,102 @@ const { invoiceUpdate } = require("../controllers/coupleController.js"); +// Booking Controller const { newBooking, getBookingedServicesByCouple, getBookingedServicesByVendor } = require("../controllers/bookingController.js"); +// ====== Rate Limiters ====== + +// Strict limiter for login/register (brute-force protection) +const authLimiter = rateLimit({ + windowMs: 5 * 60 * 1000, // 5 minutes + max: 5, // max 5 attempts + message: { error: "Too many attempts. Please try again later." }, + standardHeaders: true, + legacyHeaders: false +}); + +// Moderate limiter for sensitive write actions (create/update/delete) +const writeLimiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 20, // max 20 actions per IP + message: { error: "Too many requests. Please try again later." }, + standardHeaders: true, + legacyHeaders: false +}); + +// Lighter limiter for search/list endpoints (anti-scraping) +const searchLimiter = rateLimit({ + windowMs: 1 * 60 * 1000, // 1 minute + max: 60, // 60 requests per minute + message: { error: "Too many search requests. Please slow down." }, + standardHeaders: true, + legacyHeaders: false +}); + +// ====== Routes ====== + // Vendor -router.post("/VendorRegister", vendorRegister); -router.post("/VendorLogin", vendorLogIn); +router.post("/VendorRegister", authLimiter, vendorRegister); +router.post("/VendorLogin", authLimiter, vendorLogIn); // Service -router.post("/ServiceCreate", serviceCreate); -router.get("/getVendorServices/:id", getVendorServices); -router.get("/getServices", getServices); -router.get("/getServiceDetail/:id", getServiceDetail); -router.get("/getInterestedCouples/:id", getInterestedCouples); -router.get("/getAddedToInvoiceServices/:id", getAddedToInvoiceServices); +router.post("/ServiceCreate", writeLimiter, serviceCreate); +router.get("/getVendorServices/:id", searchLimiter, getVendorServices); +router.get("/getServices", searchLimiter, getServices); +router.get("/getServiceDetail/:id", searchLimiter, getServiceDetail); +router.get("/getInterestedCouples/:id", searchLimiter, getInterestedCouples); +router.get( + "/getAddedToInvoiceServices/:id", + searchLimiter, + getAddedToInvoiceServices +); -router.put("/ServiceUpdate/:id", updateService); -router.put("/addReview/:id", addReview); +router.put("/ServiceUpdate/:id", writeLimiter, updateService); +router.put("/addReview/:id", writeLimiter, addReview); -router.get("/searchService/:key", searchService); -router.get("/searchServicebyCategory/:key", searchServicebyCategory); -router.get("/searchServicebySubCategory/:key", searchServicebySubCategory); +router.get("/searchService/:key", searchLimiter, searchService); +router.get( + "/searchServicebyCategory/:key", + searchLimiter, + searchServicebyCategory +); +router.get( + "/searchServicebySubCategory/:key", + searchLimiter, + searchServicebySubCategory +); -router.delete("/DeleteService/:id", deleteService); -router.delete("/DeleteServices/:id", deleteServices); -router.put("/deleteServiceReview/:id", deleteServiceReview); -router.delete("/deleteAllServiceReviews/:id", deleteAllServiceReviews); +router.delete("/DeleteService/:id", writeLimiter, deleteService); +router.delete("/DeleteServices/:id", writeLimiter, deleteServices); +router.put("/deleteServiceReview/:id", writeLimiter, deleteServiceReview); +router.delete( + "/deleteAllServiceReviews/:id", + writeLimiter, + deleteAllServiceReviews +); // Couple -router.post("/CoupleRegister", coupleRegister); -router.post("/CoupleLogin", loginLimiter, coupleLogIn); +router.post("/CoupleRegister", authLimiter, coupleRegister); +router.post("/CoupleLogin", authLimiter, coupleLogIn); -router.get("/getInvoiceDetail/:id", getInvoiceDetail); -router.put("/CoupleUpdate/:id", invoiceUpdate); +router.get("/getInvoiceDetail/:id", searchLimiter, getInvoiceDetail); +router.put("/CoupleUpdate/:id", writeLimiter, invoiceUpdate); // Booking -router.post("/newBooking", newBooking); -router.get("/getBookingedServicesByCouple/:id", getBookingedServicesByCouple); -router.get("/getBookingedServicesByVendor/:id", getBookingedServicesByVendor); +router.post("/newBooking", writeLimiter, newBooking); +router.get( + "/getBookingedServicesByCouple/:id", + searchLimiter, + getBookingedServicesByCouple +); +router.get( + "/getBookingedServicesByVendor/:id", + searchLimiter, + getBookingedServicesByVendor +); module.exports = router; From 40c425a3b646a02a84e724e5fd206a3b0e8b31e8 Mon Sep 17 00:00:00 2001 From: tinykav Date: Sun, 21 Sep 2025 21:41:01 +0530 Subject: [PATCH 02/21] Fix: applied rate limiting to all sensitive routes --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7d5d492..4e038f3 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +# tiny + # Blissify--Wedding-Management-System SLIIT 2nd year second semester ITP module project using MERN stack technology for a wedding management system. @@ -15,7 +17,6 @@ Micro frontend architecture was implemented to reduce complexity and scale the s

banner

- Contributors 1. Sadeesha : Perera RLSB From 3af4e6f6094fdddd990805071a2bb540f10f8ac0 Mon Sep 17 00:00:00 2001 From: tinykav Date: Sun, 21 Sep 2025 22:08:55 +0530 Subject: [PATCH 03/21] Fix: applied rate limiting to all sensitive routes --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4e038f3..b5de462 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# tiny +# tinyy # Blissify--Wedding-Management-System From 1fda448d40bf372a83fdf9e813d125868c52c047 Mon Sep 17 00:00:00 2001 From: tinykav Date: Sun, 21 Sep 2025 22:17:38 +0530 Subject: [PATCH 04/21] Fix: applied rate limiting to all sensitive routes --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b5de462..5efc5b2 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# tinyy +# tinyyy # Blissify--Wedding-Management-System From df15cd374c8e131c18185bb4c649344bcc38ff46 Mon Sep 17 00:00:00 2001 From: Tiny Handalage <72993280+tinykav@users.noreply.github.com> Date: Sun, 21 Sep 2025 22:41:54 +0530 Subject: [PATCH 05/21] Create workflows --- .github/workflows | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 .github/workflows diff --git a/.github/workflows b/.github/workflows new file mode 100644 index 0000000..e91fc8c --- /dev/null +++ b/.github/workflows @@ -0,0 +1,46 @@ +name: "CodeQL" + +on: + push: + branches: [ "*" ] + pull_request: + branches: [ "*" ] + schedule: + - cron: '0 0 * * 0' # Weekly scan + +jobs: + analyze: + name: CodeQL Analysis + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'javascript' ] # Scan JavaScript only + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' # or your Node version + + - name: Install dependencies + run: npm install + + - name: Build project + run: npm run build --if-present + + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 From 9c966663a1c788899f026856d36aec63b5528b1e Mon Sep 17 00:00:00 2001 From: Tiny Handalage <72993280+tinykav@users.noreply.github.com> Date: Sun, 21 Sep 2025 22:43:27 +0530 Subject: [PATCH 06/21] Delete .github/workflows --- .github/workflows | 46 ---------------------------------------------- 1 file changed, 46 deletions(-) delete mode 100644 .github/workflows diff --git a/.github/workflows b/.github/workflows deleted file mode 100644 index e91fc8c..0000000 --- a/.github/workflows +++ /dev/null @@ -1,46 +0,0 @@ -name: "CodeQL" - -on: - push: - branches: [ "*" ] - pull_request: - branches: [ "*" ] - schedule: - - cron: '0 0 * * 0' # Weekly scan - -jobs: - analyze: - name: CodeQL Analysis - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - - strategy: - fail-fast: false - matrix: - language: [ 'javascript' ] # Scan JavaScript only - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: '20' # or your Node version - - - name: Install dependencies - run: npm install - - - name: Build project - run: npm run build --if-present - - - name: Initialize CodeQL - uses: github/codeql-action/init@v3 - with: - languages: ${{ matrix.language }} - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 From 1a2ce076a4e48d3d56ffaa2a4dfdeda46b257e22 Mon Sep 17 00:00:00 2001 From: Tiny Handalage <72993280+tinykav@users.noreply.github.com> Date: Sun, 21 Sep 2025 22:44:09 +0530 Subject: [PATCH 07/21] Create codeql.yml --- .github/workflows/codeql.yml | 46 ++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..e91fc8c --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,46 @@ +name: "CodeQL" + +on: + push: + branches: [ "*" ] + pull_request: + branches: [ "*" ] + schedule: + - cron: '0 0 * * 0' # Weekly scan + +jobs: + analyze: + name: CodeQL Analysis + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'javascript' ] # Scan JavaScript only + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' # or your Node version + + - name: Install dependencies + run: npm install + + - name: Build project + run: npm run build --if-present + + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 From 12aadf9a4b79d4cac4140e9000316acbed39bb6b Mon Sep 17 00:00:00 2001 From: tinykav Date: Sun, 21 Sep 2025 22:47:59 +0530 Subject: [PATCH 08/21] Fix: applied rate limiting to all sensitive routes --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5efc5b2..4e038f3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# tinyyy +# tiny # Blissify--Wedding-Management-System From d9dab9b1702de74a122b6874bcfa551108f39187 Mon Sep 17 00:00:00 2001 From: tinykav Date: Mon, 22 Sep 2025 01:59:33 +0530 Subject: [PATCH 09/21] Fix: applied rate limiting to all sensitive routes --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4e038f3..f27e98b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# tiny +# tinyyyy # Blissify--Wedding-Management-System From 934d6ad25bf3e28508e29cb1cac4d72359bfc962 Mon Sep 17 00:00:00 2001 From: tinykav Date: Mon, 22 Sep 2025 02:16:56 +0530 Subject: [PATCH 10/21] Fix: applied rate limiting to taskapp routes --- taskapp/backend/index.js | 47 +- taskapp/backend/package-lock.json | 852 ++++++++++++++++++++++++++++++ taskapp/backend/package.json | 1 + 3 files changed, 883 insertions(+), 17 deletions(-) diff --git a/taskapp/backend/index.js b/taskapp/backend/index.js index 689e00e..79a0382 100644 --- a/taskapp/backend/index.js +++ b/taskapp/backend/index.js @@ -12,23 +12,32 @@ const passport = require("passport"); const TodoRoutes = require("./Routes/TodoRoutes"); const NoteRoutes = require("./Routes/NoteRoutes"); const TaskRoutes = require("./Routes/TaskRoutes"); +//new +const rateLimit = require("express-rate-limit"); const PORT = 8080; +// Rate limiter for authentication routes +const authLimiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 5, // limit each IP to 5 requests per window + message: { error: "Too many attempts, please try again later." } +}); + const app = express(); app.use([ cors({ origin: process.env.FRONTEND_DOMAIN, credentials: true, - methods: ["GET", "PUT", "PATCH", "PUT", "DELETE"], + methods: ["GET", "PUT", "PATCH", "PUT", "DELETE"] }), express.json(), - express.urlencoded({ extended: true }), + express.urlencoded({ extended: true }) ]); const sessionStore = new MongoStore({ mongoUrl: process.env.MONGO_URL, - collectionName: "session", + collectionName: "session" }); app.use( session({ @@ -37,8 +46,8 @@ app.use( saveUninitialized: true, store: sessionStore, cookie: { - maxAge: 1000 * 60 * 60 * 24, - }, + maxAge: 1000 * 60 * 60 * 24 + } }) ); @@ -49,14 +58,15 @@ app.get("/", (req, res) => { res.json(" hello "); }); -app.post("/register", async (req, res) => { +// Register +app.post("/register", authLimiter, async (req, res) => { const { userName, email, password } = req.body; const salt = await bcrypt.genSalt(10); const hashedPassword = await bcrypt.hash(password, salt); const newAuth = new authModel({ userName: userName, email: email, - password: hashedPassword, + password: hashedPassword }); try { @@ -82,7 +92,7 @@ app.get( "/google/callback", passport.authenticate("google", { failureRedirect: process.env.FRONTEND_DOMAIN, - successRedirect: `${process.env.FRONTEND_DOMAIN}/Home`, + successRedirect: `${process.env.FRONTEND_DOMAIN}/Home` }) ); @@ -94,15 +104,16 @@ app.get( "/facebook/callback", passport.authenticate("facebook", { failureRedirect: process.env.FRONTEND_DOMAIN, - successRedirect: `${process.env.FRONTEND_DOMAIN}/Home`, + successRedirect: `${process.env.FRONTEND_DOMAIN}/Home` }) ); -//Local Login +// Login app.post( "/login", + authLimiter, passport.authenticate("local", { - failureRedirect: process.env.FRONTEND_DOMAIN, + failureRedirect: process.env.FRONTEND_DOMAIN }), (req, res) => { res.json({ success: "successfully logged in" }); @@ -124,7 +135,8 @@ app.get("/getUser", (req, res, next) => { }); //Forgot and reset password -app.post("/resetPassword/:id/:token", async (req, res) => { +// Reset Password +app.post("/resetPassword/:id/:token", authLimiter, async (req, res) => { const { id, token } = req.params; console.log(id); const { newPassword } = req.body; @@ -141,12 +153,13 @@ app.post("/resetPassword/:id/:token", async (req, res) => { }); }); -app.post("/forgotpass", async (req, res) => { +// Forgot Password +app.post("/forgotpass", authLimiter, async (req, res) => { const { email } = req.body; await authModel.findOne({ email: email }).then((user) => { if (!user) return res.send({ Status: "Enter a valid email" }); const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET_KEY, { - expiresIn: "1d", + expiresIn: "1d" }); var transporter = nodemailer.createTransport({ service: "gmail", @@ -155,15 +168,15 @@ app.post("/forgotpass", async (req, res) => { secure: true, auth: { user: "jhonmoorthi85131@gmail.com", - pass: "klxb xvje ygnr qvbo", - }, + pass: "klxb xvje ygnr qvbo" + } }); var mailOptions = { from: "jhonmoorthi85131@gmail.com", to: email, subject: "Forgot password for task manager", - text: `${process.env.FRONTEND_DOMAIN}/ResetPass/${user._id}/${token}`, + text: `${process.env.FRONTEND_DOMAIN}/ResetPass/${user._id}/${token}` }; transporter.sendMail(mailOptions, function (error, info) { diff --git a/taskapp/backend/package-lock.json b/taskapp/backend/package-lock.json index 6c4bb3d..f4d9c48 100644 --- a/taskapp/backend/package-lock.json +++ b/taskapp/backend/package-lock.json @@ -16,6 +16,7 @@ "cookie-session": "^2.0.0", "cors": "^2.8.5", "express": "^4.18.2", + "express-rate-limit": "^8.1.0", "express-session": "^1.17.3", "jsonwebtoken": "^9.0.2", "mongoose": "^8.0.0", @@ -34,6 +35,380 @@ "node": "18.x" } }, + "node_modules/@esbuild/android-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=12" + } + }, "node_modules/@mapbox/node-pre-gyp": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", @@ -584,6 +959,44 @@ "node": ">= 0.8" } }, + "node_modules/esbuild": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "dev": true, + "hasInstallScript": true, + "peer": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" + } + }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -638,6 +1051,23 @@ "node": ">= 0.10.0" } }, + "node_modules/express-rate-limit": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.1.0.tgz", + "integrity": "sha512-4nLnATuKupnmwqiJc27b4dCFmB/T60ExgmtDD7waf4LdrbJ8CPZzZRHYErDYNhoz+ql8fUdYwM/opf90PoPAQA==", + "dependencies": { + "ip-address": "10.0.1" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, "node_modules/express-session": { "version": "1.17.3", "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.3.tgz", @@ -1020,6 +1450,14 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "node_modules/ip-address": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", + "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", + "engines": { + "node": ">= 12" + } + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -1425,6 +1863,25 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "peer": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -1703,6 +2160,13 @@ "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "peer": true + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -1714,6 +2178,35 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "peer": true, + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -1821,6 +2314,23 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rollup": { + "version": "3.29.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.5.tgz", + "integrity": "sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==", + "dev": true, + "peer": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=14.18.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -1967,6 +2477,16 @@ "node": ">=10" } }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/sparse-bitfield": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", @@ -2158,6 +2678,62 @@ "node": ">= 0.8" } }, + "node_modules/vite": { + "version": "4.5.14", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.14.tgz", + "integrity": "sha512-+v57oAaoYNnO3hIu5Z/tJRZjq5aHM2zDve9YZ8HngVHbhk66RStobhb1sqPMIPEleV6cNKYK4eGrAbE9Ulbl2g==", + "dev": true, + "peer": true, + "dependencies": { + "esbuild": "^0.18.10", + "postcss": "^8.4.27", + "rollup": "^3.27.1" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + }, + "peerDependencies": { + "@types/node": ">= 14", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, "node_modules/vite-plugin-rewrite-all": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/vite-plugin-rewrite-all/-/vite-plugin-rewrite-all-1.0.1.tgz", @@ -2213,6 +2789,182 @@ } }, "dependencies": { + "@esbuild/android-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/android-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/android-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/darwin-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/darwin-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/freebsd-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/freebsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/linux-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/linux-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/linux-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/linux-loong64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/linux-mips64el": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/linux-ppc64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/linux-riscv64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/linux-s390x": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/linux-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/netbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/openbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/sunos-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/win32-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/win32-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "dev": true, + "optional": true, + "peer": true + }, + "@esbuild/win32-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "dev": true, + "optional": true, + "peer": true + }, "@mapbox/node-pre-gyp": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", @@ -2642,6 +3394,37 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" }, + "esbuild": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "dev": true, + "peer": true, + "requires": { + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" + } + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -2735,6 +3518,14 @@ } } }, + "express-rate-limit": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.1.0.tgz", + "integrity": "sha512-4nLnATuKupnmwqiJc27b4dCFmB/T60ExgmtDD7waf4LdrbJ8CPZzZRHYErDYNhoz+ql8fUdYwM/opf90PoPAQA==", + "requires": { + "ip-address": "10.0.1" + } + }, "express-session": { "version": "1.17.3", "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.3.tgz", @@ -2991,6 +3782,11 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "ip-address": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", + "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==" + }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -3273,6 +4069,13 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "peer": true + }, "negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -3475,11 +4278,30 @@ "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" }, + "picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "peer": true + }, "picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" }, + "postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "peer": true, + "requires": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + } + }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -3554,6 +4376,16 @@ "glob": "^7.1.3" } }, + "rollup": { + "version": "3.29.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.5.tgz", + "integrity": "sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==", + "dev": true, + "peer": true, + "requires": { + "fsevents": "~2.3.2" + } + }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -3669,6 +4501,13 @@ "semver": "^7.5.3" } }, + "source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "peer": true + }, "sparse-bitfield": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", @@ -3815,6 +4654,19 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" }, + "vite": { + "version": "4.5.14", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.14.tgz", + "integrity": "sha512-+v57oAaoYNnO3hIu5Z/tJRZjq5aHM2zDve9YZ8HngVHbhk66RStobhb1sqPMIPEleV6cNKYK4eGrAbE9Ulbl2g==", + "dev": true, + "peer": true, + "requires": { + "esbuild": "^0.18.10", + "fsevents": "~2.3.2", + "postcss": "^8.4.27", + "rollup": "^3.27.1" + } + }, "vite-plugin-rewrite-all": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/vite-plugin-rewrite-all/-/vite-plugin-rewrite-all-1.0.1.tgz", diff --git a/taskapp/backend/package.json b/taskapp/backend/package.json index 105de5f..093d99c 100644 --- a/taskapp/backend/package.json +++ b/taskapp/backend/package.json @@ -22,6 +22,7 @@ "cookie-session": "^2.0.0", "cors": "^2.8.5", "express": "^4.18.2", + "express-rate-limit": "^8.1.0", "express-session": "^1.17.3", "jsonwebtoken": "^9.0.2", "mongoose": "^8.0.0", From 215ca1dc60475c4a7e59c25c068622f5c5410891 Mon Sep 17 00:00:00 2001 From: tinykav Date: Mon, 22 Sep 2025 10:47:15 +0530 Subject: [PATCH 11/21] Added apiLimiter for /todo, /note, /task routes --- taskapp/backend/index.js | 90 ++++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 46 deletions(-) diff --git a/taskapp/backend/index.js b/taskapp/backend/index.js index 79a0382..cb5f9c9 100644 --- a/taskapp/backend/index.js +++ b/taskapp/backend/index.js @@ -12,24 +12,30 @@ const passport = require("passport"); const TodoRoutes = require("./Routes/TodoRoutes"); const NoteRoutes = require("./Routes/NoteRoutes"); const TaskRoutes = require("./Routes/TaskRoutes"); -//new const rateLimit = require("express-rate-limit"); const PORT = 8080; +const app = express(); -// Rate limiter for authentication routes +// 🔹 Auth limiter (sensitive routes) const authLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes - max: 5, // limit each IP to 5 requests per window + max: 5, message: { error: "Too many attempts, please try again later." } }); -const app = express(); +// 🔹 General API limiter (all /todo, /note, /task) +const apiLimiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100, + message: { error: "Too many requests, please slow down." } +}); + app.use([ cors({ origin: process.env.FRONTEND_DOMAIN, credentials: true, - methods: ["GET", "PUT", "PATCH", "PUT", "DELETE"] + methods: ["GET", "POST", "PUT", "PATCH", "DELETE"] }), express.json(), express.urlencoded({ extended: true }) @@ -39,6 +45,7 @@ const sessionStore = new MongoStore({ mongoUrl: process.env.MONGO_URL, collectionName: "session" }); + app.use( session({ secret: process.env.SESSION_SECRET, @@ -54,6 +61,9 @@ app.use( app.use(passport.initialize()); app.use(passport.session()); +// ================== ROUTES ================== + +// Health check app.get("/", (req, res) => { res.json(" hello "); }); @@ -63,15 +73,11 @@ app.post("/register", authLimiter, async (req, res) => { const { userName, email, password } = req.body; const salt = await bcrypt.genSalt(10); const hashedPassword = await bcrypt.hash(password, salt); - const newAuth = new authModel({ - userName: userName, - email: email, - password: hashedPassword - }); + const newAuth = new authModel({ userName, email, password: hashedPassword }); try { - const user = await authModel.findOne({ email: email }); - if (user) res.json("Already Registerd"); + const user = await authModel.findOne({ email }); + if (user) res.json("Already Registered"); else { const savedUser = await newAuth.save(); res.send(savedUser); @@ -81,13 +87,11 @@ app.post("/register", authLimiter, async (req, res) => { } }); -//Google authentication using passport - +// Google auth app.get( "/google", passport.authenticate("google", { scope: ["profile", "email"] }) ); - app.get( "/google/callback", passport.authenticate("google", { @@ -96,10 +100,8 @@ app.get( }) ); -//For Facebook Authentication - +// Facebook auth app.get("/facebook", passport.authenticate("facebook", { scope: ["email"] })); - app.get( "/facebook/callback", passport.authenticate("facebook", { @@ -120,7 +122,7 @@ app.post( } ); -//logout +// Logout app.get("/logout", (req, res, next) => { req.logOut((err) => { if (err) res.send(err); @@ -128,36 +130,33 @@ app.get("/logout", (req, res, next) => { }); }); -app.get("/getUser", (req, res, next) => { - if (req.user) { - res.json(req.user); - } +// Get user +app.get("/getUser", (req, res) => { + if (req.user) res.json(req.user); }); -//Forgot and reset password // Reset Password app.post("/resetPassword/:id/:token", authLimiter, async (req, res) => { const { id, token } = req.params; - console.log(id); const { newPassword } = req.body; - jwt.verify(token, process.env.JWT_SECRET_KEY, async (err, encode) => { + + jwt.verify(token, process.env.JWT_SECRET_KEY, async (err) => { if (err) return res.send({ Status: "Try again after few minutes" }); - else { - const salt = await bcrypt.genSalt(10); - const hashedPassword = await bcrypt.hash(newPassword, salt); - authModel - .findByIdAndUpdate({ _id: id }, { password: hashedPassword }) - .then((u) => res.send({ Status: "success" })) - .catch((err) => res.send({ Status: err })); - } + const salt = await bcrypt.genSalt(10); + const hashedPassword = await bcrypt.hash(newPassword, salt); + authModel + .findByIdAndUpdate({ _id: id }, { password: hashedPassword }) + .then(() => res.send({ Status: "success" })) + .catch((err) => res.send({ Status: err })); }); }); // Forgot Password app.post("/forgotpass", authLimiter, async (req, res) => { const { email } = req.body; - await authModel.findOne({ email: email }).then((user) => { + await authModel.findOne({ email }).then((user) => { if (!user) return res.send({ Status: "Enter a valid email" }); + const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET_KEY, { expiresIn: "1d" }); @@ -179,25 +178,24 @@ app.post("/forgotpass", authLimiter, async (req, res) => { text: `${process.env.FRONTEND_DOMAIN}/ResetPass/${user._id}/${token}` }; - transporter.sendMail(mailOptions, function (error, info) { - if (error) { - console.log(error); - } else { - return res.send({ Status: "success" }); - } + transporter.sendMail(mailOptions, function (error) { + if (error) console.log(error); + else return res.send({ Status: "success" }); }); }); }); +// Authenticator middleware const authenticator = (req, res, next) => { - if (!req.isAuthenticated()) { + if (!req.isAuthenticated()) return res.status(401).json({ error: "Login Required" }); - } next(); }; -app.use("/todo", [authenticator, TodoRoutes]); -app.use("/note", [authenticator, NoteRoutes]); -app.use("/task", [authenticator, TaskRoutes]); + +// Apply rate limiter + authenticator to APIs +app.use("/todo", apiLimiter, authenticator, TodoRoutes); +app.use("/note", apiLimiter, authenticator, NoteRoutes); +app.use("/task", apiLimiter, authenticator, TaskRoutes); app.listen(PORT, () => { console.log(`Server Running On Port : ${PORT} `); From beefa3bf31d2bcca5d2e6d55e3fb6371ed91953b Mon Sep 17 00:00:00 2001 From: tinykav Date: Mon, 22 Sep 2025 10:59:43 +0530 Subject: [PATCH 12/21] Applied global limiter (100 requests / 15 min),Applied writeLimiter (20 writes / 10 min) for POST/PATCH/DELETE --- packageapp/backend/index.js | 152 ++++++++++++++++++------------------ 1 file changed, 77 insertions(+), 75 deletions(-) diff --git a/packageapp/backend/index.js b/packageapp/backend/index.js index 8175990..d4908f7 100644 --- a/packageapp/backend/index.js +++ b/packageapp/backend/index.js @@ -1,111 +1,113 @@ -const express = require('express') -const app = express() +const express = require("express"); +const app = express(); const port = process.env.PORT || 5000; -const cors = require('cors') +const cors = require("cors"); +const rateLimit = require("express-rate-limit"); // Added + +// ===== Rate Limiting ===== +// General limiter (all routes) +const apiLimiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 min + max: 100, // 100 requests per IP + message: { error: "Too many requests, please try again later." } +}); + +// Stricter limiter for write-heavy routes (POST/PATCH/DELETE) +const writeLimiter = rateLimit({ + windowMs: 10 * 60 * 1000, // 10 min + max: 20, // 20 writes per IP + message: { error: "Too many write requests, slow down." } +}); -//middleware +// ===== Middleware ===== app.use(cors()); app.use(express.json()); - +app.use(apiLimiter); // ✅ Global limiter applied to all requests //pw -user123 +app.get("/", (req, res) => { + res.send("Hello World!"); +}); -app.get('/', (req, res) => { - res.send('Hello World!') -}) - -//mongodb configuration - - -const { MongoClient, ServerApiVersion, ObjectId } = require('mongodb'); -const uri = "mongodb+srv://demo-user:user123@cluster0.aobbdul.mongodb.net/?retryWrites=true&w=majority&appName=Cluster0"; +// ===== MongoDB configuration ===== +const { MongoClient, ServerApiVersion, ObjectId } = require("mongodb"); +const uri = + "mongodb+srv://demo-user:user123@cluster0.aobbdul.mongodb.net/?retryWrites=true&w=majority&appName=Cluster0"; -// Create a MongoClient with a MongoClientOptions object to set the Stable API version const client = new MongoClient(uri, { serverApi: { version: ServerApiVersion.v1, strict: true, - deprecationErrors: true, + deprecationErrors: true } }); async function run() { try { - // Connect the client to the server (optional starting in v4.7) await client.connect(); - //create a collection of documents - const Packagecollection = await client.db("PackageInventory").collection("Packages"); + const Packagecollection = await client + .db("PackageInventory") + .collection("Packages"); - - //insert a package to the database post method - - app.post("/upload-Package",async(req,res) => { - const data = req.body; - const result = await Packagecollection.insertOne(data); - res.send(result); + // Insert a package + app.post("/upload-Package", writeLimiter, async (req, res) => { + // Protected + const data = req.body; + const result = await Packagecollection.insertOne(data); + res.send(result); }); + // Get all packages + app.get("/all-Packages", async (req, res) => { + const packages = Packagecollection.find(); + const result = await packages.toArray(); + res.send(result); + }); - //get all data from the database - - app.get("/all-Packages",async(reg,res)=>{ - const packages = Packagecollection.find(); - const result = await packages.toArray(); - res.send(result); - }) - - - //update a package data : patch or update methods - - app.patch("/Package/:id",async(req,res) => { - const id = req.params.id; - //console.log(id); - const updatePackageData = req.body; - const filter = {_id: new ObjectId(id)}; - const options = {upsert: true}; - const updateDoc ={ - $set: { - ...updatePackageData - } - } - - //update - const result = await Packagecollection.updateOne(filter,updateDoc,options ); - res.send(result); - }) - - - //delete package data - - app.delete("/Package/:id",async(req,res) => { - const id = req.params.id; - const filter = {_id: new ObjectId(id)}; - const result = await Packagecollection.deleteOne(filter); - res.send(result); - }) + // Update a package + app.patch("/Package/:id", writeLimiter, async (req, res) => { + // Protected + const id = req.params.id; + const updatePackageData = req.body; + const filter = { _id: new ObjectId(id) }; + const options = { upsert: true }; + const updateDoc = { $set: { ...updatePackageData } }; + const result = await Packagecollection.updateOne( + filter, + updateDoc, + options + ); + res.send(result); + }); - //To get single package data + // Delete package + app.delete("/Package/:id", writeLimiter, async (req, res) => { + // Protected + const id = req.params.id; + const filter = { _id: new ObjectId(id) }; + const result = await Packagecollection.deleteOne(filter); + res.send(result); + }); - app.get("/package/:id", async(req,res) => { + // Get single package + app.get("/package/:id", async (req, res) => { const id = req.params.id; - const filter = {_id:new ObjectId(id)}; + const filter = { _id: new ObjectId(id) }; const result = await Packagecollection.findOne(filter); res.send(result); - }) - - // Send a ping to confirm a successful connection + }); await client.db("admin").command({ ping: 1 }); - console.log("Pinged your deployment. You successfully connected to MongoDB!"); + console.log( + "Pinged your deployment. You successfully connected to MongoDB!" + ); } finally { - // Ensures that the client will close when you finish/error - //await client.close(); + // keep client open } } run().catch(console.dir); - app.listen(port, () => { - console.log(`Example app listening on port ${port}`) -}) \ No newline at end of file + console.log(`Example app listening on port ${port}`); +}); From cece69d7b8418ec88d146c0dc3dd61d6a1237ce7 Mon Sep 17 00:00:00 2001 From: tinykav Date: Mon, 22 Sep 2025 11:07:06 +0530 Subject: [PATCH 13/21] add rate limiting to user and review routes --- mainapp/backend/routes/review.js | 23 +++++++++++----- mainapp/backend/routes/user.js | 46 +++++++++++++++++++++++--------- 2 files changed, 51 insertions(+), 18 deletions(-) diff --git a/mainapp/backend/routes/review.js b/mainapp/backend/routes/review.js index 71098d2..1e1c0b7 100644 --- a/mainapp/backend/routes/review.js +++ b/mainapp/backend/routes/review.js @@ -1,6 +1,17 @@ -import express from 'express' -import { createReview } from './../controlles/reviewController.js'; -import { verifyUser } from '../utils/verifyToken.js'; -const router = express.Router();//const router=express.Router() -router.post('/:tourId',verifyUser,createReview) -export default router; \ No newline at end of file +import express from "express"; +import rateLimit from "express-rate-limit"; +import { createReview } from "./../controlles/reviewController.js"; +import { verifyUser } from "../utils/verifyToken.js"; + +const router = express.Router(); + +// Rate limiter for review creation (max 10 requests per 15 minutes per IP) +const reviewLimiter = rateLimit({ + windowMs: 15 * 60 * 1000, + max: 10, + message: { error: "Too many review attempts, please try again later." } +}); + +router.post("/:tourId", verifyUser, reviewLimiter, createReview); + +export default router; diff --git a/mainapp/backend/routes/user.js b/mainapp/backend/routes/user.js index aff6e60..f1d03f8 100644 --- a/mainapp/backend/routes/user.js +++ b/mainapp/backend/routes/user.js @@ -1,17 +1,39 @@ -import express from 'express' -import { updateUser,deleteUser,getSingleUser,getAllUser } from '../controlles/userController.js' +import express from "express"; +import rateLimit from "express-rate-limit"; +import { + updateUser, + deleteUser, + getSingleUser, + getAllUser +} from "../controlles/userController.js"; +import { verifyAdmin, verifyUser } from "../utils/verifyToken.js"; const router = express.Router(); -import { verifyAdmin, verifyUser } from '../utils/verifyToken.js'; -//update User -router.put('/:id',verifyUser,updateUser); -//delete User -router.delete('/:id',verifyUser,deleteUser); +// General limiter for user actions +const userLimiter = rateLimit({ + windowMs: 15 * 60 * 1000, + max: 20, + message: { error: "Too many requests, please slow down." } +}); -//get Single User -router.get('/:id',verifyUser,getSingleUser); +// Stricter limiter for sensitive actions (update/delete) +const sensitiveLimiter = rateLimit({ + windowMs: 15 * 60 * 1000, + max: 5, + message: { error: "Too many attempts, please try again later." } +}); -//get All User -router.get('/',verifyAdmin,getAllUser); -export default router; \ No newline at end of file +// Update User +router.put("/:id", verifyUser, sensitiveLimiter, updateUser); + +// Delete User +router.delete("/:id", verifyUser, sensitiveLimiter, deleteUser); + +// Get Single User +router.get("/:id", verifyUser, userLimiter, getSingleUser); + +// Get All Users (admin only, also rate-limited) +router.get("/", verifyAdmin, userLimiter, getAllUser); + +export default router; From 30d68ba89118224ee083dee1d265dc9cc6345b3e Mon Sep 17 00:00:00 2001 From: tinykav Date: Mon, 22 Sep 2025 11:13:47 +0530 Subject: [PATCH 14/21] add rate limiting to auth routes (login/register), tour and booking routes to prevent abuse --- mainapp/backend/routes/auth.js | 21 +++++++++++++------ mainapp/backend/routes/booking.js | 35 +++++++++++++++++++++++-------- mainapp/backend/routes/tour.js | 28 +++++++++++++------------ 3 files changed, 56 insertions(+), 28 deletions(-) diff --git a/mainapp/backend/routes/auth.js b/mainapp/backend/routes/auth.js index cdf6b31..e2ad48d 100644 --- a/mainapp/backend/routes/auth.js +++ b/mainapp/backend/routes/auth.js @@ -1,9 +1,18 @@ -import express from 'express'; - -import { register,login } from './../controlles/authController.js' +import express from "express"; +import rateLimit from "express-rate-limit"; +import { register, login } from "./../controlles/authController.js"; const router = express.Router(); -router.post('/register',register); -router.post('/login',login); -export default router; \ No newline at end of file +// Rate limiter for login/register endpoints +const authLimiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 5, // only 5 attempts per IP per window + message: { error: "Too many attempts, please try again later." } +}); + +// Apply limiter only to sensitive routes +router.post("/register", authLimiter, register); +router.post("/login", authLimiter, login); + +export default router; diff --git a/mainapp/backend/routes/booking.js b/mainapp/backend/routes/booking.js index d736722..0766cad 100644 --- a/mainapp/backend/routes/booking.js +++ b/mainapp/backend/routes/booking.js @@ -1,11 +1,28 @@ -import express from 'express' -import { createBooking, getAllBooking, getBooking } from './../controlles/bookingController.js'; -import { verifyAdmin, verifyUser } from '../utils/verifyToken.js'; -import { get } from 'mongoose'; -const router = express.Router();//const router=express.Router() -router.post('/',createBooking); -router.get('/',verifyUser,getBooking); -router.get('/',verifyAdmin,getAllBooking); +import express from "express"; +import rateLimit from "express-rate-limit"; +import { + createBooking, + getAllBooking, + getBooking +} from "./../controlles/bookingController.js"; +import { verifyAdmin, verifyUser } from "../utils/verifyToken.js"; +const router = express.Router(); -export default router; \ No newline at end of file +// Rate limiter for bookings (stricter, since these are sensitive operations) +const bookingLimiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 10, // limit to 10 booking requests per IP + message: { error: "Too many booking attempts, please try again later." } +}); + +// Create a booking (user action) +router.post("/", verifyUser, bookingLimiter, createBooking); + +// Get bookings (user sees their own bookings) +router.get("/", verifyUser, bookingLimiter, getBooking); + +// Admin gets all bookings +router.get("/all", verifyAdmin, bookingLimiter, getAllBooking); + +export default router; diff --git a/mainapp/backend/routes/tour.js b/mainapp/backend/routes/tour.js index 6279b1b..0f1e961 100644 --- a/mainapp/backend/routes/tour.js +++ b/mainapp/backend/routes/tour.js @@ -1,4 +1,5 @@ import express from "express"; +import rateLimit from "express-rate-limit"; import { createTour, updateTour, @@ -7,32 +8,33 @@ import { getAllTour, getTourBysearch, getFeaturedTour, - getTourCount, + getTourCount } from "./../controlles/tourController.js"; import { verifyAdmin } from "../utils/verifyToken.js"; const router = express.Router(); +// Rate limiter for admin-modifying routes (POST/PUT/DELETE) +const adminLimiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 20, // allow 20 requests per IP + message: { error: "Too many admin requests, please try again later." } +}); + //create new tour -router.post("/", verifyAdmin, createTour); +router.post("/", verifyAdmin, adminLimiter, createTour); + //update tour -router.put("/:id", verifyAdmin, updateTour); -// //delete tour +router.put("/:id", verifyAdmin, adminLimiter, updateTour); -router.delete("/:id", verifyAdmin, deleteTour); +//delete tour +router.delete("/:id", verifyAdmin, adminLimiter, deleteTour); -// //getSingleTour +// Public routes (kept unthrottled or you can add a general limiter if needed) router.get("/:id", getSingleTour); - -// //getAllTour router.get("/", getAllTour); - -//get tour by search router.get("/search/getTourBySearch", getTourBysearch); - -// //get tour by search router.get("/search/getFeaturedTours", getFeaturedTour); -// //get tour by search router.get("/search/getTourCount", getTourCount); export default router; From bbef07e14911d074eeadd4a76874e48a935f4ebd Mon Sep 17 00:00:00 2001 From: tinykav Date: Mon, 22 Sep 2025 13:16:29 +0530 Subject: [PATCH 15/21] updated tour, booking, and user routes --- mainapp/backend/routes/booking.js | 23 +++++++++++++---------- mainapp/backend/routes/tour.js | 31 +++++++++++++++++-------------- mainapp/backend/routes/user.js | 12 +++--------- 3 files changed, 33 insertions(+), 33 deletions(-) diff --git a/mainapp/backend/routes/booking.js b/mainapp/backend/routes/booking.js index 0766cad..cddfff8 100644 --- a/mainapp/backend/routes/booking.js +++ b/mainapp/backend/routes/booking.js @@ -9,20 +9,23 @@ import { verifyAdmin, verifyUser } from "../utils/verifyToken.js"; const router = express.Router(); -// Rate limiter for bookings (stricter, since these are sensitive operations) +// Booking limiter (strict) const bookingLimiter = rateLimit({ - windowMs: 15 * 60 * 1000, // 15 minutes - max: 10, // limit to 10 booking requests per IP + windowMs: 15 * 60 * 1000, + max: 10, message: { error: "Too many booking attempts, please try again later." } }); -// Create a booking (user action) -router.post("/", verifyUser, bookingLimiter, createBooking); - -// Get bookings (user sees their own bookings) -router.get("/", verifyUser, bookingLimiter, getBooking); +// Public GET limiter (for database reads) +const readLimiter = rateLimit({ + windowMs: 15 * 60 * 1000, + max: 100, + message: { error: "Too many requests, please try again later." } +}); -// Admin gets all bookings -router.get("/all", verifyAdmin, bookingLimiter, getAllBooking); +// Routes +router.post("/", verifyUser, bookingLimiter, createBooking); +router.get("/", verifyUser, readLimiter, getBooking); // user’s own bookings +router.get("/all", verifyAdmin, readLimiter, getAllBooking); // admin all bookings export default router; diff --git a/mainapp/backend/routes/tour.js b/mainapp/backend/routes/tour.js index 0f1e961..c59b79c 100644 --- a/mainapp/backend/routes/tour.js +++ b/mainapp/backend/routes/tour.js @@ -14,27 +14,30 @@ import { verifyAdmin } from "../utils/verifyToken.js"; const router = express.Router(); -// Rate limiter for admin-modifying routes (POST/PUT/DELETE) +// Admin limiter (strict) const adminLimiter = rateLimit({ - windowMs: 15 * 60 * 1000, // 15 minutes - max: 20, // allow 20 requests per IP + windowMs: 15 * 60 * 1000, + max: 20, message: { error: "Too many admin requests, please try again later." } }); -//create new tour -router.post("/", verifyAdmin, adminLimiter, createTour); +// Public GET limiter (generous) +const readLimiter = rateLimit({ + windowMs: 15 * 60 * 1000, + max: 100, + message: { error: "Too many requests, please try again later." } +}); -//update tour +// Create / update / delete tours (admin only) +router.post("/", verifyAdmin, adminLimiter, createTour); router.put("/:id", verifyAdmin, adminLimiter, updateTour); - -//delete tour router.delete("/:id", verifyAdmin, adminLimiter, deleteTour); -// Public routes (kept unthrottled or you can add a general limiter if needed) -router.get("/:id", getSingleTour); -router.get("/", getAllTour); -router.get("/search/getTourBySearch", getTourBysearch); -router.get("/search/getFeaturedTours", getFeaturedTour); -router.get("/search/getTourCount", getTourCount); +// Public read routes (rate-limited) +router.get("/:id", readLimiter, getSingleTour); +router.get("/", readLimiter, getAllTour); +router.get("/search/getTourBySearch", readLimiter, getTourBysearch); +router.get("/search/getFeaturedTours", readLimiter, getFeaturedTour); +router.get("/search/getTourCount", readLimiter, getTourCount); export default router; diff --git a/mainapp/backend/routes/user.js b/mainapp/backend/routes/user.js index f1d03f8..9d17d70 100644 --- a/mainapp/backend/routes/user.js +++ b/mainapp/backend/routes/user.js @@ -10,30 +10,24 @@ import { verifyAdmin, verifyUser } from "../utils/verifyToken.js"; const router = express.Router(); -// General limiter for user actions +// General limiter const userLimiter = rateLimit({ windowMs: 15 * 60 * 1000, max: 20, message: { error: "Too many requests, please slow down." } }); -// Stricter limiter for sensitive actions (update/delete) +// Stricter limiter for sensitive ops const sensitiveLimiter = rateLimit({ windowMs: 15 * 60 * 1000, max: 5, message: { error: "Too many attempts, please try again later." } }); -// Update User +// Routes router.put("/:id", verifyUser, sensitiveLimiter, updateUser); - -// Delete User router.delete("/:id", verifyUser, sensitiveLimiter, deleteUser); - -// Get Single User router.get("/:id", verifyUser, userLimiter, getSingleUser); - -// Get All Users (admin only, also rate-limited) router.get("/", verifyAdmin, userLimiter, getAllUser); export default router; From dd452b99c16e6273b46ea5deb314517d3b12652e Mon Sep 17 00:00:00 2001 From: tinykav Date: Mon, 22 Sep 2025 20:50:11 +0530 Subject: [PATCH 16/21] add per-route rate limiting to todo, task, and note APIs --- taskapp/backend/Routes/NoteRoutes.js | 63 +++++++++++++-------------- taskapp/backend/Routes/TaskRoutes.js | 63 +++++++++++++-------------- taskapp/backend/Routes/TodoRoutes.js | 65 +++++++++++++--------------- 3 files changed, 90 insertions(+), 101 deletions(-) diff --git a/taskapp/backend/Routes/NoteRoutes.js b/taskapp/backend/Routes/NoteRoutes.js index 5bb760f..57fb99c 100644 --- a/taskapp/backend/Routes/NoteRoutes.js +++ b/taskapp/backend/Routes/NoteRoutes.js @@ -1,55 +1,52 @@ -const noteRoutes = require("express").Router(); +const express = require("express"); +const rateLimit = require("express-rate-limit"); const dataModel = require("../Models/DataModel"); -noteRoutes.get("/getNote", async (req, res) => { +const noteRoutes = express.Router(); + +// Rate limiters +const readLimiter = rateLimit({ + windowMs: 15 * 60 * 1000, + max: 100, + message: { error: "Too many requests, please slow down." } +}); + +const writeLimiter = rateLimit({ + windowMs: 15 * 60 * 1000, + max: 20, + message: { error: "Too many modifications, please try again later." } +}); + +// Routes +noteRoutes.get("/getNote", readLimiter, async (req, res) => { const { _id } = req.user; - const newNote = new dataModel({ - _id: _id, - }); let note = await dataModel.findById(_id); - if (!note) note = await newNote.save(); - console.log(note.notes); + if (!note) note = await new dataModel({ _id }).save(); res.json(note.notes); }); -noteRoutes.post("/postNote", async (req, res) => { +noteRoutes.post("/postNote", writeLimiter, async (req, res) => { const { _id } = req.user; const note = req.body; - await dataModel - .findByIdAndUpdate({ _id: _id }, { $push: { notes: note } }) - .catch((err) => { - console.log(err); - }); + await dataModel.findByIdAndUpdate({ _id }, { $push: { notes: note } }); res.json({ success: "Posted Successfully" }); }); -noteRoutes.patch("/updateNote/:id", async (req, res) => { +noteRoutes.patch("/updateNote/:id", writeLimiter, async (req, res) => { const { id } = req.params; const { newText } = req.body; - await dataModel - .findOneAndUpdate( - { "notes.id": id }, - { - $set: { - "notes.$.noteText": newText, - }, - }, - { new: true } - ) - .catch((err) => { - console.log(err); - }); + await dataModel.findOneAndUpdate( + { "notes.id": id }, + { $set: { "notes.$.noteText": newText } }, + { new: true } + ); res.json({ success: "Updated successfully" }); }); -noteRoutes.delete("/deleteNote/:id", async (req, res) => { +noteRoutes.delete("/deleteNote/:id", writeLimiter, async (req, res) => { const { _id } = req.user; const { id } = req.params; - await dataModel - .findByIdAndUpdate(_id, { $pull: { notes: { id: id } } }) - .catch((err) => { - console.log(err); - }); + await dataModel.findByIdAndUpdate(_id, { $pull: { notes: { id } } }); res.json({ success: "Deleted successfully" }); }); diff --git a/taskapp/backend/Routes/TaskRoutes.js b/taskapp/backend/Routes/TaskRoutes.js index ada367c..6dd5b6a 100644 --- a/taskapp/backend/Routes/TaskRoutes.js +++ b/taskapp/backend/Routes/TaskRoutes.js @@ -1,55 +1,52 @@ -const taskRoutes = require("express").Router(); +const express = require("express"); +const rateLimit = require("express-rate-limit"); const dataModel = require("../Models/DataModel"); -taskRoutes.get("/getTask", async (req, res) => { +const taskRoutes = express.Router(); + +// Rate limiters +const readLimiter = rateLimit({ + windowMs: 15 * 60 * 1000, + max: 100, + message: { error: "Too many requests, please slow down." } +}); + +const writeLimiter = rateLimit({ + windowMs: 15 * 60 * 1000, + max: 20, + message: { error: "Too many modifications, please try again later." } +}); + +// Routes +taskRoutes.get("/getTask", readLimiter, async (req, res) => { const { _id } = req.user; - const newTask = new dataModel({ - _id: _id, - }); let task = await dataModel.findById(_id); - if (!task) task = await newTask.save(); - console.log(task.tasks); + if (!task) task = await new dataModel({ _id }).save(); res.json(task.tasks); }); -taskRoutes.post("/postTask", async (req, res) => { +taskRoutes.post("/postTask", writeLimiter, async (req, res) => { const { _id } = req.user; const newTask = req.body; - await dataModel - .findByIdAndUpdate({ _id: _id }, { $push: { tasks: newTask } }) - .catch((err) => { - console.log(err); - }); + await dataModel.findByIdAndUpdate({ _id }, { $push: { tasks: newTask } }); res.json({ success: "Posted Successfully" }); }); -taskRoutes.patch("/updateTask/:id", async (req, res) => { +taskRoutes.patch("/updateTask/:id", writeLimiter, async (req, res) => { const { id } = req.params; const { done } = req.body; - await dataModel - .findOneAndUpdate( - { "tasks.id": id }, - { - $set: { - "tasks.$.done": done, - }, - }, - { new: true } - ) - .catch((err) => { - console.log(err); - }); + await dataModel.findOneAndUpdate( + { "tasks.id": id }, + { $set: { "tasks.$.done": done } }, + { new: true } + ); res.json({ success: "Updated successfully" }); }); -taskRoutes.delete("/deleteTask/:id", async (req, res) => { +taskRoutes.delete("/deleteTask/:id", writeLimiter, async (req, res) => { const { _id } = req.user; const { id } = req.params; - await dataModel - .findByIdAndUpdate(_id, { $pull: { tasks: { id: id } } }) - .catch((err) => { - console.log(err); - }); + await dataModel.findByIdAndUpdate(_id, { $pull: { tasks: { id } } }); res.json({ success: "Deleted successfully" }); }); diff --git a/taskapp/backend/Routes/TodoRoutes.js b/taskapp/backend/Routes/TodoRoutes.js index 0623841..670d137 100644 --- a/taskapp/backend/Routes/TodoRoutes.js +++ b/taskapp/backend/Routes/TodoRoutes.js @@ -1,57 +1,52 @@ -const todoRoutes = require("express").Router(); +const express = require("express"); +const rateLimit = require("express-rate-limit"); const dataModel = require("../Models/DataModel"); -todoRoutes.get("/getTodo", async (req, res) => { +const todoRoutes = express.Router(); + +// Rate limiters +const readLimiter = rateLimit({ + windowMs: 15 * 60 * 1000, + max: 100, + message: { error: "Too many requests, please slow down." } +}); + +const writeLimiter = rateLimit({ + windowMs: 15 * 60 * 1000, + max: 20, + message: { error: "Too many modifications, please try again later." } +}); + +// Routes +todoRoutes.get("/getTodo", readLimiter, async (req, res) => { const { _id } = req.user; - // console.log("req.user", req.user); - const newTodo = new dataModel({ - _id: _id, - }); let todo = await dataModel.findById(_id); - if (!todo) todo = await newTodo.save(); - console.log(todo.todos); + if (!todo) todo = await new dataModel({ _id }).save(); res.json(todo.todos); }); -todoRoutes.post("/postTodo", async (req, res) => { +todoRoutes.post("/postTodo", writeLimiter, async (req, res) => { const { _id } = req.user; const todo = req.body; - await dataModel - .findByIdAndUpdate({ _id: _id }, { $push: { todos: todo } }) - .catch((err) => { - console.log(err); - }); + await dataModel.findByIdAndUpdate({ _id }, { $push: { todos: todo } }); res.json({ success: "Posted Successfully" }); }); -todoRoutes.patch("/updateTodo/:todoId", async (req, res) => { - // const { _id } = req.user; +todoRoutes.patch("/updateTodo/:todoId", writeLimiter, async (req, res) => { const { todoId } = req.params; const { status } = req.body; - await dataModel - .findOneAndUpdate( - { "todos.todoId": todoId }, - { - $set: { - "todos.$.status": status, - }, - }, - { new: true } - ) - .catch((err) => { - console.log(err); - }); + await dataModel.findOneAndUpdate( + { "todos.todoId": todoId }, + { $set: { "todos.$.status": status } }, + { new: true } + ); res.json({ success: "Updated successfully" }); }); -todoRoutes.delete("/deleteTodo/:todoId", async (req, res) => { +todoRoutes.delete("/deleteTodo/:todoId", writeLimiter, async (req, res) => { const { _id } = req.user; const { todoId } = req.params; - await dataModel - .findByIdAndUpdate(_id, { $pull: { todos: { todoId: todoId } } }) - .catch((err) => { - console.log(err); - }); + await dataModel.findByIdAndUpdate(_id, { $pull: { todos: { todoId } } }); res.json({ success: "Deleted successfully" }); }); From 6cf88cdf4f4ffc2ad51704001cda7fc8748d999c Mon Sep 17 00:00:00 2001 From: tinykav Date: Mon, 22 Sep 2025 21:04:44 +0530 Subject: [PATCH 17/21] updated taskapp routes --- taskapp/backend/Routes/NoteRoutes.js | 27 ++++++++++++-------------- taskapp/backend/Routes/TaskRoutes.js | 27 ++++++++++++-------------- taskapp/backend/Routes/TodoRoutes.js | 29 +++++++++++++--------------- 3 files changed, 37 insertions(+), 46 deletions(-) diff --git a/taskapp/backend/Routes/NoteRoutes.js b/taskapp/backend/Routes/NoteRoutes.js index 57fb99c..ee12c8a 100644 --- a/taskapp/backend/Routes/NoteRoutes.js +++ b/taskapp/backend/Routes/NoteRoutes.js @@ -4,35 +4,31 @@ const dataModel = require("../Models/DataModel"); const noteRoutes = express.Router(); -// Rate limiters -const readLimiter = rateLimit({ +// Per-route limiter +const noteLimiter = rateLimit({ windowMs: 15 * 60 * 1000, - max: 100, - message: { error: "Too many requests, please slow down." } + max: 50, + message: { error: "Too many requests to Note API, please slow down." } }); -const writeLimiter = rateLimit({ - windowMs: 15 * 60 * 1000, - max: 20, - message: { error: "Too many modifications, please try again later." } -}); - -// Routes -noteRoutes.get("/getNote", readLimiter, async (req, res) => { +// GET all Notes +noteRoutes.get("/getNote", noteLimiter, async (req, res) => { const { _id } = req.user; let note = await dataModel.findById(_id); if (!note) note = await new dataModel({ _id }).save(); res.json(note.notes); }); -noteRoutes.post("/postNote", writeLimiter, async (req, res) => { +// POST a new Note +noteRoutes.post("/postNote", noteLimiter, async (req, res) => { const { _id } = req.user; const note = req.body; await dataModel.findByIdAndUpdate({ _id }, { $push: { notes: note } }); res.json({ success: "Posted Successfully" }); }); -noteRoutes.patch("/updateNote/:id", writeLimiter, async (req, res) => { +// PATCH update Note +noteRoutes.patch("/updateNote/:id", noteLimiter, async (req, res) => { const { id } = req.params; const { newText } = req.body; await dataModel.findOneAndUpdate( @@ -43,7 +39,8 @@ noteRoutes.patch("/updateNote/:id", writeLimiter, async (req, res) => { res.json({ success: "Updated successfully" }); }); -noteRoutes.delete("/deleteNote/:id", writeLimiter, async (req, res) => { +// DELETE Note +noteRoutes.delete("/deleteNote/:id", noteLimiter, async (req, res) => { const { _id } = req.user; const { id } = req.params; await dataModel.findByIdAndUpdate(_id, { $pull: { notes: { id } } }); diff --git a/taskapp/backend/Routes/TaskRoutes.js b/taskapp/backend/Routes/TaskRoutes.js index 6dd5b6a..72bec83 100644 --- a/taskapp/backend/Routes/TaskRoutes.js +++ b/taskapp/backend/Routes/TaskRoutes.js @@ -4,35 +4,31 @@ const dataModel = require("../Models/DataModel"); const taskRoutes = express.Router(); -// Rate limiters -const readLimiter = rateLimit({ +// Per-route limiter +const taskLimiter = rateLimit({ windowMs: 15 * 60 * 1000, - max: 100, - message: { error: "Too many requests, please slow down." } + max: 50, + message: { error: "Too many requests to Task API, please slow down." } }); -const writeLimiter = rateLimit({ - windowMs: 15 * 60 * 1000, - max: 20, - message: { error: "Too many modifications, please try again later." } -}); - -// Routes -taskRoutes.get("/getTask", readLimiter, async (req, res) => { +// GET all Tasks +taskRoutes.get("/getTask", taskLimiter, async (req, res) => { const { _id } = req.user; let task = await dataModel.findById(_id); if (!task) task = await new dataModel({ _id }).save(); res.json(task.tasks); }); -taskRoutes.post("/postTask", writeLimiter, async (req, res) => { +// POST a new Task +taskRoutes.post("/postTask", taskLimiter, async (req, res) => { const { _id } = req.user; const newTask = req.body; await dataModel.findByIdAndUpdate({ _id }, { $push: { tasks: newTask } }); res.json({ success: "Posted Successfully" }); }); -taskRoutes.patch("/updateTask/:id", writeLimiter, async (req, res) => { +// PATCH update Task +taskRoutes.patch("/updateTask/:id", taskLimiter, async (req, res) => { const { id } = req.params; const { done } = req.body; await dataModel.findOneAndUpdate( @@ -43,7 +39,8 @@ taskRoutes.patch("/updateTask/:id", writeLimiter, async (req, res) => { res.json({ success: "Updated successfully" }); }); -taskRoutes.delete("/deleteTask/:id", writeLimiter, async (req, res) => { +// DELETE Task +taskRoutes.delete("/deleteTask/:id", taskLimiter, async (req, res) => { const { _id } = req.user; const { id } = req.params; await dataModel.findByIdAndUpdate(_id, { $pull: { tasks: { id } } }); diff --git a/taskapp/backend/Routes/TodoRoutes.js b/taskapp/backend/Routes/TodoRoutes.js index 670d137..f8f4160 100644 --- a/taskapp/backend/Routes/TodoRoutes.js +++ b/taskapp/backend/Routes/TodoRoutes.js @@ -4,35 +4,31 @@ const dataModel = require("../Models/DataModel"); const todoRoutes = express.Router(); -// Rate limiters -const readLimiter = rateLimit({ - windowMs: 15 * 60 * 1000, - max: 100, - message: { error: "Too many requests, please slow down." } +// Per-route limiter +const todoLimiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 50, // 50 requests per IP + message: { error: "Too many requests to Todo API, please slow down." } }); -const writeLimiter = rateLimit({ - windowMs: 15 * 60 * 1000, - max: 20, - message: { error: "Too many modifications, please try again later." } -}); - -// Routes -todoRoutes.get("/getTodo", readLimiter, async (req, res) => { +// GET all Todos +todoRoutes.get("/getTodo", todoLimiter, async (req, res) => { const { _id } = req.user; let todo = await dataModel.findById(_id); if (!todo) todo = await new dataModel({ _id }).save(); res.json(todo.todos); }); -todoRoutes.post("/postTodo", writeLimiter, async (req, res) => { +// POST a new Todo +todoRoutes.post("/postTodo", todoLimiter, async (req, res) => { const { _id } = req.user; const todo = req.body; await dataModel.findByIdAndUpdate({ _id }, { $push: { todos: todo } }); res.json({ success: "Posted Successfully" }); }); -todoRoutes.patch("/updateTodo/:todoId", writeLimiter, async (req, res) => { +// PATCH update Todo +todoRoutes.patch("/updateTodo/:todoId", todoLimiter, async (req, res) => { const { todoId } = req.params; const { status } = req.body; await dataModel.findOneAndUpdate( @@ -43,7 +39,8 @@ todoRoutes.patch("/updateTodo/:todoId", writeLimiter, async (req, res) => { res.json({ success: "Updated successfully" }); }); -todoRoutes.delete("/deleteTodo/:todoId", writeLimiter, async (req, res) => { +// DELETE Todo +todoRoutes.delete("/deleteTodo/:todoId", todoLimiter, async (req, res) => { const { _id } = req.user; const { todoId } = req.params; await dataModel.findByIdAndUpdate(_id, { $pull: { todos: { todoId } } }); From 24ae889fad1a5f76f41f7ca098354d26296baa3f Mon Sep 17 00:00:00 2001 From: tinykav Date: Mon, 22 Sep 2025 23:21:14 +0530 Subject: [PATCH 18/21] stop flagging Sensitive Data Exposure in both feedbackapp and packageapp --- feedbackapp/backend/.gitignore | 3 ++- feedbackapp/backend/server.js | 34 +++++++++++++++++----------------- packageapp/backend/index.js | 31 +++++++++++-------------------- 3 files changed, 30 insertions(+), 38 deletions(-) diff --git a/feedbackapp/backend/.gitignore b/feedbackapp/backend/.gitignore index 30bc162..7af7f04 100644 --- a/feedbackapp/backend/.gitignore +++ b/feedbackapp/backend/.gitignore @@ -1 +1,2 @@ -/node_modules \ No newline at end of file +/node_modules +.env \ No newline at end of file diff --git a/feedbackapp/backend/server.js b/feedbackapp/backend/server.js index f23fe7c..3193ca6 100644 --- a/feedbackapp/backend/server.js +++ b/feedbackapp/backend/server.js @@ -1,33 +1,33 @@ -const express = require('express'); +const express = require("express"); const app = express(); -const cors = require('cors'); +const cors = require("cors"); const port = 3001; -const host = 'localhost'; -const mongoose = require('mongoose'); -const router = require('./routes/feedbackRouter'); +const host = "localhost"; +const mongoose = require("mongoose"); +const router = require("./routes/feedbackRouter"); app.use(cors()); //cors origin unblocking(cross origine resoures sharing) app.use(express.json()); -const uri = 'mongodb+srv://sunera:feedback1211@cluster0.j2j5esj.mongodb.net/feedback_db?retryWrites=true&w=majority&appName=Cluster0'; +// Use environment variable for MongoDB URI +const uri = process.env.MONGO_URI; + const connect = async () => { - try { - await mongoose.connect(uri); - console.log('connected to mongoDB'); - } catch (error) { - console.log('mongoDB error: ',error); - - } + try { + await mongoose.connect(uri); + console.log("connected to mongoDB"); + } catch (error) { + console.log("mongoDB error: ", error); + } }; connect(); //call back function -const server = app.listen(port,host, () => { - console.log(`Node server is listing to ${server.address().port}`) //check actually working sever? - +const server = app.listen(port, host, () => { + console.log(`Node server is listing to ${server.address().port}`); //check actually working sever? }); -app.use('/api',router); \ No newline at end of file +app.use("/api", router); diff --git a/packageapp/backend/index.js b/packageapp/backend/index.js index d4908f7..c670c1e 100644 --- a/packageapp/backend/index.js +++ b/packageapp/backend/index.js @@ -1,38 +1,34 @@ const express = require("express"); const app = express(); -const port = process.env.PORT || 5000; const cors = require("cors"); -const rateLimit = require("express-rate-limit"); // Added +const rateLimit = require("express-rate-limit"); +require("dotenv").config(); // 🔹 Load environment variables +const { MongoClient, ServerApiVersion, ObjectId } = require("mongodb"); + +const port = process.env.PORT || 5000; // ===== Rate Limiting ===== // General limiter (all routes) const apiLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 min - max: 100, // 100 requests per IP + max: 100, message: { error: "Too many requests, please try again later." } }); -// Stricter limiter for write-heavy routes (POST/PATCH/DELETE) +// Stricter limiter for write-heavy routes const writeLimiter = rateLimit({ windowMs: 10 * 60 * 1000, // 10 min - max: 20, // 20 writes per IP + max: 20, message: { error: "Too many write requests, slow down." } }); // ===== Middleware ===== app.use(cors()); app.use(express.json()); -app.use(apiLimiter); // ✅ Global limiter applied to all requests - -//pw -user123 -app.get("/", (req, res) => { - res.send("Hello World!"); -}); +app.use(apiLimiter); // ✅ Apply global limiter // ===== MongoDB configuration ===== -const { MongoClient, ServerApiVersion, ObjectId } = require("mongodb"); -const uri = - "mongodb+srv://demo-user:user123@cluster0.aobbdul.mongodb.net/?retryWrites=true&w=majority&appName=Cluster0"; +const uri = process.env.MONGO_URI; // 🔹 No hardcoded credentials const client = new MongoClient(uri, { serverApi: { @@ -52,7 +48,6 @@ async function run() { // Insert a package app.post("/upload-Package", writeLimiter, async (req, res) => { - // Protected const data = req.body; const result = await Packagecollection.insertOne(data); res.send(result); @@ -67,7 +62,6 @@ async function run() { // Update a package app.patch("/Package/:id", writeLimiter, async (req, res) => { - // Protected const id = req.params.id; const updatePackageData = req.body; const filter = { _id: new ObjectId(id) }; @@ -83,7 +77,6 @@ async function run() { // Delete package app.delete("/Package/:id", writeLimiter, async (req, res) => { - // Protected const id = req.params.id; const filter = { _id: new ObjectId(id) }; const result = await Packagecollection.deleteOne(filter); @@ -99,9 +92,7 @@ async function run() { }); await client.db("admin").command({ ping: 1 }); - console.log( - "Pinged your deployment. You successfully connected to MongoDB!" - ); + console.log("✅ MongoDB connection successful!"); } finally { // keep client open } From a3ff70a78cffbe4dd4d663fb000b29e9bcee2ec8 Mon Sep 17 00:00:00 2001 From: tinykav Date: Mon, 22 Sep 2025 23:39:36 +0530 Subject: [PATCH 19/21] change read me file --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index f27e98b..89e449a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,3 @@ -# tinyyyy - # Blissify--Wedding-Management-System SLIIT 2nd year second semester ITP module project using MERN stack technology for a wedding management system. From 8677554e80f6875117c6e7afbee9a4dcc7486675 Mon Sep 17 00:00:00 2001 From: tinykav Date: Tue, 23 Sep 2025 00:00:57 +0530 Subject: [PATCH 20/21] apply rate limiting before authorization middleware --- mainapp/backend/routes/booking.js | 22 ++++++++++++++-------- mainapp/backend/routes/review.js | 3 ++- mainapp/backend/routes/tour.js | 11 ++++++++--- mainapp/backend/routes/user.js | 12 ++++++++---- 4 files changed, 32 insertions(+), 16 deletions(-) diff --git a/mainapp/backend/routes/booking.js b/mainapp/backend/routes/booking.js index cddfff8..44e44dc 100644 --- a/mainapp/backend/routes/booking.js +++ b/mainapp/backend/routes/booking.js @@ -9,23 +9,29 @@ import { verifyAdmin, verifyUser } from "../utils/verifyToken.js"; const router = express.Router(); -// Booking limiter (strict) +// Booking limiter (strict - protects writes) const bookingLimiter = rateLimit({ - windowMs: 15 * 60 * 1000, - max: 10, + windowMs: 15 * 60 * 1000, // 15 minutes + max: 10, // allow max 10 requests per IP message: { error: "Too many booking attempts, please try again later." } }); -// Public GET limiter (for database reads) +// Public GET limiter (for read-only database operations) const readLimiter = rateLimit({ windowMs: 15 * 60 * 1000, max: 100, message: { error: "Too many requests, please try again later." } }); -// Routes -router.post("/", verifyUser, bookingLimiter, createBooking); -router.get("/", verifyUser, readLimiter, getBooking); // user’s own bookings -router.get("/all", verifyAdmin, readLimiter, getAllBooking); // admin all bookings +// ===== Routes ===== + +// Create a booking (rate limit first → then verify → then controller) +router.post("/", bookingLimiter, verifyUser, createBooking); + +// Get bookings (user's own bookings) +router.get("/", readLimiter, verifyUser, getBooking); + +// Get all bookings (admin only) +router.get("/all", readLimiter, verifyAdmin, getAllBooking); export default router; diff --git a/mainapp/backend/routes/review.js b/mainapp/backend/routes/review.js index 1e1c0b7..dd5e98e 100644 --- a/mainapp/backend/routes/review.js +++ b/mainapp/backend/routes/review.js @@ -12,6 +12,7 @@ const reviewLimiter = rateLimit({ message: { error: "Too many review attempts, please try again later." } }); -router.post("/:tourId", verifyUser, reviewLimiter, createReview); +//Rate limiter comes BEFORE verifyUser → fixes CodeQL warning +router.post("/:tourId", reviewLimiter, verifyUser, createReview); export default router; diff --git a/mainapp/backend/routes/tour.js b/mainapp/backend/routes/tour.js index c59b79c..a89c009 100644 --- a/mainapp/backend/routes/tour.js +++ b/mainapp/backend/routes/tour.js @@ -28,10 +28,15 @@ const readLimiter = rateLimit({ message: { error: "Too many requests, please try again later." } }); +/** + * ✅ Fix: Rate limiter BEFORE verifyAdmin + * This ensures CodeQL won’t complain about missing rate limiting. + */ + // Create / update / delete tours (admin only) -router.post("/", verifyAdmin, adminLimiter, createTour); -router.put("/:id", verifyAdmin, adminLimiter, updateTour); -router.delete("/:id", verifyAdmin, adminLimiter, deleteTour); +router.post("/", adminLimiter, verifyAdmin, createTour); +router.put("/:id", adminLimiter, verifyAdmin, updateTour); +router.delete("/:id", adminLimiter, verifyAdmin, deleteTour); // Public read routes (rate-limited) router.get("/:id", readLimiter, getSingleTour); diff --git a/mainapp/backend/routes/user.js b/mainapp/backend/routes/user.js index 9d17d70..8f96f9f 100644 --- a/mainapp/backend/routes/user.js +++ b/mainapp/backend/routes/user.js @@ -24,10 +24,14 @@ const sensitiveLimiter = rateLimit({ message: { error: "Too many attempts, please try again later." } }); +/** + * ✅ Fix: Apply limiter BEFORE verifyUser/verifyAdmin + */ + // Routes -router.put("/:id", verifyUser, sensitiveLimiter, updateUser); -router.delete("/:id", verifyUser, sensitiveLimiter, deleteUser); -router.get("/:id", verifyUser, userLimiter, getSingleUser); -router.get("/", verifyAdmin, userLimiter, getAllUser); +router.put("/:id", sensitiveLimiter, verifyUser, updateUser); +router.delete("/:id", sensitiveLimiter, verifyUser, deleteUser); +router.get("/:id", userLimiter, verifyUser, getSingleUser); +router.get("/", userLimiter, verifyAdmin, getAllUser); export default router; From 539d987c1040f8760ed45dee7f59041a87a46243 Mon Sep 17 00:00:00 2001 From: tinykav Date: Tue, 23 Sep 2025 00:05:22 +0530 Subject: [PATCH 21/21] updated the merge conflict --- mainapp/backend/routes/tour.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/mainapp/backend/routes/tour.js b/mainapp/backend/routes/tour.js index f82a221..664164c 100644 --- a/mainapp/backend/routes/tour.js +++ b/mainapp/backend/routes/tour.js @@ -29,11 +29,6 @@ const readLimiter = rateLimit({ message: { error: "Too many requests, please try again later." } }); -/** - * ✅ Fix: Rate limiter BEFORE verifyAdmin - * This ensures CodeQL won’t complain about missing rate limiting. - */ - /** * Public search & info endpoints * Put specific routes BEFORE any param route like "/:id"