Every CVESearch/Get API key

CVE-2026-125116

Mongoose: Discriminator key values matching inherited properties (e.g. __t: 'constructor') bypass schema casting and can poison collections

4.8 MediumFix available

Mongoose is a MongoDB object modeling tool designed to work in an asynchronous environment. Prior to 6.13.12, 7.8.13, 8.24.7, and 9.11.4, Mongoose uses attacker-controlled discriminator key values, including the default __t field, to index plain-object registries such as Model.discriminators and schema.discriminators without requiring an own property. Values such as constructor, toString, or hasOwnProperty can therefore resolve to inherited built-in functions instead of a registered discriminator, causing type confusion in model construction, document creation, hydration, query and update casting, embedded discriminators, and populate operations. In the bulkWrite() insertOne path with skipValidation enabled, a constructor value can cause a raw input object to be stored without schema casting or strict filtering, and a stored poisoned document can make every query that hydrates it fail until the document is removed. The weakness reads inherited properties but does not modify Object.prototype and is not prototype pollution. This issue is fixed in versions 6.13.12, 7.8.13, 8.24.7, and 9.11.4.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
High
Privileges needed
None
User interaction
None

What an attacker can affect

Confidentiality
None
Integrity
Low
Availability
Low

Exploit likelihood

0.34%Unchanged since Nov 10, 2026
90%50%10%1%

What each source reports

CVE List Score used4.8

Mongoose: Discriminator key values matching inherited properties (e.g. __t: 'constructor') bypass schema casting and can poison collections

Mongoose is a MongoDB object modeling tool designed to work in an asynchronous environment. Prior to 6.13.12, 7.8.13, 8.24.7, and 9.11.4, Mongoose uses attacker-controlled discriminator key values, including the default __t field, to index plain-object registries such as Model.discriminators and schema.discriminators without requiring an own property. Values such as constructor, toString, or hasOwnProperty can therefore resolve to inherited built-in functions instead of a registered discriminator, causing type confusion in model construction, document creation, hydration, query and update casting, embedded discriminators, and populate operations. In the bulkWrite() insertOne path with skipValidation enabled, a constructor value can cause a raw input object to be stored without schema casting or strict filtering, and a stored poisoned document can make every query that hydrates it fail until the document is removed. The weakness reads inherited properties but does not modify Object.prototype and is not prototype pollution. This issue is fixed in versions 6.13.12, 7.8.13, 8.24.7, and 9.11.4.

Record
CVE-2026-125116
CVSS 3.1
4.8 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:L
Weakness
CWE-843
Updated
2026-11-09
EUVDEUVD-2026-1204974.8

Mongoose: Discriminator key values matching inherited properties (e.g. __t: 'constructor') bypass schema casting and can poison collections

Record
EUVD-2026-120497
CVSS 3.1
4.8 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:L
Updated
2026-11-10
GitHub AdvisoriesGHSA-wr5m-5jph-8m964.8

Mongoose: Discriminator key values matching inherited properties (e.g. __t: 'constructor') bypass schema casting and can poison collections

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…
Record
GHSA-wr5m-5jph-8m96
CVSS 3.1
4.8 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:L
Weakness
CWE-843
Updated
2026-11-10
NVD4.8

Mongoose is a MongoDB object modeling tool designed to work in an asynchronous environment. Prior to 6.13.12, 7.8.13, 8.24.7, and 9.11.4, Mongoose uses attacker-controlled discriminator key values, including the default __t field, to index plain-object registries such as Model.discriminators and schema.discriminators without requiring an own property. Values such as constructor, toString, or hasOwnProperty can therefore resolve to inherited built-in functions instead of a registered discriminator, causing type confusion in model construction, document creation, hydration, query and update casting, embedded discriminators, and populate operations. In the bulkWrite() insertOne path with skipValidation enabled, a constructor value can cause a raw input object to be stored without schema casting or strict filtering, and a stored poisoned document can make every query that hydrates it fail until the document is removed. The weakness reads inherited properties but does not modify Object.prototype and is not prototype pollution. This issue is fixed in versions 6.13.12, 7.8.13, 8.24.7, and 9.11.4.

Record
CVE-2026-125116
CVSS 3.1
4.8 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:L
Weakness
CWE-843
Updated
2026-11-10
OSV4.8

Mongoose: Discriminator key values matching inherited properties (e.g. __t: 'constructor') bypass schema casting and can poison collections

Mongoose is a MongoDB object modeling tool designed to work in an asynchronous environment. Prior to 6.13.12, 7.8.13, 8.24.7, and 9.11.4, Mongoose uses attacker-controlled discriminator key values, including the default __t field, to index plain-object registries such as Model.discriminators and schema.discriminators without requiring an own property. Values such as constructor, toString, or hasOwnProperty can therefore resolve to inherited built-in functions instead of a registered discriminator, causing type confusion in model construction, document creation, hydration, query and update casting, embedded discriminators, and populate operations. In the bulkWrite() insertOne path with skipValidation enabled, a constructor value can cause a raw input object to be stored without schema casting or strict filtering, and a stored poisoned document can make every query that hydrates it fail until the document is removed. The weakness reads inherited properties but does not modify Object.prototype and is not prototype pollution. This issue is fixed in versions 6.13.12, 7.8.13, 8.24.7, and 9.11.4.

Record
CVE-2026-125116
CVSS 3.1
4.8 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:L
Weakness
CWE-843
Updated
2026-11-11
OSVGHSA-wr5m-5jph-8m964.8

Mongoose: Discriminator key values matching inherited properties (e.g. __t: 'constructor') bypass schema casting and can poison collections

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…
Record
GHSA-wr5m-5jph-8m96
CVSS 3.1
4.8 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:L
Weakness
CWE-843
Updated
2026-11-10

Affected products

Affected packages

Affected packages
PackageAffectedFixed
mongoose
npm
before 6.13.12
6.13.12
mongoose
npm
7.0.0 up to 7.8.13
7.8.13
mongoose
npm
8.0.0 up to 8.24.7
8.24.7
mongoose
npm
9.0.0 up to 9.11.4
9.11.4

History

History
RecordedChangedSources
publishedEUVD
affected, referencesOSV
affected, facts, referencesGitHub Advisories
aliases, referencesNVD, OSV
cwes, references, severity, titleCVE List
first indexedEUVD

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-125116 is a medium vulnerability (CVSS 4.8) in Automattic Mongoose, published November 9, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.34%, the 27th percentile. Fixed in mongoose 6.13.12.

References

Show 9 moreShow less
This page doesn't list everything

Left out here: the rest of 2 source descriptions. The JSON record has them.

MoveEnter Open