Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
20 changes: 20 additions & 0 deletions backend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.21.0",
"express-mongo-sanitize": "^2.2.0",
"express-rate-limit": "^7.5.0",
"helmet": "^8.1.0",
"http": "^0.0.1-security",
"jsonwebtoken": "^9.0.2",
"moment": "^2.30.1",
Expand Down
11 changes: 8 additions & 3 deletions backend/src/app.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import express from "express";
import cookieParser from "cookie-parser";
import cors from "cors";
import helmet from "helmet";
import mongoSanitize from "express-mongo-sanitize";
const app = express();
import { User } from "./models/user.model.js";
import { Company } from "./models/company.model.js";
import { Professor } from "./models/professor.model.js";


// Security headers (disable CSP initially to avoid breaking frontend inline scripts/styles)
app.use(helmet({ contentSecurityPolicy: false }));

app.use(
cors({
Expand All @@ -22,6 +25,9 @@ app.use(express.urlencoded({ extended: true, limit: "16kb" }));
app.use(express.static("public"));
app.use(cookieParser());

// Sanitize MongoDB queries to prevent NoSQL injection
app.use(mongoSanitize());

import academicsRouter from "./routes/academic.routes.js";
app.use("/api/v1/academics", academicsRouter);

Expand Down Expand Up @@ -96,7 +102,6 @@ import {
app.use(notFoundHandler);

// Global error handler - must be the last middleware
// Temporary debug test for companyInterview

app.use(errorHandler);

export { app };
19 changes: 12 additions & 7 deletions backend/src/controllers/admin.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Professor } from "../models/professor.model.js";
import { ApiError } from "../utils/ApiError.js";
import { ApiResponse } from "../utils/ApiResponse.js";
import { asyncHandler } from "../utils/asyncHandler.js";
import { Otp } from "../models/otp.model.js";

const getUnverifiedUsers = asyncHandler(async (req, res) => {
const admin = req.admin;
Expand Down Expand Up @@ -193,15 +194,16 @@ const loginAdmin = asyncHandler(async (req, res) => {
admin._id
);
const loggedInAdmin = await Admin.findById(admin._id).select(
"-password -refeshToken"
"-password -refreshToken"
);
// if (!admin.isAdmin) {
// throw new ApiError(403, "You are not verified as an admin yet!");
// }

const options = {
httpOnly: true,
secure: false,
secure: process.env.NODE_ENV === "production",
sameSite: "Lax",
};
return res
.status(200)
Expand Down Expand Up @@ -230,22 +232,25 @@ const logoutAdmin = asyncHandler(async (req, res) => {
new: true,
}
);
res.clearCookie("accessToken");
res.clearCookie("refreshToken");
const cookieOptions = {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "Lax",
};
res.clearCookie("accessToken", cookieOptions);
res.clearCookie("refreshToken", cookieOptions);
return res
.status(200)
.json(new ApiResponse(200, {}, "Admin logged out successfully!"));
});

const getCurrendAdmin = asyncHandler(async (req, res) => {
const _id = req?.admin?._id;
const admin = await Admin.findById({ _id });
const admin = await Admin.findById({ _id }).select("-password -refreshToken");
if (!admin) throw new ApiError(404, "admin not found");
res.status(200).json(new ApiResponse(200, admin, "admin fetched"));
});

import { Otp } from "../models/otp.model.js";

export const rejectUser = asyncHandler(async (req, res) => {
const { userId, reason } = req.body;
if (!userId) {
Expand Down
16 changes: 14 additions & 2 deletions backend/src/controllers/awards.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ const deleteAward = asyncHandler(async (req, res) => {
const { id } = req.params;

try {
const award = await Award.findById(id);
if (!award) {
throw new ApiError(404, "Award not found");
}
if (award.student.toString() !== req.user._id.toString()) {
throw new ApiError(403, "You can only delete your own awards");
}
const deletedAward = await Award.findByIdAndDelete(id);

if (!deletedAward) {
Expand Down Expand Up @@ -78,6 +85,9 @@ const getAwardById = asyncHandler(async (req, res) => {
if (!award) {
throw new ApiError(404, "Award not found");
}
if (award.student.toString() !== req.user._id.toString()) {
throw new ApiError(403, "You can only view your own awards");
}

res.status(200).json({
success: true,
Expand All @@ -96,19 +106,21 @@ const getAllAwards = asyncHandler(async (req, res) => {

const updateAward = asyncHandler(async (req, res) => {
const { id } = req.params;
const { title, description, date, student } = req.body;
const { title, description, date } = req.body;

try {
const award = await Award.findById(id);

if (!award) {
throw new ApiError(404, "Award not found");
}
if (award.student.toString() !== req.user._id.toString()) {
throw new ApiError(403, "You can only update your own awards");
}

award.title = title;
award.description = description;
award.date = date;
award.student = student;

const docURL = award.doc;
if (docURL) {
Expand Down
3 changes: 3 additions & 0 deletions backend/src/controllers/exam.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ const getExamById = asyncHandler(async (req, res) => {
if (!exam) {
throw new ApiError(404, "Exam not found");
}
if (exam.name.toString() !== req.user._id.toString()) {
throw new ApiError(403, "You can only view your own exam records");
}

res.status(200).json({
success: true,
Expand Down
3 changes: 3 additions & 0 deletions backend/src/controllers/higher-education.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ const getHigherEducationById = asyncHandler(async (req, res) => {
if (!higherEducation) {
throw new ApiError(404, "Higher Education not found");
}
if (higherEducation.name.toString() !== req.user._id.toString()) {
throw new ApiError(403, "You can only view your own higher education records");
}

res.status(200).json({
success: true,
Expand Down
10 changes: 5 additions & 5 deletions backend/src/controllers/internship.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const addInternship = asyncHandler(async (req, res) => {
if (!uploadedDoc) {
throw new ApiError(400, "Document upload failed!");
}
docUrl = uploadedDoc.url;
docUrl = uploadedDoc.secure_url;
} else if (location === "outside_bit" && !req.file) {
throw new ApiError(
400,
Expand Down Expand Up @@ -97,11 +97,11 @@ const addInternDocs = asyncHandler(async (req, res) => {
if (!InternDocsLocalPath) throw new ApiError(400, "Document is neccesary");
const InternDocs = await uploadOnCloudinary(InternDocsLocalPath);
if (!InternDocs) {
throw new ApiError(400, "Not uploaded on Closuinary. something went wrong");
throw new ApiError(400, "Not uploaded on Cloudinary. Something went wrong");
}
const newInternRecord = await Internship.updateOne(
{ _id },
{ $set: { doc: InternDocs.url } }
{ $set: { doc: InternDocs.secure_url } }
);
res
.status(200)
Expand Down Expand Up @@ -226,12 +226,12 @@ const getAllVerifiedInternshipData = asyncHandler(async (req, res) => {
});

const getInternshipDataforStudent = asyncHandler(async (req, res) => {
const { student_id } = req.body;
const student_id = req.user._id;
const response = await Internship.find(
{ student: student_id },
{ verfied: true }
)
.populate("student")
.populate("student", "-password -refreshToken")
.populate("company");
res
.status(200)
Expand Down
7 changes: 5 additions & 2 deletions backend/src/controllers/interview.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,11 @@ const getAllInterviewExps = asyncHandler(async (req, res) => {
sort = "-createdAt",
} = req.query;

// Whitelist allowed sort fields to prevent sort injection
const allowedSortFields = ["createdAt", "-createdAt", "company", "-company"];
const sanitizedSort = allowedSortFields.includes(sort) ? sort : "-createdAt";

const filter = {};
console.log(companyId, studentId);
if (companyId) {
filter.company = companyId;
}
Expand All @@ -77,7 +80,7 @@ const getAllInterviewExps = asyncHandler(async (req, res) => {
const interviewExps = await InterviewExp.find(filter)
.populate("company", "companyName")
.populate("student", "fullName email image branch cgpa")
.sort(sort)
.sort(sanitizedSort)
.skip(skip)
.limit(parseInt(limit, 10));

Expand Down
46 changes: 33 additions & 13 deletions backend/src/controllers/profProject.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ import {
import { Professor } from "../models/professor.model.js";

const addNewProject = asyncHandler(async (req, res) => {
const { profId, title, desc, categories, startDate, endDate, relevantLinks } =
const { title, desc, categories, startDate, endDate, relevantLinks } =
req.body;
// Derive professor ID from authenticated token, not client input
const profId = req.professor._id;
const professor = await Professor.findById(profId);
if (!professor) {
throw new ApiError(404, "Professor not found!");
Expand Down Expand Up @@ -88,6 +90,11 @@ const editProject = asyncHandler(async (req, res) => {
throw new ApiError(404, "Project not found");
}

// Verify ownership
if (project.professor.toString() !== req.professor._id.toString()) {
throw new ApiError(403, "Not authorized to edit this project");
}

project.title = title || project.title;
project.desc = desc || project.desc;
project.categories = categories || project.categories;
Expand Down Expand Up @@ -191,11 +198,13 @@ const applyToProject = asyncHandler(async (req, res) => {

const getAllApplications = asyncHandler(async (req, res) => {
const { status } = req.params;
const applications = await RequestProj.find({ status })
// Only return applications for projects owned by this professor
const profProjects = await AdhocProject.find({ professor: req.professor._id }).select("_id");
const projectIds = profProjects.map((p) => p._id);
const applications = await RequestProj.find({ status, projectId: { $in: projectIds } })
.populate("studentId", "fullName email rollNumber mobileNumber")
.populate("projectId", "title profName") // Added populate for projectId
.select("status applicationDate doc projectId"); // Included projectId in selected fields
console.log("applications", applications);
.populate("projectId", "title profName")
.select("status applicationDate doc projectId");
res.status(200).json({ success: true, data: applications });
});

Expand All @@ -210,16 +219,22 @@ const updateApplicationStatus = asyncHandler(async (req, res) => {
);
}

const updatedApplication = await RequestProj.findByIdAndUpdate(
applicationId,
{ status },
{ new: true }
);

if (!updatedApplication) {
const application = await RequestProj.findById(applicationId).populate("projectId");
if (!application) {
throw new ApiError(404, "Application not found");
}
res.status(200).json({ success: true, data: updatedApplication });
if (!application.projectId) {
throw new ApiError(404, "Referenced project no longer exists");
}

// Verify the professor owns the project
if (application.projectId.professor.toString() !== req.professor._id.toString()) {
throw new ApiError(403, "Not authorized to update this application");
}

application.status = status;
await application.save();
res.status(200).json({ success: true, data: application });
});

const getStudentApplications = asyncHandler(async (req, res) => {
Expand Down Expand Up @@ -250,6 +265,11 @@ const closeProject = asyncHandler(async (req, res) => {
throw new ApiError(404, "Project not found");
}

// Verify ownership
if (project.professor.toString() !== req.professor._id.toString()) {
throw new ApiError(403, "Not authorized to close this project");
}

project.closed = true;
await project.save();

Expand Down
Loading