diff --git a/libs/dal/src/repositories/base-repository.ts b/libs/dal/src/repositories/base-repository.ts index faa2c532..0dd06db9 100644 --- a/libs/dal/src/repositories/base-repository.ts +++ b/libs/dal/src/repositories/base-repository.ts @@ -12,12 +12,37 @@ export class BaseRepository { this._model = MongooseModel; } + private sanitizeQuery(query: FilterQuery): FilterQuery { + if (typeof query !== 'object' || Array.isArray(query)) { + throw new Error('Invalid query format'); + } + + const sanitizedQuery: Record = {}; + for (const key of Object.keys(query)) { + const value = query[key]; + + if (typeof value === 'object' && value !== null && !Array.isArray(value)) { + const hasOperators = Object.keys(value).some((subKey) => subKey.startsWith('$')); + if (hasOperators) { + sanitizedQuery[key] = value; + continue; + } + } + + sanitizedQuery[key] = { $eq: value }; + } + + return sanitizedQuery as FilterQuery; + } + public static createObjectId() { return new Types.ObjectId().toString(); } async count(query: FilterQuery): Promise { - return await this.MongooseModel.countDocuments(query); + const sanitizedQuery = this.sanitizeQuery(query); + + return await this.MongooseModel.countDocuments(sanitizedQuery); } async aggregate(query: any[]): Promise { @@ -36,20 +61,24 @@ export class BaseRepository { } async findOne(query: FilterQuery, select?: string) { - const data = await this.MongooseModel.findOne(query, select); + const sanitizedQuery = this.sanitizeQuery(query); + const data = await this.MongooseModel.findOne(sanitizedQuery, select); if (!data) return null; return this.mapEntity(data.toObject()); } async delete(query: FilterQuery) { - const data = await this.MongooseModel.findOneAndDelete(query); + const sanitizedQuery = this.sanitizeQuery(query); + const data = await this.MongooseModel.findOneAndDelete(sanitizedQuery); //just return data return data; } async deleteMany(query: FilterQuery): Promise<{ acknowledged: boolean; deletedCount: number }> { - const data = await this.MongooseModel.deleteMany(query); + const sanitizedQuery = this.sanitizeQuery(query); + + const data = await this.MongooseModel.deleteMany(sanitizedQuery); return data; } @@ -59,7 +88,9 @@ export class BaseRepository { select = '', options: { limit?: number; sort?: any; skip?: number } = {} ): Promise { - const data = await this.MongooseModel.find(query, select, { + const sanitizedQuery = this.sanitizeQuery(query); + + const data = await this.MongooseModel.find(sanitizedQuery, select, { sort: options.sort || null, }) .skip(options.skip) @@ -78,7 +109,9 @@ export class BaseRepository { data: T[]; total: number; } | null> { - const data = await this.MongooseModel.find(query, select, { + const sanitizedQuery = this.sanitizeQuery(query); + + const data = await this.MongooseModel.find(sanitizedQuery, select, { sort: options.sort || null, }) .skip(options.skip) @@ -101,8 +134,10 @@ export class BaseRepository { options: { limit?: number; sort?: any; skip?: number } = {}, batchSize = 500 ) { + const sanitizedQuery = this.sanitizeQuery(query); + for await (const doc of this._model - .find(query, select, { + .find(sanitizedQuery, select, { sort: options.sort || null, }) .batchSize(batchSize) @@ -129,7 +164,8 @@ export class BaseRepository { matched: number; modified: number; }> { - const saved = await this.MongooseModel.updateMany(query, updateBody, { + const sanitizedQuery = this.sanitizeQuery(query); + const saved = await this.MongooseModel.updateMany(sanitizedQuery, updateBody, { multi: true, }); @@ -144,7 +180,9 @@ export class BaseRepository { updateBody: UpdateQuery, options: QueryOptions = { new: true } // By default return updated document ): Promise { - return this.MongooseModel.findOneAndUpdate(query, updateBody, options); + const sanitizedQuery = this.sanitizeQuery(query); + + return this.MongooseModel.findOneAndUpdate(sanitizedQuery, updateBody, options); } protected mapEntity(data: any): T { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dd9fee3b..c630b9cb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -791,8 +791,8 @@ importers: specifier: '>=12.0.0' version: 18.2.11(rxjs@7.8.1)(zone.js@0.14.10) '@impler/client': - specifier: ^0.27.1 - version: 0.27.1 + specifier: ^0.27.2 + version: 0.27.2 devDependencies: '@angular/compiler': specifier: ^12.0.0 @@ -858,8 +858,8 @@ importers: packages/react: dependencies: '@impler/client': - specifier: ^0.27.1 - version: 0.27.1 + specifier: ^0.27.2 + version: 0.27.2 react: specifier: '>=16.8.0' version: 18.2.0 @@ -2268,8 +2268,8 @@ packages: resolution: {integrity: sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q==} engines: {node: '>=6.9.0'} - '@impler/client@0.27.1': - resolution: {integrity: sha512-5nU1rZwgRVq1ZK75lsA7yeBLElcmiqPpQ2yOb5rkaI1VlPS3ntKPH4yGNPKU1pVNqSKE51z2sZD17T3/0C8TQg==} + '@impler/client@0.27.2': + resolution: {integrity: sha512-cxqwFJItReoClou5uYm692XRk2TZ0TIBaniH7jMoUkespFvX7AXsXZyA1ldnQfUUPA4pnaI66TvkTW8rvUTn4w==} engines: {node: '>=10'} '@isaacs/cliui@8.0.2': @@ -15280,7 +15280,7 @@ snapshots: '@hutson/parse-repository-url@3.0.2': {} - '@impler/client@0.27.1': {} + '@impler/client@0.27.2': {} '@isaacs/cliui@8.0.2': dependencies: