From 201071b2b55765bc82c0d39ecb11ca7b580b30df Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Sun, 17 Jul 2022 16:01:54 -0400 Subject: [PATCH 01/10] fix(types): allow any value for AddFields Fix #12096 --- test/types/PipelineStage.test.ts | 8 ++++++++ types/expressions.d.ts | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/test/types/PipelineStage.test.ts b/test/types/PipelineStage.test.ts index 37968a3b36..67dc307818 100644 --- a/test/types/PipelineStage.test.ts +++ b/test/types/PipelineStage.test.ts @@ -415,3 +415,11 @@ const stages4: PipelineStage[] = [ } } ]; + +(function gh12096() { + const data: PipelineStage.AddFields = { + $addFields: { + name: { $meta: 'Bill' } + } + }; +})(); diff --git a/types/expressions.d.ts b/types/expressions.d.ts index 69b882b2eb..bce492cfe5 100644 --- a/types/expressions.d.ts +++ b/types/expressions.d.ts @@ -2449,7 +2449,7 @@ declare module 'mongoose' { FunctionExpression | ObjectIdExpression | ConditionalExpressionOperator | - Expression.Let; + any; export type ObjectIdExpression = TypeExpressionOperatorReturningObjectId; From 9524f896b8dd27aa94dbd36495bb366e01678351 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Sun, 17 Jul 2022 16:18:19 -0400 Subject: [PATCH 02/10] fix(types): make `$addToSet` fields mutable to allow programatically constructing $addToSet Fix #12091 --- test/types/queries.test.ts | 14 ++++++++++++++ types/index.d.ts | 10 +++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/test/types/queries.test.ts b/test/types/queries.test.ts index 7a881947f7..7a6550f7ab 100644 --- a/test/types/queries.test.ts +++ b/test/types/queries.test.ts @@ -325,5 +325,19 @@ function gh11964() { /* ... */ } } +} +function gh12091() { + interface IUser{ + friendsNames: string[]; + } + const userSchema = new Schema({ + friendsNames: [String] + }); + + const update: UpdateQuery = { $addToSet: { friendsNames: 'John Doe' } }; + if (!update?.$addToSet) { + return; + } + update.$addToSet.friendsNames = 'Jane Doe'; } diff --git a/types/index.d.ts b/types/index.d.ts index 8b7a63bbc4..4396c60baa 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -437,6 +437,10 @@ declare module 'mongoose' { export type SortOrder = -1 | 1 | 'asc' | 'ascending' | 'desc' | 'descending'; + type Mutable = { + -readonly [K in keyof T]: T[K]; + } + type _UpdateQuery = { /** @see https://docs.mongodb.com/manual/reference/operator/update-field/ */ $currentDate?: AnyKeys & AnyObject; @@ -450,10 +454,10 @@ declare module 'mongoose' { $unset?: AnyKeys & AnyObject; /** @see https://docs.mongodb.com/manual/reference/operator/update-array/ */ - $addToSet?: mongodb.SetFields; + $addToSet?: Mutable>; $pop?: AnyKeys & AnyObject; - $pull?: mongodb.PullOperator; - $push?: mongodb.PushOperator; + $pull?: Mutable>; + $push?: Mutable>; $pullAll?: mongodb.PullAllOperator; /** @see https://docs.mongodb.com/manual/reference/operator/update-bitwise/ */ From 0156d5edae17b959bb4718b8186708287afb9a08 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Sun, 17 Jul 2022 16:19:28 -0400 Subject: [PATCH 03/10] style: fix lint --- types/index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/index.d.ts b/types/index.d.ts index 4396c60baa..3cd944d0d8 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -439,7 +439,7 @@ declare module 'mongoose' { type Mutable = { -readonly [K in keyof T]: T[K]; - } + }; type _UpdateQuery = { /** @see https://docs.mongodb.com/manual/reference/operator/update-field/ */ From 80b7d53a1b5cb73594559a9ec171e0d672031bc9 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Sun, 17 Jul 2022 20:50:23 -0400 Subject: [PATCH 04/10] Revert "chore: upgrade mongodb driver to 4.8.0" --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 35c4100ee6..258da9ef8c 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "dependencies": { "bson": "^4.6.2", "kareem": "2.4.1", - "mongodb": "4.8.0", + "mongodb": "4.7.0", "mpath": "0.9.0", "mquery": "4.0.3", "ms": "2.1.3", From bc302f4667d62eadc8841a1946d5eefe9353f459 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Mon, 18 Jul 2022 11:59:47 -0400 Subject: [PATCH 05/10] chore: release 6.4.5 --- CHANGELOG.md | 15 +++++++++++++++ package.json | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 27d572732b..74093f5ef3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,18 @@ +6.4.5 / 2022-07-18 +================== + * fix(model+timestamps): set timestamps on subdocuments in insertMany() #12060 + * fix: correct isAtlas check #12110 [skrtheboss](https://github.com/skrtheboss) + * fix(types): fix various issues with auto typed schemas #12042 [mohammad0-0ahmad](https://github.com/mohammad0-0ahmad) + * fix(types): allow any value for AddFields #12096 + * fix(types): allow arbitrary expressions for ConcatArrays #12058 + * fix(types): make $addToSet fields mutable to allow programatically constructing $addToSet #12091 + * fix(types): add $let as a possible expression to $addFields #12087 [AbdelrahmanHafez](https://github.com/AbdelrahmanHafez) + * fix(types): fix $switch expression type #12088 [AbdelrahmanHafez](https://github.com/AbdelrahmanHafez) + * fix(types): correct options type for syncIndexes() #12101 [lpizzinidev](https://github.com/lpizzinidev) + * fix(types): avoid treating | undefined types as any in `Require_id` to better support `_id: String` with auto-typed schemas #12070 + * docs: fix up various jsdoc issues #12086 [hasezoey](https://github.com/hasezoey) + * docs: add sanitizeFilter to mongoose.set() options #12112 [pathei-kosmos](https://github.com/pathei-kosmos) + 6.4.4 / 2022-07-08 ================== * fix(types): allow using an object to configure timestamps #12061 [lantw44](https://github.com/lantw44) diff --git a/package.json b/package.json index 258da9ef8c..bfb28d8cf1 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "mongoose", "description": "Mongoose MongoDB ODM", - "version": "6.4.4", + "version": "6.4.5", "author": "Guillermo Rauch ", "keywords": [ "mongodb", From a45cfb6b0ce0067ae9794cfa80f7917e1fb3c6f8 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Mon, 18 Jul 2022 22:21:57 -0400 Subject: [PATCH 06/10] fix(schema): disallow setting __proto__ when creating schema with dotted properties Fix #12085 --- lib/schema.js | 7 +++++++ test/schema.test.js | 10 ++++++++++ 2 files changed, 17 insertions(+) diff --git a/lib/schema.js b/lib/schema.js index 781ccdbbcf..730bddeac4 100644 --- a/lib/schema.js +++ b/lib/schema.js @@ -554,6 +554,10 @@ Schema.prototype.add = function add(obj, prefix) { const keys = Object.keys(obj); const typeKey = this.options.typeKey; for (const key of keys) { + if (utils.specialProperties.has(key)) { + continue; + } + const fullPath = prefix + key; const val = obj[key]; @@ -854,6 +858,9 @@ Schema.prototype.path = function(path, obj) { let fullPath = ''; for (const sub of subpaths) { + if (utils.specialProperties.has(sub)) { + throw new Error('Cannot set special property `' + sub + '` on a schema'); + } fullPath = fullPath += (fullPath.length > 0 ? '.' : '') + sub; if (!branch[sub]) { this.nested[fullPath] = true; diff --git a/test/schema.test.js b/test/schema.test.js index 7bc6299e23..6ef548b005 100644 --- a/test/schema.test.js +++ b/test/schema.test.js @@ -2792,4 +2792,14 @@ describe('schema', function() { }); }, /Cannot use schema-level projections.*subdocument_mapping.not_selected/); }); + + it('disallows setting special properties with `add()` or constructor (gh-12085)', async function() { + const maliciousPayload = '{"__proto__.toString": "Number"}'; + + assert.throws(() => { + mongoose.Schema(JSON.parse(maliciousPayload)); + }, /__proto__/); + + assert.ok({}.toString()); + }); }); From 1344214cc152159f7788fd3b2a013590e1c0c679 Mon Sep 17 00:00:00 2001 From: Nagalokesh <20608938+LokeshKanumoori@users.noreply.github.com> Date: Tue, 19 Jul 2022 19:11:43 +0530 Subject: [PATCH 07/10] Update migrating_to_6.md correct the proof/example statement of isObjectIdOrHexString --- docs/migrating_to_6.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/migrating_to_6.md b/docs/migrating_to_6.md index 270624e336..68e4a8249e 100644 --- a/docs/migrating_to_6.md +++ b/docs/migrating_to_6.md @@ -194,8 +194,8 @@ mongoose.isValidObjectId(new User({ name: 'test' })); // true // character hex strings. mongoose.isObjectIdOrHexString(new mongoose.Types.ObjectId()); // true mongoose.isObjectIdOrHexString('62261a65d66c6be0a63c051f'); // true -mongoose.isValidObjectId('0123456789ab'); // false -mongoose.isValidObjectId(6); // false +mongoose.isObjectIdOrHexString('0123456789ab'); // false +mongoose.isObjectIdOrHexString(6); // false ```

Schema Defined Document Key Order

From 086bd9f3f9aa06396a922d9948eee0248cf4bbb8 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Tue, 19 Jul 2022 11:54:05 -0400 Subject: [PATCH 08/10] fix(query): apply lean transform option to top-level document Fix #12093 --- lib/query.js | 10 ++++++++-- test/query.test.js | 38 ++++++++++++++++++++++---------------- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/lib/query.js b/lib/query.js index 1bfed85a27..634616fe79 100644 --- a/lib/query.js +++ b/lib/query.js @@ -4019,7 +4019,9 @@ Query.prototype._findAndModify = function(type, callback) { */ function _completeOneLean(schema, doc, path, res, opts, callback) { - if (opts.lean && opts.lean.transform) { + if (opts.lean && typeof opts.lean.transform === 'function') { + opts.lean.transform(doc); + for (let i = 0; i < schema.childSchemas.length; i++) { const childPath = path ? path + '.' + schema.childSchemas[i].model.path : schema.childSchemas[i].model.path; const _schema = schema.childSchemas[i].schema; @@ -4053,7 +4055,11 @@ function _completeOneLean(schema, doc, path, res, opts, callback) { */ function _completeManyLean(schema, docs, path, opts, callback) { - if (opts.lean && opts.lean.transform) { + if (opts.lean && typeof opts.lean.transform === 'function') { + for (const doc of docs) { + opts.lean.transform(doc); + } + for (let i = 0; i < schema.childSchemas.length; i++) { const childPath = path ? path + '.' + schema.childSchemas[i].model.path : schema.childSchemas[i].model.path; const _schema = schema.childSchemas[i].schema; diff --git a/test/query.test.js b/test/query.test.js index 746774c5c4..1ebc5320aa 100644 --- a/test/query.test.js +++ b/test/query.test.js @@ -4006,22 +4006,28 @@ describe('Query', function() { }); const Test = db.model('gh10423', testSchema); await Test.create({ name: 'foo', foo: [{ sub: 'Test' }, { sub: 'Testerson' }], otherName: { nickName: 'Bar' } }); - const result = await Test.find().lean({ transform: (doc) => { - delete doc._id; - return doc; - } }); - assert(result[0]._id); - assert.equal(result[0].otherName._id, undefined); - assert.equal(result[0].foo[0]._id, undefined); - assert.equal(result[0].foo[1]._id, undefined); - const single = await Test.findOne().lean({ transform: (doc) => { - delete doc._id; - return doc; - } }); - assert(single._id); - assert.equal(single.otherName._id, undefined); - assert.equal(single.foo[0]._id, undefined); - assert.equal(single.foo[0]._id, undefined); + + const result = await Test.find().lean({ + transform: (doc) => { + delete doc._id; + return doc; + } + }); + assert.strictEqual(result[0]._id, undefined); + assert.strictEqual(result[0].otherName._id, undefined); + assert.strictEqual(result[0].foo[0]._id, undefined); + assert.strictEqual(result[0].foo[1]._id, undefined); + + const single = await Test.findOne().lean({ + transform: (doc) => { + delete doc._id; + return doc; + } + }); + assert.strictEqual(single._id, undefined); + assert.strictEqual(single.otherName._id, undefined); + assert.strictEqual(single.foo[0]._id, undefined); + assert.strictEqual(single.foo[0]._id, undefined); }); it('skips applying default projections over slice projections (gh-11940)', async function() { From 2262a7709fe72ea255d23e1aea4ac84e9627e523 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Tue, 19 Jul 2022 20:16:53 -0400 Subject: [PATCH 09/10] fix(document): avoid mutating original object passed to $set() when applying defaults to nested properties Fix #12102 --- lib/document.js | 2 +- test/document.test.js | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/document.js b/lib/document.js index 72c12c7544..e6f73aeb77 100644 --- a/lib/document.js +++ b/lib/document.js @@ -1149,8 +1149,8 @@ Document.prototype.$set = function $set(path, val, type, options) { } if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { - $applyDefaultsToNested(path[key], prefix + key, this); this.$set(prefix + key, path[key], constructing, Object.assign({}, options, { _skipMarkModified: true })); + $applyDefaultsToNested(this.$get(prefix + key), prefix + key, this); continue; } else if (strict) { // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) diff --git a/test/document.test.js b/test/document.test.js index 160b14fbe3..359a0ee050 100644 --- a/test/document.test.js +++ b/test/document.test.js @@ -8831,7 +8831,7 @@ describe('document', function() { assert.ok(!user.updatedAt); }); - it('Sets default when passing undefined as value for a key in a nested subdoc (gh-9039)', async function() { + it('Sets default when passing undefined as value for a key in a nested subdoc (gh-12102) (gh-9039)', async function() { const Test = db.model('Test', { nested: { prop: { @@ -8841,9 +8841,11 @@ describe('document', function() { } }); - - const doc = await Test.create({ nested: { prop: undefined } }); + const obj = { nested: { prop: undefined } }; + const doc = await Test.create(obj); assert.equal(doc.nested.prop, 'some default value'); + + assert.deepStrictEqual(obj, { nested: { prop: undefined } }); }); it('allows accessing $locals when initializing (gh-9098)', function() { From 422f9da02d2e8c0c227887265bce25cecaecf403 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Tue, 19 Jul 2022 20:23:54 -0400 Subject: [PATCH 10/10] test(schema): add coverage for calling `plugin()` with options Fix #12077 --- test/schema.test.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/schema.test.js b/test/schema.test.js index 7bc6299e23..8cfac9618a 100644 --- a/test/schema.test.js +++ b/test/schema.test.js @@ -924,6 +924,19 @@ describe('schema', function() { assert.equal(called, true); }); + + it('options param (gh-12077)', function() { + const Tobi = new Schema(); + let called = false; + + Tobi.plugin(function(schema, opts) { + assert.equal(schema, Tobi); + assert.deepStrictEqual(opts, { answer: 42 }); + called = true; + }, { answer: 42 }); + + assert.equal(called, true); + }); }); describe('options', function() {