Summary
Authorization checks are missing when awarding points to a user. The point award flow validates the acting user's organization but does not verify that the target userId belongs to the same organization.
As a result, an authorized user may be able to award points to users outside their organization if they know a valid user ID.
Steps to reproduce
- Log in as a user who has permission to award points.
- Obtain the ID of a user belonging to a different organization.
- Send a point award request using that user's ID.
Example:
{
"userId": "<user-from-another-organization>",
"points": 100,
"reason": "Test award"
}
- Observe that the request succeeds and the target user's points are modified.
Actual behavior
The service updates the target user's points based only on the supplied userId.
No validation is performed to ensure the target user belongs to the same organization as the requester.
Expected behavior
Before awarding points, the application should verify that:
- The target user exists.
- The target user belongs to the same organization as the requester (or another explicitly authorized organization).
Requests targeting users outside the requester's organization should be rejected.
Reproduction details
- OS: Any
- Browser / environment: Any
- Version: Current repository state
- Device: Any
Screenshots / Logs
Relevant code path:
await creditPoints({
userId,
organizationId,
...
});
Service implementation:
const user = await User.findByIdAndUpdate(
userId,
{
$inc: {
points: amount,
totalPointsEarned: amount,
},
},
{ new: true },
);
The update is performed without validating organization ownership.
Additional context
The transaction record stores the requester's organization:
organization: organizationId
However, the updated user is selected only by _id, creating a potential cross-tenant modification issue if user IDs from other organizations are known.
Proposed fix (optional)
Validate organization ownership before updating the user:
const user = await User.findOneAndUpdate(
{
_id: userId,
organization: organizationId,
},
{
$inc: {
points: amount,
totalPointsEarned: amount,
},
},
{ new: true },
);
Alternatively, explicitly compare the target user's organization with the requester's organization and reject mismatches with a 403 Forbidden response.
Summary
Authorization checks are missing when awarding points to a user. The point award flow validates the acting user's organization but does not verify that the target
userIdbelongs to the same organization.As a result, an authorized user may be able to award points to users outside their organization if they know a valid user ID.
Steps to reproduce
Example:
{ "userId": "<user-from-another-organization>", "points": 100, "reason": "Test award" }Actual behavior
The service updates the target user's points based only on the supplied
userId.No validation is performed to ensure the target user belongs to the same organization as the requester.
Expected behavior
Before awarding points, the application should verify that:
Requests targeting users outside the requester's organization should be rejected.
Reproduction details
Screenshots / Logs
Relevant code path:
Service implementation:
The update is performed without validating organization ownership.
Additional context
The transaction record stores the requester's organization:
organization: organizationIdHowever, the updated user is selected only by
_id, creating a potential cross-tenant modification issue if user IDs from other organizations are known.Proposed fix (optional)
Validate organization ownership before updating the user:
Alternatively, explicitly compare the target user's organization with the requester's organization and reject mismatches with a
403 Forbiddenresponse.