Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
4e10246
Fix: applied rate limiting to all sensitive routes
tinykav Sep 21, 2025
40c425a
Fix: applied rate limiting to all sensitive routes
tinykav Sep 21, 2025
3af4e6f
Fix: applied rate limiting to all sensitive routes
tinykav Sep 21, 2025
1fda448
Fix: applied rate limiting to all sensitive routes
tinykav Sep 21, 2025
df15cd3
Create workflows
tinykav Sep 21, 2025
9c96666
Delete .github/workflows
tinykav Sep 21, 2025
1a2ce07
Create codeql.yml
tinykav Sep 21, 2025
12aadf9
Fix: applied rate limiting to all sensitive routes
tinykav Sep 21, 2025
cb0f476
Merge branch 'tiny' of https://github.com/y4-systems/ssd-project into…
tinykav Sep 21, 2025
d9dab9b
Fix: applied rate limiting to all sensitive routes
tinykav Sep 21, 2025
934d6ad
Fix: applied rate limiting to taskapp routes
tinykav Sep 21, 2025
215ca1d
Added apiLimiter for /todo, /note, /task routes
tinykav Sep 22, 2025
beefa3b
Applied global limiter (100 requests / 15 min),Applied writeLimiter (…
tinykav Sep 22, 2025
cece69d
add rate limiting to user and review routes
tinykav Sep 22, 2025
30d68ba
add rate limiting to auth routes (login/register), tour and booking r…
tinykav Sep 22, 2025
bbef07e
updated tour, booking, and user routes
tinykav Sep 22, 2025
dd452b9
add per-route rate limiting to todo, task, and note APIs
tinykav Sep 22, 2025
6cf88cd
updated taskapp routes
tinykav Sep 22, 2025
24ae889
stop flagging Sensitive Data Exposure in both feedbackapp and packageapp
tinykav Sep 22, 2025
a3ff70a
change read me file
tinykav Sep 22, 2025
98ebe51
Merge branch 'main' into tiny
tinykav Sep 22, 2025
8677554
apply rate limiting before authorization middleware
tinykav Sep 22, 2025
031318b
Merge branch 'tiny' of https://github.com/y4-systems/ssd-project into…
tinykav Sep 22, 2025
539d987
updated the merge conflict
tinykav Sep 22, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -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
72 changes: 27 additions & 45 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,45 +1,27 @@
# SSD Project — Securing the App

## Team
- Nuwani Fonseka — OAuth (Google) + DoS (ReDoS) fix
- Hasindu — SQL Injection, Dependency updates
- Sadeesha — CSRF, Hardcoded URL removal
- Tiny — Sensitive Data Exposure, (Rate Limiting moved to Tiny)

## Original project
- Source (pre-fixes): <PUT LINK>
*Note:* Original commit history predates semester start. (See assignment brief.) :contentReference[oaicite:0]{index=0}

## Secured project
- Fork / modified code (with fixes): <PUT LINK TO YOUR REPO/BRANCH>
- Key branches:
- `main` — stable, after fixes
- `nuwani-Oauth` — OAuth + DoS work

## My vulnerabilities (Nuwani)
1) **DoS (ReDoS) risk** in tour search
- **Before:** `new RegExp(req.query.city, "i")` built from user input
- **After:** Removed dynamic regex. Added input validation and **MongoDB collation** (`strength: 2`) for case-insensitive match.
- **Endpoints:**
- `GET /api/v1/tours/search?city=Colombo&distance=10&maxGroupSize=5`
- `GET /api/v1/tours/featured`
- `GET /api/v1/tours/count`
- **Files touched:**
- `mainapp/backend/controllers/tourController.js`
- `mainapp/backend/models/Tour.js` (index: `tourSchema.index({ city: 1 }, { collation: { locale: "en", strength: 2 } })`)
- `mainapp/backend/routes/tour.js` (route order hardened)

2) **OAuth 2.0 (Google) login**
- Added OAuth flow with popup-aware route handlers; state parameter preserved; tokens handled server-side.
- **Files:** `eventapp/backend/server.js`, `frontend/src/pages/Login.js`, `frontend/src/pages/SsoHandler.js`, etc.

