diff --git a/packages/mongodb/src/adapter.ts b/packages/mongodb/src/adapter.ts index ddce4075bc..eec0ba869a 100644 --- a/packages/mongodb/src/adapter.ts +++ b/packages/mongodb/src/adapter.ts @@ -86,6 +86,9 @@ export class MongoDbAdapter< const { $select, $sort, $limit: _limit, $skip = 0, ...query } = (params.query || {}) as AdapterQuery const $limit = getLimit(_limit, options.paginate) if (id !== null) { + if (typeof id !== 'string' && typeof id !== 'number' && !(id instanceof ObjectId)) { + throw new BadRequest(`Invalid id '${JSON.stringify(id)}'`) + } query.$and = (query.$and || []).concat({ [this.id]: this.getObjectId(id) }) diff --git a/packages/mongodb/test/index.test.ts b/packages/mongodb/test/index.test.ts index 5d3d2f6fc5..a85a47dede 100644 --- a/packages/mongodb/test/index.test.ts +++ b/packages/mongodb/test/index.test.ts @@ -839,6 +839,65 @@ describe('Feathers MongoDB Service', () => { }) }) + describe('NoSQL injection via object id', () => { + let target: Person + + beforeEach(async () => { + target = await app.service('people').create({ name: 'Target' }) + }) + + afterEach(async () => { + try { + await app.service('people').remove(target._id) + } catch (e: unknown) {} + }) + + it('rejects object as id in get', async () => { + await assert.rejects( + () => app.service('people').get({ $ne: null } as any), + { + name: 'BadRequest' + } + ) + }) + + it('rejects object as id in remove', async () => { + await assert.rejects( + () => app.service('people').remove({ $ne: null } as any), + { + name: 'BadRequest' + } + ) + }) + + it('rejects object as id in update', async () => { + await assert.rejects( + () => app.service('people').update({ $ne: null } as any, { name: 'Hacked' }), + { + name: 'BadRequest' + } + ) + }) + + it('rejects object as id in patch', async () => { + await assert.rejects( + () => app.service('people').patch({ $ne: null } as any, { name: 'Hacked' }), + { + name: 'BadRequest' + } + ) + }) + + it('rejects regex operator as id', async () => { + await assert.rejects( + () => app.service('people').get({ $regex: '^' } as any), + { + name: 'BadRequest' + } + ) + }) + }) + testSuite(app, errors, 'people', '_id') testSuite(app, errors, 'people-customid', 'customid') })