Skip to content

Commit

Permalink
Merge pull request #13644 from Automattic/vkarpov15/gh-13609
Browse files Browse the repository at this point in the history
Correctly clean up nested subdocs modified state on save()
  • Loading branch information
vkarpov15 committed Jul 23, 2023
2 parents faf6bf7 + ec6ecb5 commit ae3894c
Show file tree
Hide file tree
Showing 6 changed files with 40 additions and 5 deletions.
2 changes: 1 addition & 1 deletion lib/document.js
Original file line number Diff line number Diff line change
Expand Up @@ -3321,8 +3321,8 @@ Document.prototype.$__reset = function reset() {
const resetArrays = new Set();
for (const subdoc of subdocs) {
const fullPathWithIndexes = subdoc.$__fullPathWithIndexes();
subdoc.$__reset();
if (this.isModified(fullPathWithIndexes) || isParentInit(fullPathWithIndexes)) {
subdoc.$__reset();
if (subdoc.$isDocumentArrayElement) {
resetArrays.add(subdoc.parentArray());
} else {
Expand Down
10 changes: 9 additions & 1 deletion lib/types/ArraySubdocument.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,15 @@ function ArraySubdocument(obj, parentArr, skipId, fields, index) {
this.$setIndex(index);
this.$__parent = this[documentArrayParent];

Subdocument.call(this, obj, fields, this[documentArrayParent], skipId, { isNew: true });
let options;
if (typeof skipId === 'object' && skipId != null) {
options = { isNew: true, ...skipId };
skipId = undefined;
} else {
options = { isNew: true };
}

Subdocument.call(this, obj, fields, this[documentArrayParent], skipId, options);
}

/*!
Expand Down
4 changes: 2 additions & 2 deletions lib/types/DocumentArray/methods/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const methods = {
* @memberOf MongooseDocumentArray
*/

_cast(value, index) {
_cast(value, index, options) {
if (this[arraySchemaSymbol] == null) {
return value;
}
Expand Down Expand Up @@ -89,7 +89,7 @@ const methods = {
if (Constructor.$isMongooseDocumentArray) {
return Constructor.cast(value, this, undefined, undefined, index);
}
const ret = new Constructor(value, this, undefined, undefined, index);
const ret = new Constructor(value, this, options, undefined, index);
ret.isNew = true;
return ret;
},
Expand Down
2 changes: 1 addition & 1 deletion lib/types/array/methods/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -595,7 +595,7 @@ const methods = {
*/

pull() {
const values = [].map.call(arguments, this._cast, this);
const values = [].map.call(arguments, (v, i) => this._cast(v, i, { defaults: false }), this);
const cur = this[arrayParentSymbol].get(this[arrayPathSymbol]);
let i = cur.length;
let mem;
Expand Down
4 changes: 4 additions & 0 deletions lib/types/subdocument.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ module.exports = Subdocument;
*/

function Subdocument(value, fields, parent, skipId, options) {
if (typeof skipId === 'object' && skipId != null && options == null) {
options = skipId;
skipId = undefined;
}
if (parent != null) {
// If setting a nested path, should copy isNew from parent re: gh-7048
const parentOptions = { isNew: parent.isNew };
Expand Down
23 changes: 23 additions & 0 deletions test/document.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12290,6 +12290,29 @@ describe('document', function() {
assert.deepStrictEqual(doc.elements[1].modifiedPaths(), []);
});

it('cleans up all nested subdocs modified state on save (gh-13609)', async function() {
const TwoElementSchema = new mongoose.Schema({
elementName: String
});

const TwoNestedSchema = new mongoose.Schema({
nestedName: String,
elements: [TwoElementSchema]
});

const TwoDocDefaultSchema = new mongoose.Schema({
docName: String,
nested: { type: TwoNestedSchema, default: {} }
});

const Test = db.model('Test', TwoDocDefaultSchema);
const doc = new Test({ docName: 'MyDocName' });
doc.nested.nestedName = 'qdwqwd';
doc.nested.elements.push({ elementName: 'ElementName1' });
await doc.save();
assert.deepStrictEqual(doc.nested.modifiedPaths(), []);
});

it('avoids prototype pollution on init', async function() {
const Example = db.model('Example', new Schema({ hello: String }));

Expand Down

0 comments on commit ae3894c

Please sign in to comment.