Skip to content
Merged
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
257 changes: 257 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 @@ -12,6 +12,8 @@
"license": "ISC",
"type": "module",
"dependencies": {
Comment on lines 13 to 14
"@arcjet/inspect": "^1.5.0",
"@arcjet/node": "^1.5.0",
"bcryptjs": "^3.0.3",
"body-parser": "^2.3.0",
"cookie-parser": "^1.4.7",
Expand Down
31 changes: 31 additions & 0 deletions backend/src/lib/arcjet.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import arcjet, { shield, detectBot, slidingWindow } from "@arcjet/node";

import ENV from "../lib/env.js";

const aj = arcjet({
key: ENV.ARCJET_KEY,
rules: [
// Shield protects your app from common attacks e.g. SQL injection
shield({ mode: "LIVE" }),
// Create a bot detection rule
detectBot({
mode: "LIVE", // Blocks requests. Use "DRY_RUN" to log only
// Block all bots except the following
allow: [
"CATEGORY:SEARCH_ENGINE", // Google, Bing, etc
// Uncomment to allow these other common bot categories
// See the full list at https://arcjet.com/bot-list
//"CATEGORY:MONITOR", // Uptime monitoring services
//"CATEGORY:PREVIEW", // Link previews e.g. Slack, Discord
],
}),
// Create a token bucket rate limit. Other algorithms are supported.
slidingWindow({
mode: "LIVE", // Blocks requests. Use "DRY_RUN" to log only
max: 100, // Max 100 requests
interval: 60,
}),
],
});
Comment on lines +5 to +29

export default aj;
2 changes: 2 additions & 0 deletions backend/src/lib/env.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ const ENV = {
NODE_ENV: process.env.NODE_ENV,
MONGO_URI: process.env.MONGO_URI,
JWT_SECRET: process.env.JWT_SECRET,
ARCJET_KEY: process.env.ARCJET_KEY,
ARCJET_ENV: process.env.ARCJET_ENV,

}

Expand Down
33 changes: 33 additions & 0 deletions backend/src/middleware/arcjet.middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import aj from "../lib/arcjet.js";
import { isSpoofedBot } from "@arcjet/inspect";

export const arcjetProtection = async (req, res, next) => {
try {
const decision = await aj.protect(req);

if (decision.isDenied()) {
if (decision.reason.isRateLimit()) {
return res.status(429).json({ message: "Rate limit exceeded. Please try again later." });
} else if (decision.reason.isBot()) {
return res.status(403).json({ message: "Bot access denied." });
} else {
return res.status(403).json({
message: "Access denied by security policy.",
});
}
}

// check for spoofed bots
if (decision.results.some(isSpoofedBot)) {
return res.status(403).json({
error: "Spoofed bot detected",
message: "Malicious bot activity detected.",
});
Comment on lines +22 to +25
}

next();
} catch (error) {
console.log("Arcjet Protection Error:", error);
next();
}
Comment on lines +29 to +32
};
3 changes: 3 additions & 0 deletions backend/src/routes/auth.routes.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import express from "express";
import { login, signup, logout , userProfile } from "../controllers/auth.controller.js";
import { protectRoute } from "../middleware/auth.middleware.js";
import { arcjetProtection } from "../middleware/arcjet.middleware.js";

const router = express.Router();

router.use(arcjetProtection); // Apply Arcjet protection to all routes

router.post("/login", login);
router.post("/signup", signup);
router.post("/logout", logout);
Expand Down