Skip to content

Commit 615ca19

Browse files
committed
fix: security for volnerability- sonarcube
1 parent cc33679 commit 615ca19

1 file changed

Lines changed: 68 additions & 18 deletions

File tree

src/controllers/activityLogController.js

Lines changed: 68 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1+
const mongoose = require('mongoose');
12
const ActivityLog = require('../models/activityLog');
23
const usersProfiles = require('../models/userProfile');
34

5+
const isValidObjectId = (id) =>
6+
mongoose.Types.ObjectId.isValid(id) && new mongoose.Types.ObjectId(id).toString() === String(id);
7+
const sanitizeString = (str) => String(str).slice(0, 500);
8+
49
const activityLogController = function () {
510
// Format response - only include assisted_users if is_assisted is true
611
const formatLogs = (logs) =>
@@ -30,7 +35,16 @@ const activityLogController = function () {
3035
return res.status(403).json({ error: "Forbidden: Cannot access another student's log" });
3136
}
3237

33-
const logs = await ActivityLog.find({ actor_id: studentId })
38+
const authorizedStudentId = requestedStudentId || String(studentId);
39+
if (
40+
!authorizedStudentId ||
41+
typeof authorizedStudentId !== 'string' ||
42+
authorizedStudentId.length > 100
43+
) {
44+
return res.status(400).json({ error: 'Invalid studentId' });
45+
}
46+
47+
const logs = await ActivityLog.find({ actor_id: authorizedStudentId })
3448
.sort({ created_at: -1 })
3549
.select('action_type metadata created_at actor_id is_assisted assisted_users');
3650

@@ -54,13 +68,15 @@ const activityLogController = function () {
5468
return res.status(400).json({ error: 'actionType and entityId are required' });
5569
}
5670

57-
// Get valid enums
71+
if (typeof entityId !== 'string' || entityId.length > 200) {
72+
return res.status(400).json({ error: 'Invalid entityId' });
73+
}
74+
5875
const validActionTypes = ActivityLog.schema.path('action_type').enumValues;
5976
const validAssistanceTypes = ActivityLog.schema
6077
.path('assisted_users')
6178
.schema.path('assistance_type').enumValues;
6279

63-
// Validate actionType
6480
if (!validActionTypes.includes(actionType)) {
6581
return res.status(400).json({
6682
error: `Invalid actionType. Must be one of: ${validActionTypes.join(', ')}`,
@@ -72,13 +88,11 @@ const activityLogController = function () {
7288

7389
if (isAssistedFromClient) {
7490
if (!['Educator', 'Administrator'].includes(currentUser.role)) {
75-
// Unauthorized user tried to set the flag
7691
return res.status(403).json({
7792
error: 'Only educators or administrators can set the assisted flag',
7893
});
7994
}
8095

81-
// Authorized user
8296
isAssisted = true;
8397

8498
if (!assistedUsersFromClient || assistedUsersFromClient.length === 0) {
@@ -87,10 +101,19 @@ const activityLogController = function () {
87101
});
88102
}
89103

90-
// Fetch and map assisted users
91-
const userIds = assistedUsersFromClient.map((u) => u.userId);
104+
if (assistedUsersFromClient.length > 50) {
105+
return res.status(400).json({ error: 'Too many assisted users' });
106+
}
107+
108+
const userIds = assistedUsersFromClient.map((u) => String(u.userId).slice(0, 50));
109+
const uniqueUserIds = [...new Set(userIds)].filter(isValidObjectId);
110+
111+
if (uniqueUserIds.length === 0) {
112+
return res.status(400).json({ error: 'Invalid assisted user IDs' });
113+
}
114+
92115
const usersProfile = await usersProfiles
93-
.find({ _id: { $in: userIds } })
116+
.find({ _id: { $in: uniqueUserIds } })
94117
.select('firstName lastName');
95118

96119
assistedUsers = usersProfile.map((user) => {
@@ -105,19 +128,30 @@ const activityLogController = function () {
105128

106129
return {
107130
user_id: user._id,
108-
name: `${user.firstName} ${user.lastName}`,
131+
name: sanitizeString(`${user.firstName} ${user.lastName}`),
109132
assisted_at: new Date(),
110133
assistance_type: assistanceType,
111134
};
112135
});
113136
}
114137

115-
// Build log object
138+
const sanitizedMetadata = {};
139+
if (metadata && typeof metadata === 'object') {
140+
for (const key of Object.keys(metadata).slice(0, 50)) {
141+
const value = metadata[key];
142+
if (typeof value === 'string') {
143+
sanitizedMetadata[sanitizeString(key)] = sanitizeString(value);
144+
} else if (typeof value === 'number' || typeof value === 'boolean') {
145+
sanitizedMetadata[sanitizeString(key)] = value;
146+
}
147+
}
148+
}
149+
116150
const logData = {
117151
actor_id: currentUser.requestorId,
118152
action_type: actionType,
119-
entity_id: entityId,
120-
metadata: metadata || {},
153+
entity_id: sanitizeString(entityId),
154+
metadata: sanitizedMetadata,
121155
created_at: new Date(),
122156
is_assisted: isAssisted,
123157
assisted_users: assistedUsers,
@@ -145,6 +179,10 @@ const activityLogController = function () {
145179

146180
if (!logId) return res.status(400).json({ error: 'Missing logId' });
147181

182+
if (!isValidObjectId(logId)) {
183+
return res.status(400).json({ error: 'Invalid logId' });
184+
}
185+
148186
if (!['Educator', 'Administrator'].includes(currentUser.role)) {
149187
return res.status(403).json({
150188
error: 'Only educators or administrators can update the assisted flag',
@@ -154,7 +192,6 @@ const activityLogController = function () {
154192
const log = await ActivityLog.findById(logId);
155193
if (!log) return res.status(404).json({ error: 'Activity log not found' });
156194

157-
// Prepare assisted users only if isAssisted is true
158195
let assistedUsers = [];
159196
if (isAssistedFromClient) {
160197
if (!assistedUsersFromClient || assistedUsersFromClient.length === 0) {
@@ -163,13 +200,23 @@ const activityLogController = function () {
163200
});
164201
}
165202

203+
if (assistedUsersFromClient.length > 50) {
204+
return res.status(400).json({ error: 'Too many assisted users' });
205+
}
206+
166207
const validAssistanceTypes = ActivityLog.schema
167208
.path('assisted_users')
168209
.schema.path('assistance_type').enumValues;
169210

170-
const userIds = assistedUsersFromClient.map((u) => u.userId);
211+
const userIds = assistedUsersFromClient.map((u) => String(u.userId).slice(0, 50));
212+
const uniqueUserIds = [...new Set(userIds)].filter(isValidObjectId);
213+
214+
if (uniqueUserIds.length === 0) {
215+
return res.status(400).json({ error: 'Invalid assisted user IDs' });
216+
}
217+
171218
const usersProfile = await usersProfiles
172-
.find({ _id: { $in: userIds } })
219+
.find({ _id: { $in: uniqueUserIds } })
173220
.select('firstName lastName');
174221

175222
assistedUsers = usersProfile.map((user) => {
@@ -184,14 +231,13 @@ const activityLogController = function () {
184231

185232
return {
186233
user_id: user._id,
187-
name: `${user.firstName} ${user.lastName}`,
234+
name: sanitizeString(`${user.firstName} ${user.lastName}`),
188235
assisted_at: new Date(),
189236
assistance_type: assistanceType,
190237
};
191238
});
192239
}
193240

194-
// Update log
195241
log.is_assisted = Boolean(isAssistedFromClient);
196242
log.assisted_users = assistedUsers;
197243
await log.save();
@@ -212,7 +258,11 @@ const activityLogController = function () {
212258
const { studentId } = req.params;
213259
const currentUser = req.body.requestor;
214260
if (!studentId) return res.status(400).json({ error: 'Missing studentId' });
215-
// Add correct rule once you get it so the permission is correct
261+
262+
if (!studentId || typeof studentId !== 'string' || studentId.length > 100) {
263+
return res.status(400).json({ error: 'Invalid studentId' });
264+
}
265+
216266
if (currentUser.role !== 'educator' && currentUser.role !== 'Administrator') {
217267
return res.status(403).json({ error: 'Only Educators can view students logs' });
218268
}

0 commit comments

Comments
 (0)