GHSA-wr5m-5jph-8m96

Suggest an improvement
Source
https://github.com/advisories/GHSA-wr5m-5jph-8m96
Import Source
https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/11/GHSA-wr5m-5jph-8m96/GHSA-wr5m-5jph-8m96.json
JSON Data
https://api.osv.dev/v1/vulns/GHSA-wr5m-5jph-8m96
Aliases
Published
2026-11-10T20:57:18Z
Modified
2026-11-10T21:15:03Z
Severity
  • 4.8 (Medium) CVSS_V3 - CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:L CVSS Calculator
Summary
Mongoose: Discriminator key values matching inherited properties (e.g. __t: 'constructor') bypass schema casting and can poison collections
Details

Summary

Mongoose chooses which discriminator model or schema to use by looking up the discriminator key value (__t by default) in a plain object of registered discriminators, for example Model.discriminators[doc.__t]. That value comes from user documents and query filters. Because the lookup did not check own properties, a value that names a property inherited from Object.prototype resolves to that inherited value instead of a discriminator. Examples are 'constructor', 'toString' and 'hasOwnProperty'.

  • __t: 'constructor' resolves to the global Object function. Mongoose then calls new Object(doc), which returns the raw user object where a Mongoose document is expected.
  • __t: 'toString' and similar names resolve to built-in functions that throw when used as a constructor or schema.

The most serious effect: through bulkWrite() insertOne with skipValidation: true, the raw user object is stored as-is, without schema casting or strict filtering. After that, every query that returns the stored document throws, until the document is removed from the database.

Details

The registries that hold discriminators are plain objects: Model.discriminators, schema.discriminators, and the embedded discriminator registries on document arrays and single nested subdocuments. Several code paths indexed them directly with a value taken from the document or filter:

Code path Input
Model constructor (new Model(doc)) document
Model.create(), insertMany(), bulkWrite(), bulkSave() documents
Hydrating query results (find(), findOne(), etc.) and Model.hydrate() stored documents
Casting query filters, including flat paths such as 'events.__t' and $elemMatch query filter
Casting updates and array filters update filter, arrayFilters
Document array push() / create(), document array and single nested casting subdocuments
Populate on embedded discriminators stored subdocuments

getDiscriminatorByValue() already iterated the registry's own keys safely. But most callers tried the direct lookup first and only fell back to it afterwards, so a truthy inherited value took precedence.

The lookups only read inherited properties. They do not write to Object.prototype or any other shared object, and they do not change in-memory state that affects other requests. This is NOT a prototype pollution vector.

Proof of concept

const mongoose = require('mongoose');

const Base = mongoose.model('Base', new mongoose.Schema({ name: String, age: Number }));
Base.discriminator('Child', new mongoose.Schema({ extra: String }));

// Type confusion: the "document" is the raw input object
const doc = new Base({ name: 'x', __t: 'constructor' });
console.log(doc instanceof mongoose.Document); // false
// Schema bypass and persistent poison document via bulkWrite + skipValidation
await Base.bulkWrite([{
  insertOne: { document: { name: 'x', age: 'not a number', notInSchema: true, __t: 'constructor' } }
}], { skipValidation: true });
// Stored verbatim: { age: 'not a number', notInSchema: true, __t: 'constructor', ... }

await Base.find(); // throws: Cannot read properties of undefined (reading 'eachPath')

Impact

  • Crashing the request. An attacker who controls the discriminator key of a document or filter can make the request fail. In most code paths that sent the value, this means a TypeError or a non-document object returned to application code. Ways in include Model.create(req.body), new Model(req.body), Model.find(req.query)-style filters, and pushing user input onto embedded discriminator arrays. The effect is limited to the request carrying the value.
  • Schema bypass on bulkWrite({ skipValidation: true }). When an application passes untrusted documents to bulkWrite() insertOne with skipValidation: true, the raw object is stored without casting or strict filtering. Fields the schema would normally strip are persisted, such as an isAdmin that isn't in the schema. Fields with wrong types are also persisted.
  • Persistent denial of service for queries. A stored document with such a discriminator key value makes hydration throw. Every query that returns that document fails, for all users, until the document is deleted directly in the database. Besides the bulkWrite() path above, the document can get into the collection through another application, the raw MongoDB driver, or any other writer that does not go through Mongoose casting.

This issue doesn't let an attacker modify Object.prototype, built-in functions, schemas or other shared in-memory state. The schema bypass requires the application to opt in to skipValidation with untrusted input.

Database specific
{
    "cwe_ids": [
        "CWE-843"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-11-10T20:57:18Z",
    "nvd_published_at": "2026-11-09T23:17:21Z",
    "severity": "MODERATE"
}
References

Affected packages

npm / mongoose

Package

Affected ranges

Type
SEMVER
Events
Introduced
0 Unknown introduced version / All previous versions are affected
Fixed
6.13.12

Database specific

source
"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/11/GHSA-wr5m-5jph-8m96/GHSA-wr5m-5jph-8m96.json"

npm / mongoose

Package

Affected ranges

Type
SEMVER
Events
Introduced
7.0.0
Fixed
7.8.13

Database specific

source
"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/11/GHSA-wr5m-5jph-8m96/GHSA-wr5m-5jph-8m96.json"

npm / mongoose

Package

Affected ranges

Type
SEMVER
Events
Introduced
8.0.0
Fixed
8.24.7

Database specific

source
"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/11/GHSA-wr5m-5jph-8m96/GHSA-wr5m-5jph-8m96.json"

npm / mongoose

Package

Affected ranges

Type
SEMVER
Events
Introduced
9.0.0
Fixed
9.11.4

Database specific

source
"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/11/GHSA-wr5m-5jph-8m96/GHSA-wr5m-5jph-8m96.json"