mirror of
https://github.com/wekan/wekan.git
synced 2025-12-16 15:30:13 +01:00
Security Fix usd-2022-0041: CWE-284 Improper Access Control.
Thanks to Christian Pöschl of usd AG and xet7 !
This commit is contained in:
parent
6bbd622066
commit
f6591d7820
2 changed files with 112 additions and 2 deletions
|
|
@ -588,15 +588,37 @@ Users.deny({
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Custom MongoDB engine that enforces field restrictions
|
||||||
|
class SecureMongoDBEngine extends MongoDBEngine {
|
||||||
|
getSearchCursor(searchObject, options) {
|
||||||
|
// Always enforce field projection to prevent data leakage
|
||||||
|
const secureProjection = {
|
||||||
|
_id: 1,
|
||||||
|
username: 1,
|
||||||
|
'profile.fullname': 1,
|
||||||
|
'profile.avatarUrl': 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Override any projection passed in options
|
||||||
|
const secureOptions = {
|
||||||
|
...options,
|
||||||
|
projection: secureProjection,
|
||||||
|
};
|
||||||
|
|
||||||
|
return super.getSearchCursor(searchObject, secureOptions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Search a user in the complete server database by its name, username or emails adress. This
|
// Search a user in the complete server database by its name, username or emails adress. This
|
||||||
// is used for instance to add a new user to a board.
|
// is used for instance to add a new user to a board.
|
||||||
UserSearchIndex = new Index({
|
UserSearchIndex = new Index({
|
||||||
collection: Users,
|
collection: Users,
|
||||||
fields: ['username', 'profile.fullname', 'profile.avatarUrl'],
|
fields: ['username', 'profile.fullname', 'profile.avatarUrl'],
|
||||||
allowedFields: ['username', 'profile.fullname', 'profile.avatarUrl'],
|
allowedFields: ['username', 'profile.fullname', 'profile.avatarUrl', '_id'],
|
||||||
engine: new MongoDBEngine({
|
engine: new SecureMongoDBEngine({
|
||||||
fields: function (searchObject, options) {
|
fields: function (searchObject, options) {
|
||||||
return {
|
return {
|
||||||
|
_id: 1,
|
||||||
username: 1,
|
username: 1,
|
||||||
'profile.fullname': 1,
|
'profile.fullname': 1,
|
||||||
'profile.avatarUrl': 1,
|
'profile.avatarUrl': 1,
|
||||||
|
|
@ -2756,6 +2778,51 @@ if (Meteor.isServer) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Server-side method to sanitize user data for search results
|
||||||
|
Meteor.methods({
|
||||||
|
sanitizeUserForSearch(userData) {
|
||||||
|
check(userData, Object);
|
||||||
|
|
||||||
|
// Only allow safe fields for user search
|
||||||
|
const safeFields = {
|
||||||
|
_id: 1,
|
||||||
|
username: 1,
|
||||||
|
'profile.fullname': 1,
|
||||||
|
'profile.avatarUrl': 1,
|
||||||
|
'profile.initials': 1,
|
||||||
|
'emails.address': 1,
|
||||||
|
'emails.verified': 1,
|
||||||
|
authenticationMethod: 1,
|
||||||
|
isAdmin: 1,
|
||||||
|
loginDisabled: 1,
|
||||||
|
teams: 1,
|
||||||
|
orgs: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
const sanitized = {};
|
||||||
|
for (const field of Object.keys(safeFields)) {
|
||||||
|
if (userData[field] !== undefined) {
|
||||||
|
sanitized[field] = userData[field];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure sensitive fields are never included
|
||||||
|
delete sanitized.services;
|
||||||
|
delete sanitized.resume;
|
||||||
|
delete sanitized.email;
|
||||||
|
delete sanitized.createdAt;
|
||||||
|
delete sanitized.modifiedAt;
|
||||||
|
delete sanitized.sessionData;
|
||||||
|
delete sanitized.importUsernames;
|
||||||
|
|
||||||
|
if (process.env.DEBUG === 'true') {
|
||||||
|
console.log('Sanitized user data for search:', Object.keys(sanitized));
|
||||||
|
}
|
||||||
|
|
||||||
|
return sanitized;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Users;
|
export default Users;
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,49 @@ Meteor.publish('user-authenticationMethod', function (match) {
|
||||||
return ret;
|
return ret;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Secure user search publication for board sharing
|
||||||
|
Meteor.publish('user-search', function (searchTerm) {
|
||||||
|
check(searchTerm, String);
|
||||||
|
|
||||||
|
// Only allow logged-in users to search for other users
|
||||||
|
if (!this.userId) {
|
||||||
|
return this.ready();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a regex for case-insensitive search
|
||||||
|
const searchRegex = new RegExp(searchTerm, 'i');
|
||||||
|
|
||||||
|
// Search for users by username, fullname, or email
|
||||||
|
const ret = ReactiveCache.getUsers(
|
||||||
|
{
|
||||||
|
$or: [
|
||||||
|
{ username: searchRegex },
|
||||||
|
{ 'profile.fullname': searchRegex },
|
||||||
|
{ 'emails.address': searchRegex }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fields: {
|
||||||
|
_id: 1,
|
||||||
|
username: 1,
|
||||||
|
'profile.fullname': 1,
|
||||||
|
'profile.avatarUrl': 1,
|
||||||
|
'profile.initials': 1,
|
||||||
|
'emails.address': 1,
|
||||||
|
'emails.verified': 1,
|
||||||
|
authenticationMethod: 1,
|
||||||
|
isAdmin: 1,
|
||||||
|
loginDisabled: 1,
|
||||||
|
teams: 1,
|
||||||
|
orgs: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
});
|
||||||
|
|
||||||
// update last connection date and last connection average time (in seconds) for a user
|
// update last connection date and last connection average time (in seconds) for a user
|
||||||
// function UpdateLastConnectionDateAndLastConnectionAverageTime(lstUsers) {
|
// function UpdateLastConnectionDateAndLastConnectionAverageTime(lstUsers) {
|
||||||
// let lastConnectionAverageTime;
|
// let lastConnectionAverageTime;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue