mirror of
https://github.com/wekan/wekan.git
synced 2025-12-16 07:20:12 +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
|
||||
// is used for instance to add a new user to a board.
|
||||
UserSearchIndex = new Index({
|
||||
collection: Users,
|
||||
fields: ['username', 'profile.fullname', 'profile.avatarUrl'],
|
||||
allowedFields: ['username', 'profile.fullname', 'profile.avatarUrl'],
|
||||
engine: new MongoDBEngine({
|
||||
allowedFields: ['username', 'profile.fullname', 'profile.avatarUrl', '_id'],
|
||||
engine: new SecureMongoDBEngine({
|
||||
fields: function (searchObject, options) {
|
||||
return {
|
||||
_id: 1,
|
||||
username: 1,
|
||||
'profile.fullname': 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;
|
||||
|
|
|
|||
|
|
@ -49,6 +49,49 @@ Meteor.publish('user-authenticationMethod', function (match) {
|
|||
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
|
||||
// function UpdateLastConnectionDateAndLastConnectionAverageTime(lstUsers) {
|
||||
// let lastConnectionAverageTime;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue