Skip to content

Commit

Permalink
fix: disallow using $where in match
Browse files Browse the repository at this point in the history
  • Loading branch information
vkarpov15 committed Nov 26, 2024
1 parent b34aba6 commit c9e86bf
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 5 deletions.
6 changes: 1 addition & 5 deletions lib/helpers/populate/assignVals.js
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ function numDocs(v) {

function valueFilter(val, assignmentOpts, populateOptions, allIds) {
const userSpecifiedTransform = typeof populateOptions.transform === 'function';
const transform = userSpecifiedTransform ? populateOptions.transform : noop;
const transform = userSpecifiedTransform ? populateOptions.transform : v => v;
if (Array.isArray(val)) {
// find logic
const ret = [];
Expand Down Expand Up @@ -341,7 +341,3 @@ function isPopulatedObject(obj) {
obj.$__ != null ||
leanPopulateMap.has(obj);
}

function noop(v) {
return v;
}
19 changes: 19 additions & 0 deletions lib/helpers/populate/getModelsMapForPopulate.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,15 @@ module.exports = function getModelsMapForPopulate(model, docs, options) {
if (hasMatchFunction) {
match = match.call(doc, doc);
}
if (Array.isArray(match)) {
for (const item of match) {
if (item != null && item.$where) {
throw new MongooseError('Cannot use $where filter with populate() match');
}
}
} else if (match != null && match.$where != null) {
throw new MongooseError('Cannot use $where filter with populate() match');
}
data.match = match;
data.hasMatchFunction = hasMatchFunction;
data.isRefPath = isRefPath;
Expand Down Expand Up @@ -447,6 +456,16 @@ function _virtualPopulate(model, docs, options, _virtualRes) {
data.match = match;
data.hasMatchFunction = hasMatchFunction;

if (Array.isArray(match)) {
for (const item of match) {
if (item != null && item.$where) {
throw new MongooseError('Cannot use $where filter with populate() match');
}
}
} else if (match != null && match.$where != null) {
throw new MongooseError('Cannot use $where filter with populate() match');
}

// Get local fields
const ret = _getLocalFieldValues(doc, localField, model, options, virtual);

Expand Down
46 changes: 46 additions & 0 deletions test/model.populate.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3641,6 +3641,52 @@ describe('model: populate:', function() {
assert.deepEqual(band.members.map(b => b.name).sort(), ['AA', 'AB']);
});

it('match prevents using $where', async function() {
const ParentSchema = new Schema({
name: String,
child: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Child'
},
children: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Child'
}]
});

const ChildSchema = new Schema({
name: String
});
ChildSchema.virtual('parent', {
ref: 'Parent',
localField: '_id',
foreignField: 'parent'
});

const Parent = db.model('Parent', ParentSchema);
const Child = db.model('Child', ChildSchema);

const child = await Child.create({ name: 'Luke' });
const parent = await Parent.create({ name: 'Anakin', child: child._id });

await assert.rejects(
() => Parent.findOne().populate({ path: 'child', match: { $where: 'console.log("oops!");' } }),
/Cannot use \$where filter with populate\(\) match/
);
await assert.rejects(
() => Parent.find().populate({ path: 'child', match: { $where: 'console.log("oops!");' } }),
/Cannot use \$where filter with populate\(\) match/
);
await assert.rejects(
() => parent.populate({ path: 'child', match: { $where: 'console.log("oops!");' } }),
/Cannot use \$where filter with populate\(\) match/
);
await assert.rejects(
() => Child.find().populate({ path: 'parent', match: { $where: 'console.log("oops!");' } }),
/Cannot use \$where filter with populate\(\) match/
);
});

it('multiple source docs', async function() {
const PersonSchema = new Schema({
name: String,
Expand Down

0 comments on commit c9e86bf

Please sign in to comment.