agentggagentgg
Back to all findings
MEDIUMconfirmedjs-nosql-injectionjs-nosql-injectionc6b85b9a1c88

Operator/object injection via JSON.parse() of route parameter into Sequelize where clause

`getRecycleItem` runs `RecycleModel.findAll({ where: { id: JSON.parse(req.params.id) } })`, letting an attacker substitute the scalar id with arbitrary JSON (objects, arrays, operator hashes) and influence the resulting query semantics.

Fileroutes/recycles.ts
Lines1216
Confidence
85%
File statusvalidated
Details

Lines 12–16 of routes/recycles.ts:

RecycleModel.findAll({
  where: {
    id: JSON.parse(req.params.id)
  }
})

The brief's true-positive pattern "JSON.parse(req.body.*) as a query argument" applies directly: whatever JSON the caller supplies becomes the id selector. Although the underlying store is SQL (Sequelize), the JSON.parse hop allows the attacker to smuggle non-scalar values into the where clause:

  • /api/Recycles/[1,2,3] becomes WHERE id IN (1,2,3) — pulls records the user shouldn't see.
  • /api/Recycles/{"$gt":0} or /api/Recycles/{"gt":0} (depending on Sequelize version) maps to operator semantics returning every recycle row.
  • Object payloads crash the DB layer in ways that leak SQL/Sequelize stack traces (this is the documented Juice Shop "REST API" / "JSON" injection challenge).

The correct shape is to coerce: id: Number(req.params.id) (and 400 on NaN). There is no coercion or schema validation between the request and the query.

Proof of concept

GET /api/Recycles/%7B%22%24gt%22%3A0%7D (URL-encoded {"$gt":0}) — unauthenticated. The server JSON-parses the param and passes the resulting object as where.id, returning all recycle rows rather than the single requested record.

Impact

Unauthenticated horizontal authorization bypass / data exposure: an attacker can enumerate or dump the entire Recycle table by sending a JSON object instead of a numeric id. Malformed payloads also leak Sequelize error stack traces.

Validation
confirmed

The code literally passes JSON.parse(req.params.id) into where: { id: ... } with no coercion, schema validation, or auth check in this handler. An attacker can supply a JSON array ([1,2,3]) or operator object that Sequelize will interpret as an IN/operator filter, returning rows beyond the intended single id — this is the canonical Juice Shop "REST API JSON injection" pattern. The catch block also discards the underlying error, but pre-parse failures still leak via crashes/errors. Exploit chain (untrusted route param → JSON.parse → ORM where clause) is reachable and matches the detector's PoC.

CVSS 3.1
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N
Base score: 5.3 · MEDIUM

The sink is a plain Express GET route (getRecycleItem) with no authentication or authorization middleware visible in the file, so an unauthenticated remote attacker can hit /api/Recycles/... directly (AV:N, PR:N, UI:N, AC:L). Supplying a JSON operator object (e.g. {"$gt":0}) for req.params.id causes RecycleModel.findAll to return rows the attacker should not see, disclosing data from the Recycle table — but the bug is confined to that single read-only findAll against one model, so impact stays within the app (S:U), there is no write/update path (I:N), and the .catch swallows errors so no meaningful DoS is demonstrated (A:N). Confidentiality is rated L rather than H because disclosure is limited to the Recycle table contents, not the entire datastore or arbitrary records of the attacker's choosing.

References