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
5 changes: 5 additions & 0 deletions constants/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,20 @@ const APPLICATION_ROLES = {

const API_RESPONSE_MESSAGES = {
APPLICATION_CREATED_SUCCESS: "Application created successfully",
APPLICATION_UPDATED_SUCCESS: "Application updated successfully",
APPLICATION_RETURN_SUCCESS: "Applications returned successfully",
NUDGE_SUCCESS: "Nudge sent successfully",
FEEDBACK_SUBMITTED_SUCCESS: "Application feedback submitted successfully",
};

const APPLICATION_ERROR_MESSAGES = {
APPLICATION_ALREADY_REVIEWED: "Application has already been reviewed",
APPLICATION_NOT_FOUND: "Application not found",
APPLICATION_EDIT_UNAUTHORIZED: "You are not authorized to edit this application",
NUDGE_TOO_SOON: "Nudge unavailable. You'll be able to nudge again after 24 hours.",
NUDGE_ONLY_PENDING_ALLOWED: "Nudge unavailable. Only pending applications can be nudged.",
EDIT_TOO_SOON: "You can edit your application again 24 hours after your last edit.",
EMPTY_UPDATE_PAYLOAD: "Update payload must include at least one editable field.",
};

const APPLICATION_LOG_MESSAGES = {
Expand Down
37 changes: 19 additions & 18 deletions controllers/applications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const { createApplicationService } = require("../services/applicationService");
const { Conflict } = require("http-errors");
const logger = require("../utils/logger");
const { APPLICATION_STATUS_TYPES } = require("../constants/application");
const { buildApplicationUpdatePayload } = require("../utils/application");

const getAllOrUserApplication = async (req: CustomRequest, res: CustomResponse): Promise<any> => {
try {
Expand Down Expand Up @@ -105,26 +106,26 @@ const updateApplication = async (req: CustomRequest, res: CustomResponse) => {
try {
const { applicationId } = req.params;
const rawBody = req.body;
const dataToUpdate = buildApplicationUpdatePayload(rawBody);
const userId = req.userData.id;
const username = req.userData.username;

const applicationLog = {
type: logType.APPLICATION_UPDATED,
meta: {
applicationId,
username: req.userData.username,
userId: req.userData.id,
},
body: rawBody,
};

const promises = [
ApplicationModel.updateApplication(rawBody, applicationId),
addLog(applicationLog.type, applicationLog.meta, applicationLog.body),
];
const result = await ApplicationModel.updateApplication(dataToUpdate, applicationId, userId, username, rawBody);

await Promise.all(promises);
return res.json({
message: "Application updated successfully!",
});
switch (result.status) {
case APPLICATION_STATUS.notFound:
return res.boom.notFound(APPLICATION_ERROR_MESSAGES.APPLICATION_NOT_FOUND);
case APPLICATION_STATUS.unauthorized:
return res.boom.unauthorized(APPLICATION_ERROR_MESSAGES.APPLICATION_EDIT_UNAUTHORIZED);
case APPLICATION_STATUS.tooSoon:
return res.boom.conflict(APPLICATION_ERROR_MESSAGES.EDIT_TOO_SOON);
case APPLICATION_STATUS.success:
return res.json({
message: API_RESPONSE_MESSAGES.APPLICATION_UPDATED_SUCCESS,
});
default:
return res.boom.badImplementation(INTERNAL_SERVER_ERROR);
}
} catch (err) {
logger.error(`Error while updating the application: ${err}`);
return res.boom.badImplementation(INTERNAL_SERVER_ERROR);
Expand Down
4 changes: 3 additions & 1 deletion controllers/discordactions.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ const DISCORD_BASE_URL = config.get("services.discordBot.baseUrl");

const createGroupRole = async (req, res) => {
try {
const rolename = `group-${req.body.rolename}`;
const { role = false } = req.query;
const rolename = role ? req.body.rolename : `group-${req.body.rolename}`;

const { roleExists } = await discordRolesModel.isGroupRoleExists({ rolename });

Expand Down Expand Up @@ -505,6 +506,7 @@ const generateInviteForUser = async (req, res) => {

const inviteOptions = {
channelId: channelId,
role: req.approvedApplicationRole,
};
const response = await fetch(`${DISCORD_BASE_URL}/invite`, {
method: "POST",
Expand Down
26 changes: 1 addition & 25 deletions controllers/external-accounts.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ const { addOrUpdate, getUsersByRole, updateUsersInBatch } = require("../models/u
const { retrieveDiscordUsers, fetchUsersForKeyValues } = require("../services/dataAccessLayer");
const { EXTERNAL_ACCOUNTS_POST_ACTIONS } = require("../constants/external-accounts");
const removeDiscordRoleUtils = require("../utils/removeDiscordRoleFromUser");
const addDiscordRoleUtils = require("../utils/addDiscordRoleToUser");
const config = require("config");
const logger = require("../utils/logger");
const { markUnDoneTasksOfArchivedUsersBacklog } = require("../models/tasks");
Expand Down Expand Up @@ -80,30 +79,7 @@ const linkExternalAccount = async (req, res) => {
);

if (!unverifiedRoleRemovalResponse.success) {
// Tolerable errors that should not block role assignment
const tolerableErrors = ["Role doesn't exist", "Role deletion from database failed"];
const isTolerableError = tolerableErrors.some((err) => unverifiedRoleRemovalResponse.message.includes(err));

if (!isTolerableError) {
const message = `User details updated but ${unverifiedRoleRemovalResponse.message}. Please contact admin`;
return res.boom.internal(message, { message });
}
logger.info(
`Tolerable error during unverified role removal for Discord ID: ${attributes.discordId}: ${unverifiedRoleRemovalResponse.message}`
);
}

const developerRoleId = config.get("discordDeveloperRoleId");
const newRoleId = config.get("discordNewRoleId");

try {
await addDiscordRoleUtils.addDiscordRoleToUser(attributes.discordId, developerRoleId, "Developer");
await addDiscordRoleUtils.addDiscordRoleToUser(attributes.discordId, newRoleId, "New");
logger.info(`Roles (Developer, New) assigned successfully for Discord ID: ${attributes.discordId}`);
} catch (roleError) {
logger.error(`Error assigning roles after verification: ${roleError}`);
const message = `Your discord profile has been linked but role assignment failed. Please contact admin`;
return res.boom.internal(message, { message });
return res.boom.internal(null, { message: "Unverified role removal failed. Please contact admin" });
}

return res.status(200).json({ message: "Your discord profile has been linked successfully" });
Expand Down
7 changes: 6 additions & 1 deletion middlewares/checkCanGenerateDiscordLink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,16 @@ const checkCanGenerateDiscordLink = async (req: CustomRequest, res: CustomRespon
}

const approvedApplication = applications.find((application: { status: string; }) => application.status === 'accepted');

if (!approvedApplication) {
return res.boom.forbidden("Only users with an accepted application can generate a Discord invite link.");
}

if (approvedApplication.isNew !== true || !approvedApplication.role) {
return res.boom.forbidden("You are not eligible to generate a Discord invite link.");
}

req.approvedApplicationRole = approvedApplication.role;
return next();
} catch (error) {
return res.boom.badImplementation("An error occurred while checking user applications.");
Expand Down
88 changes: 69 additions & 19 deletions middlewares/validators/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,24 @@ const { APPLICATION_STATUS_TYPES, APPLICATION_ROLES } = require("../../constants
const { phoneNumberRegex } = require("../../constants/subscription-validator");
const logger = require("../../utils/logger");

const socialLinkSchema = joi
.object({
phoneNumber: joi.string().optional().regex(phoneNumberRegex).message('"phoneNumber" must be in a valid format'),
github: joi.string().min(1).optional(),
instagram: joi.string().min(1).optional(),
linkedin: joi.string().min(1).optional(),
twitter: joi.string().min(1).optional(),
peerlist: joi.string().min(1).optional(),
behance: joi.string().min(1).optional(),
dribbble: joi.string().min(1).optional(),
})
.optional();

const validateApplicationData = async (req: CustomRequest, res: CustomResponse, next: NextFunction) => {
if (req.body.socialLink?.phoneNo) {
req.body.socialLink.phoneNo = req.body.socialLink.phoneNo.trim();
if (req.body.socialLink?.phoneNumber) {
req.body.socialLink.phoneNumber = req.body.socialLink.phoneNumber.trim();
}

const socialLinkSchema = joi
.object({
phoneNo: joi.string().optional().regex(phoneNumberRegex).message('"phoneNo" must be in a valid format'),
github: joi.string().min(1).optional(),
instagram: joi.string().min(1).optional(),
linkedin: joi.string().min(1).optional(),
twitter: joi.string().min(1).optional(),
peerlist: joi.string().min(1).optional(),
behance: joi.string().min(1).optional(),
dribbble: joi.string().min(1).optional(),
})
.optional();

const schema = joi
.object()
.strict()
Expand Down Expand Up @@ -65,11 +65,11 @@ const validateApplicationData = async (req: CustomRequest, res: CustomResponse,
next();
} catch (error) {
logger.error(`Error in validating application data: ${error}`);
res.boom.badRequest(error.details[0].message);
return res.boom.badRequest(error.details[0].message);
}
};

const validateApplicationUpdateData = async (req: CustomRequest, res: CustomResponse, next: NextFunction) => {
const validateApplicationFeedbackData = async (req: CustomRequest, res: CustomResponse, next: NextFunction) => {
const schema = joi
.object({
status: joi
Expand Down Expand Up @@ -109,7 +109,56 @@ const validateApplicationUpdateData = async (req: CustomRequest, res: CustomResp
next();
} catch (error) {
logger.error(`Error in validating recruiter data: ${error}`);
res.boom.badRequest(error.details[0].message);
return res.boom.badRequest(error.details[0].message);
}
};

const validateApplicationUpdateData = async (req: CustomRequest, res: CustomResponse, next: NextFunction) => {
if (req.body.socialLink?.phoneNumber) {
req.body.socialLink.phoneNumber = req.body.socialLink.phoneNumber.trim();
}

const professionalSchema = joi
.object({
institution: joi.string().min(1).optional(),
skills: joi.string().min(5).optional(),
})
.optional();

const schema = joi
.object()
.strict()
.min(1)
.keys({
imageUrl: joi.string().uri().optional(),
foundFrom: joi.string().min(1).optional(),
introduction: joi.string().min(1).optional(),
forFun: joi
.string()
.custom((value, helpers) => customWordCountValidator(value, helpers, 100))
.optional(),
funFact: joi
.string()
.custom((value, helpers) => customWordCountValidator(value, helpers, 100))
.optional(),
whyRds: joi
.string()
.custom((value, helpers) => customWordCountValidator(value, helpers, 100))
.optional(),
numberOfHours: joi.number().min(1).max(168).optional(),
professional: professionalSchema,
socialLink: socialLinkSchema,
})
.messages({
"object.min": "Update payload must contain at least one allowed field.",
});

try {
await schema.validateAsync(req.body);
next();
} catch (error) {
logger.error(`Error in validating application update data: ${error}`);
return res.boom.badRequest(error.details[0].message);
}
};

Expand All @@ -127,12 +176,13 @@ const validateApplicationQueryParam = async (req: CustomRequest, res: CustomResp
next();
} catch (error) {
logger.error(`Error validating query params : ${error}`);
res.boom.badRequest(error.details[0].message);
return res.boom.badRequest(error.details[0].message);
}
};

module.exports = {
validateApplicationData,
validateApplicationFeedbackData,
validateApplicationUpdateData,
validateApplicationQueryParam,
};
10 changes: 8 additions & 2 deletions middlewares/validators/discordactions.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
const Joi = require("joi");
const { validateMillisecondsTimestamp } = require("./utils");
const logger = require("../../utils/logger");

const validateGroupRoleBody = async (req, res, next) => {
const schema = Joi.object({
const bodySchema = Joi.object({
rolename: Joi.string().trim().required(),
description: Joi.string().trim(),
});

const querySchema = Joi.object({
role: Joi.boolean().default(false).optional(),
});

try {
await schema.validateAsync(req.body);
await bodySchema.validateAsync(req.body);
req.query = await querySchema.validateAsync(req.query);
next();
} catch (error) {
logger.error(`Error validating createGroupRole payload : ${error}`);
Expand Down
59 changes: 53 additions & 6 deletions models/applications.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { application } from "../types/application";
import { addLog } from "./logs";
const { logType } = require("../constants/logs");
const firestore = require("../utils/firestore");
const logger = require("../utils/logger");
const ApplicationsModel = firestore.collection("applicants");
Expand Down Expand Up @@ -128,13 +130,58 @@ const addApplication = async (data: application) => {
}
};

const updateApplication = async (dataToUpdate: object, applicationId: string) => {
try {
await ApplicationsModel.doc(applicationId).update(dataToUpdate);
} catch (err) {
logger.error("Error in updating intro", err);
throw err;
const updateApplication = async (
dataToUpdate: object,
applicationId: string,
userId: string,
username: string,
rawBody: object
) => {
const result = await firestore.runTransaction(async (transaction) => {
const currentTime = Date.now();
const twentyFourHoursInMilliseconds = convertDaysToMilliseconds(1);

const applicationRef = ApplicationsModel.doc(applicationId);
const applicationDoc = await transaction.get(applicationRef);

if (!applicationDoc.exists) {
return { status: APPLICATION_STATUS.notFound };
}

const application = applicationDoc.data();

if (application.userId !== userId) {
return { status: APPLICATION_STATUS.unauthorized };
}

const lastEditAt = application.lastEditAt;
if (lastEditAt) {
const lastEditTimestamp = new Date(lastEditAt).getTime();
const timeDifference = currentTime - lastEditTimestamp;

if (timeDifference < twentyFourHoursInMilliseconds) {
return { status: APPLICATION_STATUS.tooSoon };
}
}

const requestBody = {
...dataToUpdate,
lastEditAt: new Date(currentTime).toISOString(),
};
transaction.update(applicationRef, requestBody);

return { status: APPLICATION_STATUS.success };
});

if (result.status === APPLICATION_STATUS.success) {
await addLog(
logType.APPLICATION_UPDATED,
{ applicationId, username, userId },
rawBody
);
}

return result;
};

const nudgeApplication = async ({ applicationId, userId }: { applicationId: string; userId: string }) => {
Expand Down
3 changes: 2 additions & 1 deletion routes/applications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@
);
router.get("/:applicationId", authenticate, authorizeRoles([SUPERUSER]), applications.getApplicationById);
router.post("/", authenticate, applicationValidator.validateApplicationData, applications.addApplication);
router.patch("/:applicationId", authenticate, applicationValidator.validateApplicationUpdateData, applications.updateApplication);

Check failure

Code scanning / CodeQL

Missing rate limiting High

This route handler performs
authorization
, but is not rate-limited.
This route handler performs
authorization
, but is not rate-limited.
This route handler performs
authorization
, but is not rate-limited.

Copilot Autofix

AI 12 days ago

In general, the fix is to introduce a rate-limiting middleware (for example, with express-rate-limit) and apply it to the relevant routes (or the entire router) that perform authentication/authorization and likely expensive operations. This middleware will cap the number of requests per IP (or another key) within a given time window, mitigating denial‑of‑service attempts via excessive authorized requests.

For this specific file, the best minimal‑change fix is:

  • Import and configure a rate limiter in routes/applications.ts using express-rate-limit.
  • Attach the limiter either to the entire router (so all application routes are covered) or at least to the specific routes performing authorization. Applying it once to the router is simpler and addresses all three variants in one place without altering existing controller logic.

Concrete changes in routes/applications.ts:

  1. Add a require("express-rate-limit") import right after the existing imports.
  2. Define a limiter constant using RateLimit({ windowMs: ..., max: ... }). We’ll choose reasonable defaults (e.g., 100 requests per 15 minutes) as in the background example.
  3. Apply router.use(limiter); after const router = express.Router(); so it protects all routes in this router, including the one on line 20 that triggered the alert.

No other existing middleware, handlers, or signatures need to change.

Suggested changeset 2
routes/applications.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/routes/applications.ts b/routes/applications.ts
--- a/routes/applications.ts
+++ b/routes/applications.ts
@@ -5,9 +5,17 @@
 const applications = require("../controllers/applications");
 const { authorizeOwnOrSuperUser } = require("../middlewares/authorizeOwnOrSuperUser");
 const applicationValidator = require("../middlewares/validators/application");
+const RateLimit = require("express-rate-limit");
 
 const router = express.Router();
 
+const limiter = RateLimit({
+  windowMs: 15 * 60 * 1000, // 15 minutes
+  max: 100, // limit each IP to 100 requests per windowMs
+});
+
+router.use(limiter);
+
 router.get(
   "/",
   authenticate,
EOF
@@ -5,9 +5,17 @@
const applications = require("../controllers/applications");
const { authorizeOwnOrSuperUser } = require("../middlewares/authorizeOwnOrSuperUser");
const applicationValidator = require("../middlewares/validators/application");
const RateLimit = require("express-rate-limit");

const router = express.Router();

const limiter = RateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
});

router.use(limiter);

router.get(
"/",
authenticate,
package.json
Outside changed files

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/package.json b/package.json
--- a/package.json
+++ b/package.json
@@ -42,7 +42,8 @@
     "passport-github2": "0.1.12",
     "passport-google-oauth20": "^2.0.0",
     "rate-limiter-flexible": "5.0.3",
-    "winston": "3.13.0"
+    "winston": "3.13.0",
+    "express-rate-limit": "^8.2.1"
   },
   "devDependencies": {
     "@types/chai": "4.3.16",
EOF
@@ -42,7 +42,8 @@
"passport-github2": "0.1.12",
"passport-google-oauth20": "^2.0.0",
"rate-limiter-flexible": "5.0.3",
"winston": "3.13.0"
"winston": "3.13.0",
"express-rate-limit": "^8.2.1"
},
"devDependencies": {
"@types/chai": "4.3.16",
This fix introduces these dependencies
Package Version Security advisories
express-rate-limit (npm) 8.2.1 None
Copilot is powered by AI and may make mistakes. Always verify output.
router.patch(
"/:applicationId/feedback",
authenticate,
authorizeRoles([SUPERUSER]),
applicationValidator.validateApplicationUpdateData,
applicationValidator.validateApplicationFeedbackData,
applications.submitApplicationFeedback
);
router.patch("/:applicationId/nudge", authenticate, applications.nudgeApplication);
Expand Down
2 changes: 1 addition & 1 deletion routes/discordactions.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
* Short-circuit this POST method for this endpoint
* Refer https://github.com/Real-Dev-Squad/todo-action-items/issues/269 for more details.
*/
router.post("/invite", disableRoute, authenticate, checkCanGenerateDiscordLink, generateInviteForUser);
router.post("/invite", authenticate, checkCanGenerateDiscordLink, generateInviteForUser);

Check failure

Code scanning / CodeQL

Missing rate limiting High

This route handler performs
authorization
, but is not rate-limited.
This route handler performs
authorization
, but is not rate-limited.
This route handler performs
authorization
, but is not rate-limited.

Copilot Autofix

AI 11 days ago

In general, the fix is to introduce a rate-limiting middleware (such as express-rate-limit) and apply it to the sensitive route so that even authorized users cannot spam it. This limits the number of invite-generation requests per client (typically per IP or per user identifier) over a time window, mitigating DoS and abuse of downstream services like Discord.

The best fix here, without changing existing functionality, is to add express-rate-limit to this router and apply a limiter only to the /invite POST route (and potentially the GET route if desired). We keep all existing middlewares and handlers in the same order, only inserting an extra middleware in the chain. Following the example in the background, we will require('express-rate-limit'), configure a limiter (e.g., modest per-minute or per-15-minute caps), and insert it in the router.post("/invite", ...) definition immediately after authenticate so that only authenticated requests are counted/throttled. This localizes the change to routes/discordactions.js and preserves the existing behavior of authentication, role checks, and business logic.

Concretely:

  • At the top of routes/discordactions.js, add const rateLimit = require("express-rate-limit");.
  • Define a limiter constant, e.g. const inviteRateLimiter = rateLimit({ windowMs: 15 * 60 * 1000, max: 100 }); (you may adjust numbers later, but we must pick reasonable defaults now).
  • Update the /invite POST route (around line 50) to include inviteRateLimiter in the middleware list: router.post("/invite", authenticate, inviteRateLimiter, checkCanGenerateDiscordLink, generateInviteForUser);.
    All other routes remain unchanged.
Suggested changeset 2
routes/discordactions.js

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/routes/discordactions.js b/routes/discordactions.js
--- a/routes/discordactions.js
+++ b/routes/discordactions.js
@@ -32,8 +32,14 @@
 const { verifyCronJob } = require("../middlewares/authorizeBot");
 const { authorizeAndAuthenticate } = require("../middlewares/authorizeUsersAndService");
 const { disableRoute } = require("../middlewares/shortCircuit");
+const rateLimit = require("express-rate-limit");
 const router = express.Router();
 
+const inviteRateLimiter = rateLimit({
+  windowMs: 15 * 60 * 1000, // 15 minutes
+  max: 100, // limit each IP to 100 invite requests per windowMs
+});
+
 router.post("/groups", authenticate, checkIsVerifiedDiscord, validateGroupRoleBody, createGroupRole);
 router.get("/groups", authenticate, checkIsVerifiedDiscord, validateLazyLoadingParams, getPaginatedAllGroupRoles);
 router.delete("/groups/:groupId", authenticate, checkIsVerifiedDiscord, authorizeRoles([SUPERUSER]), deleteGroupRole);
@@ -47,7 +51,7 @@
  * Short-circuit this POST method for this endpoint
  * Refer https://github.com/Real-Dev-Squad/todo-action-items/issues/269 for more details.
  */
-router.post("/invite", authenticate, checkCanGenerateDiscordLink, generateInviteForUser);
+router.post("/invite", authenticate, inviteRateLimiter, checkCanGenerateDiscordLink, generateInviteForUser);
 
 router.delete("/roles", authenticate, checkIsVerifiedDiscord, deleteRole);
 router.get("/roles", authenticate, checkIsVerifiedDiscord, getGroupsRoleId);
EOF
@@ -32,8 +32,14 @@
const { verifyCronJob } = require("../middlewares/authorizeBot");
const { authorizeAndAuthenticate } = require("../middlewares/authorizeUsersAndService");
const { disableRoute } = require("../middlewares/shortCircuit");
const rateLimit = require("express-rate-limit");
const router = express.Router();

const inviteRateLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 invite requests per windowMs
});

router.post("/groups", authenticate, checkIsVerifiedDiscord, validateGroupRoleBody, createGroupRole);
router.get("/groups", authenticate, checkIsVerifiedDiscord, validateLazyLoadingParams, getPaginatedAllGroupRoles);
router.delete("/groups/:groupId", authenticate, checkIsVerifiedDiscord, authorizeRoles([SUPERUSER]), deleteGroupRole);
@@ -47,7 +51,7 @@
* Short-circuit this POST method for this endpoint
* Refer https://github.com/Real-Dev-Squad/todo-action-items/issues/269 for more details.
*/
router.post("/invite", authenticate, checkCanGenerateDiscordLink, generateInviteForUser);
router.post("/invite", authenticate, inviteRateLimiter, checkCanGenerateDiscordLink, generateInviteForUser);

router.delete("/roles", authenticate, checkIsVerifiedDiscord, deleteRole);
router.get("/roles", authenticate, checkIsVerifiedDiscord, getGroupsRoleId);
package.json
Outside changed files

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/package.json b/package.json
--- a/package.json
+++ b/package.json
@@ -42,7 +42,8 @@
     "passport-github2": "0.1.12",
     "passport-google-oauth20": "^2.0.0",
     "rate-limiter-flexible": "5.0.3",
-    "winston": "3.13.0"
+    "winston": "3.13.0",
+    "express-rate-limit": "^8.2.1"
   },
   "devDependencies": {
     "@types/chai": "4.3.16",
EOF
@@ -42,7 +42,8 @@
"passport-github2": "0.1.12",
"passport-google-oauth20": "^2.0.0",
"rate-limiter-flexible": "5.0.3",
"winston": "3.13.0"
"winston": "3.13.0",
"express-rate-limit": "^8.2.1"
},
"devDependencies": {
"@types/chai": "4.3.16",
This fix introduces these dependencies
Package Version Security advisories
express-rate-limit (npm) 8.2.1 None
Copilot is powered by AI and may make mistakes. Always verify output.

router.delete("/roles", authenticate, checkIsVerifiedDiscord, deleteRole);
router.get("/roles", authenticate, checkIsVerifiedDiscord, getGroupsRoleId);
Expand Down
Loading
Loading