agentggagentgg
Back to all findings
HIGHconfirmedmissing-access-controlidor-update-review223087448966

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.

Fileroutes/updateProductReviews.ts
Lines1329
Confidence
97%
File statusvalidated
Details

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.

Proof of concept

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.

Impact

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.

Validation
confirmed

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.

CVSS 3.1
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:H/A:N
Base score: 7.1 · HIGH

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).

References