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
8 changes: 8 additions & 0 deletions backend/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
/* eslint-disable no-undef */
const express = require("express");
require("dotenv").config();
const { validateEnv } = require("./utils/envValidator");
const botRouter = require("./routes/botRoutes");
const { errorHandler } = require("./middleware/validation");
const cors = require("cors");

// Validate environment variables on startup
validateEnv();

const app = express();
app.use(
cors({
Expand All @@ -23,6 +28,9 @@ app.get("/", (req, res) => {
res.send("Backend is Running!");
});

// Error handling middleware (must be last)
app.use(errorHandler);

app.listen(port, () => {
console.log(`Server is running at http://localhost:${port}`);
});
167 changes: 167 additions & 0 deletions backend/middleware/validation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
/**
* Validation Middleware for Express Routes
* Provides input validation and sanitization for API endpoints
*/

const validateRequest = (schema) => {
return (req, res, next) => {
const { error, value } = schema.validate(req.body, {
abortEarly: false,
stripUnknown: true,
});

if (error) {
const errors = error.details.map((detail) => ({
field: detail.path.join("."),
message: detail.message,
}));

return res.status(400).json({
success: false,
message: "Validation failed",
errors,
});
}

req.body = value;
next();
};
};

const sanitizeInput = (input) => {
if (typeof input !== "string") return input;

// Remove potential XSS attempts
return input
.replace(/[<>]/g, "") // Remove angle brackets
.trim();
};

const validateChatMessage = (req, res, next) => {
const { msg, history } = req.body;

// Validate message exists and is a string
if (!msg || typeof msg !== "string") {
return res.status(400).json({
success: false,
message: "Message is required and must be a string",
});
}

// Validate message length
if (msg.length === 0) {
return res.status(400).json({
success: false,
message: "Message cannot be empty",
});
}

if (msg.length > 2000) {
return res.status(400).json({
success: false,
message: "Message is too long (max 2000 characters)",
});
}

// Validate history is an array if provided
if (history !== undefined && !Array.isArray(history)) {
return res.status(400).json({
success: false,
message: "History must be an array",
});
}

// Limit history size to prevent abuse
if (history && history.length > 50) {
return res.status(400).json({
success: false,
message: "History is too long (max 50 messages)",
});
}

// Sanitize the message
req.body.msg = sanitizeInput(msg);

next();
};

const rateLimiter = (windowMs = 60000, maxRequests = 20) => {
const requests = new Map();

return (req, res, next) => {
const identifier = req.ip || req.connection.remoteAddress;
const now = Date.now();

if (!requests.has(identifier)) {
requests.set(identifier, []);
}

const userRequests = requests.get(identifier);

// Filter out old requests outside the time window
const recentRequests = userRequests.filter(
(timestamp) => now - timestamp < windowMs
);

if (recentRequests.length >= maxRequests) {
return res.status(429).json({
success: false,
message: "Too many requests. Please try again later.",
retryAfter: Math.ceil(windowMs / 1000),
});
}

recentRequests.push(now);
requests.set(identifier, recentRequests);

// Cleanup old entries periodically
if (Math.random() < 0.01) {
for (const [key, timestamps] of requests.entries()) {
const valid = timestamps.filter((ts) => now - ts < windowMs);
if (valid.length === 0) {
requests.delete(key);
} else {
requests.set(key, valid);
}
}
}

next();
};
};

const errorHandler = (err, req, res, next) => {

Check warning on line 133 in backend/middleware/validation.js

View workflow job for this annotation

GitHub Actions / build (18.x)

'next' is defined but never used

Check warning on line 133 in backend/middleware/validation.js

View workflow job for this annotation

GitHub Actions / build (20.x)

'next' is defined but never used
console.error("Error:", err);

// Handle specific error types
if (err.name === "ValidationError") {
return res.status(400).json({
success: false,
message: "Validation error",
errors: err.errors,
});
}

if (err.name === "UnauthorizedError") {
return res.status(401).json({
success: false,
message: "Unauthorized access",
});
}

// Default error response
return res.status(500).json({
success: false,
message: process.env.NODE_ENV === "development"

Check failure on line 155 in backend/middleware/validation.js

View workflow job for this annotation

GitHub Actions / build (18.x)

'process' is not defined

Check failure on line 155 in backend/middleware/validation.js

View workflow job for this annotation

GitHub Actions / build (20.x)

'process' is not defined
? err.message
: "Internal server error",
});
};

module.exports = {

Check failure on line 161 in backend/middleware/validation.js

View workflow job for this annotation

GitHub Actions / build (18.x)

'module' is not defined

Check failure on line 161 in backend/middleware/validation.js

View workflow job for this annotation

GitHub Actions / build (20.x)

'module' is not defined
validateRequest,
validateChatMessage,
rateLimiter,
sanitizeInput,
errorHandler,
};
7 changes: 6 additions & 1 deletion backend/routes/botRoutes.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
const express = require("express");

Check failure on line 1 in backend/routes/botRoutes.js

View workflow job for this annotation

GitHub Actions / build (18.x)

'require' is not defined

Check failure on line 1 in backend/routes/botRoutes.js

View workflow job for this annotation

GitHub Actions / build (20.x)

'require' is not defined
const returnResponse = require("../controllers/botController");

Check failure on line 2 in backend/routes/botRoutes.js

View workflow job for this annotation

GitHub Actions / build (18.x)

'require' is not defined

Check failure on line 2 in backend/routes/botRoutes.js

View workflow job for this annotation

GitHub Actions / build (20.x)

'require' is not defined
const { validateChatMessage, rateLimiter } = require("../middleware/validation");

Check failure on line 3 in backend/routes/botRoutes.js

View workflow job for this annotation

GitHub Actions / build (18.x)

'require' is not defined

Check failure on line 3 in backend/routes/botRoutes.js

View workflow job for this annotation

GitHub Actions / build (20.x)

'require' is not defined

const botRouter = express.Router();
botRouter.post("/", returnResponse);

// Apply rate limiting and validation middleware
botRouter.post("/", rateLimiter(60000, 20), validateChatMessage, returnResponse);

module.exports = botRouter;

Check failure on line 10 in backend/routes/botRoutes.js

View workflow job for this annotation

GitHub Actions / build (18.x)

'module' is not defined

Check failure on line 10 in backend/routes/botRoutes.js

View workflow job for this annotation

GitHub Actions / build (20.x)

'module' is not defined
Loading