{"id":"CVE-2026-125116","kind":"vulnerability","aliases":["CVE-2026-125116","GHSA-wr5m-5jph-8m96"],"related":[],"title":"Mongoose: Discriminator key values matching inherited properties (e.g. __t: 'constructor') bypass schema casting and can poison collections","description":"### Summary\n\nMongoose 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'`.\n\n- `__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.\n- `__t: 'toString'` and similar names resolve to built-in functions that throw when used as a constructor or schema.\n\nThe 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.\n\n### Details\n\nThe 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:\n\n| Code path | Input |\n|---|---|\n| Model constructor (`new Model(doc)`) | document |\n| `Model.create()`, `insertMany()`, `bulkWrite()`, `bulkSave()` | documents |\n| Hydrating query results (`find()`, `findOne()`, etc.) and `Model.hydrate()` | stored documents |\n| Casting query filters, including flat paths such as `'events.__t'` and `$elemMatch` | query filter |\n| Casting updates and array filters | update filter, `arrayFilters` |\n| Document array `push()` / `create()`, document array and single nested casting | subdocuments |\n| Populate on embedded discriminators | stored subdocuments |\n\n`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.\n\nThe 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.\n\n### Proof of concept\n\n```js\nconst mongoose = require('mongoose');\n\nconst Base = mongoose.model('Base', new mongoose.Schema({ name: String, age: Number }));\nBase.discriminator('Child', new mongoose.Schema({ extra: String }));\n\n// Type confusion: the \"document\" is the raw input object\nconst doc = new Base({ name: 'x', __t: 'constructor' });\nconsole.log(doc instanceof mongoose.Document); // false\n```\n\n```js\n// Schema bypass and persistent poison document via bulkWrite + skipValidation\nawait Base.bulkWrite([{\n  insertOne: { document: { name: 'x', age: 'not a number', notInSchema: true, __t: 'constructor' } }\n}], { skipValidation: true });\n// Stored verbatim: { age: 'not a number', notInSchema: true, __t: 'constructor', ... }\n\nawait Base.find(); // throws: Cannot read properties of undefined (reading 'eachPath')\n```\n\n### Impact\n\n- **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.\n- **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.\n- **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.\n\nThis 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.","summary":"Mongoose looks up discriminator models using the discriminator key value without checking own properties, so a value such as 'constructor' resolves to an inherited built-in. An attacker who controls that value can make requests fail, and with bulkWrite insertOne and skipValidation true can store an unvalidated document that makes every query returning it throw.","severity":"medium","cvss":4.8,"epss":null,"exploited":false,"exploited_at":null,"products":[{"name":"mongoose","fixed":["6.13.12"],"affected":"< 6.13.12","ecosystem":"npm"},{"name":"mongoose","fixed":["7.8.13"],"affected":">= 7.0.0, < 7.8.13","ecosystem":"npm"},{"name":"mongoose","fixed":["8.24.7"],"affected":">= 8.0.0, < 8.24.7","ecosystem":"npm"},{"name":"mongoose","fixed":["9.11.4"],"affected":">= 9.0.0, < 9.11.4","ecosystem":"npm"}],"ecosystems":["npm"],"tags":["cwe-843","discriminators","mongodb","mongoose","node.js","package"],"not_affected_if":["no-discriminators","no-user-controlled-input","skipvalidation-unused"],"how_to_check":"- Check the installed version with `npm ls mongoose`.\n- Look for `bulkWrite(` calls that pass `skipValidation: true` with untrusted input.\n- Check whether user input reaches `new Model(req.body)`, `Model.create()` or query filters that can contain the discriminator key `__t`.","fix":"- `mongoose` 6.x: move to `6.13.12`\n- `mongoose` 7.x: move to `7.8.13`\n- `mongoose` 8.x: move to `8.24.7`\n- `mongoose` 9.x: move to `9.11.4`","status":"open","published_at":"2026-11-09T23:17:21+00:00","first_seen_at":"2026-11-10T21:02:22+00:00","triaged_at":"2026-11-10T21:03:59+00:00","updated_at":"2026-11-10T21:15:58+00:00","category":"package","confidence":0.8,"triage":{"at":"2026-11-10T21:03:59Z","model":"claude-haiku-5-5","output":{"fix":"- `mongoose` 6.x: move to `6.13.12`\n- `mongoose` 7.x: move to `7.8.13`\n- `mongoose` 8.x: move to `8.24.7`\n- `mongoose` 9.x: move to `9.11.4`","reach":"internet","title":"Mongoose: discriminator key named like an inherited property breaks queries or stores bad data","impact":"medium","summary":"Mongoose looks up discriminator models using the discriminator key value without checking own properties, so a value such as 'constructor' resolves to an inherited built-in. An attacker who controls that value can make requests fail, and with bulkWrite insertOne and skipValidation true can store an unvalidated document that makes every query returning it throw.","audience":"developers","category":"package","nicknames":[],"who_cares":["mongoose","node.js","mongodb","discriminators"],"confidence":0.8,"fix_status":"available","needs_auth":"unknown","worst_case":"requests fail; stored documents break queries until deleted","analyst_note":"","how_to_check":"- Check the installed version with `npm ls mongoose`.\n- Look for `bulkWrite(` calls that pass `skipValidation: true` with untrusted input.\n- Check whether user input reaches `new Model(req.body)`, `Model.create()` or query filters that can contain the discriminator key `__t`.","plain_action":"","product_name":"","needs_analyst":false,"not_affected_if":["no-discriminators","no-user-controlled-input","skipvalidation-unused"],"who_is_affected":"Applications that use Mongoose discriminators and pass user-controlled documents or filters into model construction, create, bulkWrite or queries. The worst outcome, persistent query failure, needs bulkWrite with skipValidation true and untrusted input, or a document written to the collection by another writer. Applications without discriminators or without user-controlled input are unlikely to be reached.","user_interaction":"no","product_ecosystem":"","needs_special_setup":"yes","severity_if_unknown":"medium"},"cost_usd":0.000995,"input_tokens":6410,"output_tokens":707,"severity_before":"medium"},"analyst_status":null,"analyst_note":null,"search":"'-125116':20A,28A '-2026':19A,27A '-843':34B '5jph':24A '8m96':25A 'affect':415C 'afterward':380C 'age':441C,487C,499C 'alreadi':358C 'applic':558C,598C 'array':275C,335C,341C,345C,579C 'arrayfilt':339C 'as-i':230C 'attack':68B,522C 'await':481C,507C 'back':377C 'base':434C,436C,461C 'base.bulkwrite':482C 'base.discriminator':443C 'base.find':508C 'built':65B,202C 'built-in':64B,201C 'bulksav':307C 'bulkwrit':79B,219C,306C,479C,593C,603C 'bypass':11A,473C,591C 'call':178C 'caller':368C 'cannot':510C 'carri':587C 'cast':13A,235C,319C,332C,349C,614C 'chang':409C 'check':51B,141C 'child':444C 'choos':98C 'code':281C,295C,540C,559C 'collect':17A 'come':129C 'concept':427C 'confus':450C 'console.log':466C 'const':429C,433C,458C 'constructor':10A,59B,164C,169C,211C,299C,465C,494C,506C 'control':70B,524C 'crash':518C 'creat':343C 'cve':18A,26A 'cwe':33B 'databas':256C 'default':115C 'detail':257C 'direct':285C,371C 'discrimin':2A,35B,43B,47B,100C,110C,122C,161C,262C,271C,354C,526C,578C 'doc':181C,302C,459C,467C 'doc.__t':126C 'document':88B,132C,191C,247C,251C,274C,292C,303C,308C,318C,340C,344C,452C,477C,484C,530C,554C,601C 'e.g':8A 'eachpath':516C 'effect':217C,581C 'elemmatch':329C 'embed':270C,353C,577C 'etc':314C 'events.__t':327C 'everi':91B,241C 'exampl':124C,162C 'expect':193C 'extra':447C 'fail':76B,537C 'fals':470C 'fell':376C 'field':618C 'filter':135C,238C,294C,321C,331C,336C,338C,532C,571C,617C 'find':312C 'findon':313C 'first':373C 'flat':323C 'function':175C,204C 'getdiscriminatorbyvalu':357C 'ghsa':22A 'ghsa-wr5m-5jph-8m96':21A 'global':173C 'hasownproperti':167C 'hold':261C 'hydrat':309C 'impact':517C 'in-memori':410C 'includ':322C,562C 'index':283C 'inherit':6A,63B,150C,156C,384C,392C 'input':297C,456C,575C 'insertmani':305C 'inserton':80B,220C,483C,604C 'instanceof':468C 'instead':158C 'iter':359C 'js':428C,471C 'key':3A,48B,111C,364C,527C 'limit':583C 'look':41B,107C 'lookup':138C,372C,389C 'make':74B,90B,534C 'match':5A 'mean':547C 'memori':412C 'model':44B,101C,298C,301C,566C 'model.create':304C,563C 'model.discriminators':125C,266C 'model.find':568C 'model.hydrate':316C 'mongodb':36B 'mongoos':1A,37B,40B,97C,176C,190C,430C,432C 'mongoose':29A,30A,31A,32A 'mongoose.document':469C 'mongoose.model':435C 'mongoose.schema':438C,446C 'name':147C,198C,439C,462C,485C 'nest':278C,348C 'new':179C,300C,437C,445C,460C,565C 'node.js':38B 'non':553C 'non-docu':552C 'normal':622C 'notinschema':491C,503C 'number':442C,490C,502C 'object':119C,174C,180C,187C,227C,265C,404C,457C,555C,610C 'object.prototype':152C,399C 'onto':576C 'packag':39B 'pass':599C 'path':282C,296C,324C,541C 'persist':475C 'plain':118C,264C 'poison':16A,476C 'pollut':423C 'popul':351C 'preced':387C 'proof':425C 'properti':7A,53B,143C,149C,393C,512C 'prototyp':422C 'push':342C,573C 'queri':92B,134C,242C,310C,320C,330C 'raw':185C,225C,455C,609C 'read':391C,511C,515C 'regist':121C 'registri':259C,272C,361C 'remov':253C 'req.body':564C,567C 'req.query':569C 'request':75B,417C,520C,536C,586C 'requir':431C 'resolv':60B,153C,170C,199C 'result':311C 'return':93B,183C,244C,556C 'safe':365C 'schema':12A,103C,213C,234C,472C,590C,620C 'schema.discriminators':267C 'sent':543C 'serious':216C 'sever':280C 'share':403C 'similar':197C 'singl':277C,347C 'skipvalid':82B,222C,480C,495C,594C,606C 'state':413C 'store':85B,229C,246C,317C,355C,497C,612C 'strict':237C,616C 'string':440C,448C 'style':570C 'subdocu':279C,350C,356C 'summari':96C 'taken':289C 'throw':95B,206C,248C,509C 'took':386C 'tostr':165C,195C 'tri':369C 'true':83B,223C,492C,496C,504C,595C,607C 'truthi':383C 'type':449C 'typeerror':549C 'undefin':514C 'untrust':600C 'unvalid':87B 'updat':333C,337C 'use':45B,105C,208C 'user':131C,186C,226C,574C 'valu':4A,49B,56B,72B,112C,128C,145C,157C,288C,385C,545C,589C 'vector':424C 'verbatim':498C 'via':478C 'way':560C 'without':50B,233C,613C 'would':621C 'wr5m':23A 'write':397C 'x':463C,486C","nicknames":[],"mitigations":[],"who_affected":"Applications that use Mongoose discriminators and pass user-controlled documents or filters into model construction, create, bulkWrite or queries. The worst outcome, persistent query failure, needs bulkWrite with skipValidation true and untrusted input, or a document written to the collection by another writer. Applications without discriminators or without user-controlled input are unlikely to be reached.","tier":"C","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"developers","plain_action":null,"pkeys":["mongoose"],"ptokens":["mongoose"],"labels":["cwe843","discriminators","medium","mongodb","mongoose","nodejs","npm","package"],"campaign_type":null,"search_a":"'-125116':20A,28A '-2026':19A,27A '5jph':24A '8m96':25A 'bypass':11A 'cast':13A 'collect':17A 'constructor':10A 'cve':18A,26A 'discrimin':2A 'e.g':8A 'ghsa':22A 'ghsa-wr5m-5jph-8m96':21A 'inherit':6A 'key':3A 'match':5A 'mongoos':1A 'mongoose':29A,30A,31A,32A 'poison':16A 'properti':7A 'schema':12A 'valu':4A 'wr5m':23A","search_b":"'-843':34B 'attack':68B 'built':65B 'built-in':64B 'bulkwrit':79B 'check':51B 'constructor':59B 'control':70B 'cwe':33B 'discrimin':35B,43B,47B 'document':88B 'everi':91B 'fail':76B 'inherit':63B 'inserton':80B 'key':48B 'look':41B 'make':74B,90B 'model':44B 'mongodb':36B 'mongoos':37B,40B 'node.js':38B 'packag':39B 'properti':53B 'queri':92B 'request':75B 'resolv':60B 'return':93B 'skipvalid':82B 'store':85B 'throw':95B 'true':83B 'unvalid':87B 'use':45B 'valu':49B,56B,72B 'without':50B","search_c":"'affect':415C 'afterward':380C 'age':441C,487C,499C 'alreadi':358C 'applic':558C,598C 'array':275C,335C,341C,345C,579C 'arrayfilt':339C 'as-i':230C 'attack':522C 'await':481C,507C 'back':377C 'base':434C,436C,461C 'base.bulkwrite':482C 'base.discriminator':443C 'base.find':508C 'built':202C 'built-in':201C 'bulksav':307C 'bulkwrit':219C,306C,479C,593C,603C 'bypass':473C,591C 'call':178C 'caller':368C 'cannot':510C 'carri':587C 'cast':235C,319C,332C,349C,614C 'chang':409C 'check':141C 'child':444C 'choos':98C 'code':281C,295C,540C,559C 'come':129C 'concept':427C 'confus':450C 'console.log':466C 'const':429C,433C,458C 'constructor':164C,169C,211C,299C,465C,494C,506C 'control':524C 'crash':518C 'creat':343C 'databas':256C 'default':115C 'detail':257C 'direct':285C,371C 'discrimin':100C,110C,122C,161C,262C,271C,354C,526C,578C 'doc':181C,302C,459C,467C 'doc.__t':126C 'document':132C,191C,247C,251C,274C,292C,303C,308C,318C,340C,344C,452C,477C,484C,530C,554C,601C 'eachpath':516C 'effect':217C,581C 'elemmatch':329C 'embed':270C,353C,577C 'etc':314C 'events.__t':327C 'everi':241C 'exampl':124C,162C 'expect':193C 'extra':447C 'fail':537C 'fals':470C 'fell':376C 'field':618C 'filter':135C,238C,294C,321C,331C,336C,338C,532C,571C,617C 'find':312C 'findon':313C 'first':373C 'flat':323C 'function':175C,204C 'getdiscriminatorbyvalu':357C 'global':173C 'hasownproperti':167C 'hold':261C 'hydrat':309C 'impact':517C 'in-memori':410C 'includ':322C,562C 'index':283C 'inherit':150C,156C,384C,392C 'input':297C,456C,575C 'insertmani':305C 'inserton':220C,483C,604C 'instanceof':468C 'instead':158C 'iter':359C 'js':428C,471C 'key':111C,364C,527C 'limit':583C 'look':107C 'lookup':138C,372C,389C 'make':534C 'mean':547C 'memori':412C 'model':101C,298C,301C,566C 'model.create':304C,563C 'model.discriminators':125C,266C 'model.find':568C 'model.hydrate':316C 'mongoos':97C,176C,190C,430C,432C 'mongoose.document':469C 'mongoose.model':435C 'mongoose.schema':438C,446C 'name':147C,198C,439C,462C,485C 'nest':278C,348C 'new':179C,300C,437C,445C,460C,565C 'non':553C 'non-docu':552C 'normal':622C 'notinschema':491C,503C 'number':442C,490C,502C 'object':119C,174C,180C,187C,227C,265C,404C,457C,555C,610C 'object.prototype':152C,399C 'onto':576C 'pass':599C 'path':282C,296C,324C,541C 'persist':475C 'plain':118C,264C 'poison':476C 'pollut':423C 'popul':351C 'preced':387C 'proof':425C 'properti':143C,149C,393C,512C 'prototyp':422C 'push':342C,573C 'queri':134C,242C,310C,320C,330C 'raw':185C,225C,455C,609C 'read':391C,511C,515C 'regist':121C 'registri':259C,272C,361C 'remov':253C 'req.body':564C,567C 'req.query':569C 'request':417C,520C,536C,586C 'requir':431C 'resolv':153C,170C,199C 'result':311C 'return':183C,244C,556C 'safe':365C 'schema':103C,213C,234C,472C,590C,620C 'schema.discriminators':267C 'sent':543C 'serious':216C 'sever':280C 'share':403C 'similar':197C 'singl':277C,347C 'skipvalid':222C,480C,495C,594C,606C 'state':413C 'store':229C,246C,317C,355C,497C,612C 'strict':237C,616C 'string':440C,448C 'style':570C 'subdocu':279C,350C,356C 'summari':96C 'taken':289C 'throw':206C,248C,509C 'took':386C 'tostr':165C,195C 'tri':369C 'true':223C,492C,496C,504C,595C,607C 'truthi':383C 'type':449C 'typeerror':549C 'undefin':514C 'untrust':600C 'updat':333C,337C 'use':105C,208C 'user':131C,186C,226C,574C 'valu':112C,128C,145C,157C,288C,385C,545C,589C 'vector':424C 'verbatim':498C 'via':478C 'way':560C 'without':233C,613C 'would':621C 'write':397C 'x':463C,486C","cause_entry":null,"cause_path":null,"disclosures_checked_at":null,"first_public_at":"2026-11-02T19:37:22+00:00","kev":null,"cvss_vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:L","cvss_by":"GitHub","poc_count":0,"sources":[{"source":"ghsa","external_id":"GHSA-wr5m-5jph-8m96","url":"https://github.com/advisories/GHSA-wr5m-5jph-8m96","severity":"medium","published_at":"2026-11-10T20:57:18+00:00","modified_at":"2026-11-10T20:57:19+00:00","first_seen_at":"2026-11-10T21:02:22+00:00","fetched_at":"2026-11-10T21:16:10+00:00","note":null},{"source":"nvd","external_id":"CVE-2026-125116","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-125116","severity":null,"published_at":"2026-11-09T23:17:21+00:00","modified_at":"2026-11-09T23:17:21+00:00","first_seen_at":"2026-11-10T21:15:58+00:00","fetched_at":"2026-11-10T21:15:58+00:00","note":null}],"documents":[{"id":118482,"url":"https://api.github.com/repos/Automattic/mongoose/security-advisories/GHSA-wr5m-5jph-8m96","title":"Maintainer's advisory: api.github.com","kind":"vendor","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"advisory","confidence":null,"note":"Maintainer's advisory: api.github.com"},{"id":118483,"url":"https://github.com/Automattic/mongoose/security/advisories/GHSA-wr5m-5jph-8m96","title":"Maintainer's advisory: github.com","kind":"vendor","source":"ghsa","publisher":"vendor","text":null,"published_at":"2026-11-02T19:37:22+00:00","relation":"advisory","confidence":null,"note":"Maintainer's advisory: github.com"},{"id":118484,"url":"https://github.com/Automattic/mongoose/commit/0ecd8040d9b446f36535d79938c130c800f91766","title":"Patch: github.com","kind":"other","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Patch: github.com"},{"id":118485,"url":"https://github.com/Automattic/mongoose/commit/18afab75bea861f625b7c5b54635c84567ec566c","title":"Patch: github.com","kind":"other","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Patch: github.com"},{"id":118486,"url":"https://github.com/Automattic/mongoose/commit/390453c8ce1b36b6cfd1e63432a910da1f3d81d5","title":"Patch: github.com","kind":"other","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Patch: github.com"},{"id":118444,"url":"https://github.com/Automattic/mongoose/commit/8cee51beead554df1f3d05645edb439c69eb2a9b","title":"Patch: github.com","kind":"other","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Patch: github.com"},{"id":118488,"url":"https://github.com/Automattic/mongoose/commit/b26c43554233134ea978c5852babe13be858b2cf","title":"Patch: github.com","kind":"other","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Patch: github.com"},{"id":118489,"url":"https://github.com/Automattic/mongoose/commit/b68f90734b50cd22c89a669c8ca1469d7234b296","title":"Patch: github.com","kind":"other","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Patch: github.com"},{"id":117734,"url":"https://github.com/Automattic/mongoose/releases/tag/6.13.12","title":"Release: github.com","kind":"release","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Release: github.com"},{"id":117735,"url":"https://github.com/Automattic/mongoose/releases/tag/7.8.13","title":"Release: github.com","kind":"release","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Release: github.com"},{"id":117736,"url":"https://github.com/Automattic/mongoose/releases/tag/8.24.7","title":"Release: github.com","kind":"release","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Release: github.com"},{"id":117737,"url":"https://github.com/Automattic/mongoose/releases/tag/9.11.4","title":"Release: github.com","kind":"release","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Release: github.com"}],"disclosures":[],"exploit_sources":[],"notice":"Free to read, with fair use of about 60 pages a minute. For higher limits or API access: sales@infrastack.ai"}