## How to run
```bash
cd mainapp/backend
cp .env.example .env
# .env requires:
# PORT=5003
# MONGO_URI=mongodb+srv://<...>

npm install
npm start
# Blissify--Wedding-Management-System

SLIIT 2nd year second semester ITP module project using MERN stack technology for a wedding management system.

<p align="center">
<a href="https://ibb.co/q7HjVZs"><img src="https://i.ibb.co/5GQ4w78/Screenshot-2024-05-08-101902.png" alt="Blissify" border="0"></a>
</p>

<h3>Demo Video: https://youtu.be/ZZwhkC4C63M </h3>

<h3>Project Architecture</h3>

Micro frontend architecture was implemented to reduce complexity and scale the system according to the client's requirements. With this architecture system breaks down to subdomains. In development, these subdomains run on different ports which was implemented using lerna and craco npm libraries. mainapp works as host app and other apps such as vendorapp, guestapp work as remote apps.

<p align="center">
<a href="https://ibb.co/JdG5BDY"><img src="https://i.ibb.co/sPNym8T/banner.png" alt="banner" border="0"></a><br /><a target='_blank' href='https://imgbb.com/'></a><br /></p>

Contributors

1. Sadeesha : Perera RLSB
1. Ashika : Ridimahaliyadda AK
1. Chamika : Wijenarayana CH
1. Janudi : Adhikari AAJT
1. Dilsha : De Silva KDL
1. Sunera : Weerakkody WSS
1. Amandi : Wickramarathna ACS
1. Nethum : Rathninda RGSNC
3 changes: 2 additions & 1 deletion feedbackapp/backend/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
/node_modules
/node_modules
.env
34 changes: 17 additions & 17 deletions feedbackapp/backend/server.js
Original file line number Diff line number Diff line change
@@ -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);
app.use("/api", router);
21 changes: 15 additions & 6 deletions mainapp/backend/routes/auth.js
Original file line number Diff line number Diff line change
@@ -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;
// 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;
44 changes: 35 additions & 9 deletions mainapp/backend/routes/booking.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,37 @@
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;
// Booking limiter (strict - protects writes)
const bookingLimiter = rateLimit({
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 read-only database operations)
const readLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100,
message: { error: "Too many requests, please try again later." }
});

// ===== 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;
24 changes: 18 additions & 6 deletions mainapp/backend/routes/review.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
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;
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." }
});

//Rate limiter comes BEFORE verifyUser → fixes CodeQL warning
router.post("/:tourId", reviewLimiter, verifyUser, createReview);

export default router;
53 changes: 26 additions & 27 deletions mainapp/backend/routes/tour.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// mainapp/backend/routes/tour.js
import express from "express";
import rateLimit from "express-rate-limit";
import {
createTour,
updateTour,
Expand All @@ -8,47 +9,45 @@ import {
getAllTour,
getTourBysearch,
getFeaturedTour,
getTourCount,
} from "./../controlles/tourController.js"; // <-- NOTE: 'controllers', not 'controlles'
getTourCount
} from "./../controllers/tourController.js";
import { verifyAdmin } from "../utils/verifyToken.js";

const router = express.Router();

// Admin limiter (strict)
const adminLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 20,
message: { error: "Too many admin requests, please try again later." }
});

// Public GET limiter (generous)
const readLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100,
message: { error: "Too many requests, please try again later." }
});

/**
* Public search & info endpoints
* Put specific routes BEFORE any param route like "/:id"
*/

// GET /api/v1/tours/search?city=...&distance=...&maxGroupSize=...
router.get("/search", getTourBysearch);

// GET /api/v1/tours/featured
router.get("/featured", getFeaturedTour);

// GET /api/v1/tours/count
router.get("/count", getTourCount);
router.get("/search", readLimiter, getTourBysearch);
router.get("/featured", readLimiter, getFeaturedTour);
router.get("/count", readLimiter, getTourCount);

/**
* CRUD endpoints (admin-protected where needed)
* CRUD endpoints (admin-protected with stricter rate limiting)
*/

// POST /api/v1/tours
router.post("/", verifyAdmin, createTour);

// PUT /api/v1/tours/:id
router.put("/:id", verifyAdmin, updateTour);

// DELETE /api/v1/tours/:id
router.delete("/:id", verifyAdmin, deleteTour);
router.post("/", adminLimiter, verifyAdmin, createTour);
router.put("/:id", adminLimiter, verifyAdmin, updateTour);
router.delete("/:id", adminLimiter, verifyAdmin, deleteTour);

/**
* Param route AFTER specific routes so it won't swallow /search /featured /count
*/

// GET /api/v1/tours/:id
router.get("/:id", getSingleTour);

// GET /api/v1/tours
router.get("/", getAllTour);
router.get("/:id", readLimiter, getSingleTour);
router.get("/", readLimiter, getAllTour);

export default router;
Loading
Loading