IDOR in PATCH /rest/products/reviews — any authenticated user can edit any product review
The authenticated review-update handler updates a Mongo review document looked up by client-supplied `_id` without verifying the caller is the review author, letting any logged-in user overwrite anyone else's product review.
Route in server.ts line 625: app.patch('/rest/products/reviews', security.isAuthorized(), updateProductReviews()) — only authentication, no authorship check.
routes/updateProductReviews.ts:
export function updateProductReviews () {
return (req: Request, res: Response, next: NextFunction) => {
const user = security.authenticatedUsers.from(req)
db.reviewsCollection.update(
{ _id: req.body.id },
{ $set: { message: req.body.message } },
{ multi: true }
).then(
(result) => {
challengeUtils.solveIf(challenges.noSqlReviewsChallenge, ...)
challengeUtils.solveIf(challenges.forgedReviewChallenge, () => { return user?.data && result.original[0] && result.original[0].author !== user.data.email && result.modified === 1 })
res.json(result)
}, ...)
}
}
The filter passed to reviewsCollection.update is { _id: req.body.id } — only the review id from the body. user.data.email is read but only to award a CTF challenge; it never gates the update. With multi: true an attacker can additionally pass a NoSQL operator like {"$ne":null} to mass-update every review.
Authenticated as any user:
PATCH /rest/products/reviews HTTP/1.1
Host: target
Authorization: Bearer <attacker-JWT>
Content-Type: application/json
{ "id": "<victim review _id>", "message": "Pwned by attacker" }
The review with that _id (authored by a different user) is overwritten. Replacing the id with {"$ne": -1} rewrites all reviews in one request.
Any authenticated user can tamper with any other user's product reviews — defacement, slander, or wholesale destruction of review content via the NoSQL $ne trick.
The handler calls db.reviewsCollection.update({ _id: req.body.id }, { $set: { message: req.body.message } }, { multi: true }) using only the client-supplied id with no author === user.data.email predicate. The forgedReviewChallenge check even confirms the design intent — it fires precisely when the original author differs from the caller, meaning cross-author edits succeed. With multi: true and no input sanitization, a NoSQL operator like {"$ne": -1} also enables mass overwrite. Scope explicitly says to treat findings as real production bugs, so this is a legitimate IDOR.
The endpoint is reachable over HTTP (AV:N) and only requires any authenticated session via security.isAuthorized() in server.ts line 625 (PR:L); no victim interaction is needed (UI:N) and no race/precondition exists (AC:L). The handler filters solely by req.body.id with multi: true and never compares the review's author to user.data.email before writing, so an attacker can overwrite any single review or, by sending {"$ne": -1}, every review in the collection in one request — total integrity impact within the reviews store (I:H). The response echoes result.original (which includes author/message metadata) back to the caller, giving limited disclosure of other users' review records (C:L); no DoS or data destruction primitive beyond integrity tampering is shown (A:N), and the impact stays within the application's own data store (S:U).
- CWE-639
- CWE-862
- OWASP A01:2021 - Broken Access Control