Skip to content

Commit

Permalink
feat(query): add sanitizeProjection method to query for better docs
Browse files Browse the repository at this point in the history
  • Loading branch information
vkarpov15 committed Jul 1, 2024
1 parent eb7deb0 commit 0904a18
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 0 deletions.
53 changes: 53 additions & 0 deletions lib/query.js
Original file line number Diff line number Diff line change
Expand Up @@ -1142,6 +1142,59 @@ Query.prototype.select = function select() {
throw new TypeError('Invalid select() argument. Must be string or object.');
};

/**
* Sets this query's `sanitizeProjection` option. If set, `sanitizeProjection` does
* two things:
*
* 1. Enforces that projection values are numbers, not strings.
* 2. Prevents using `+` syntax to override properties that are deselected by default.
*
* With `sanitizeProjection()`, you can pass potentially untrusted user data to `.select()`.
*
* #### Example
*
* const userSchema = new Schema({
* name: String,
* password: { type: String, select: false }
* });
* const UserModel = mongoose.model('User', userSchema);
* const { _id } = await UserModel.create({ name: 'John', password: 'secret' })
*
* // The MongoDB server has special handling for string values that start with '$'
* // in projections, which can lead to unexpected leaking of sensitive data.
* let doc = await UserModel.findOne().select({ name: '$password' });
* doc.name; // 'secret'
* doc.password; // undefined
*
* // With `sanitizeProjection`, Mongoose forces all projection values to be numbers
* doc = await UserModel.findOne().sanitizeProjection(true).select({ name: '$password' });
* doc.name; // 'John'
* doc.password; // undefined
*
* // By default, Mongoose supports projecting in `password` using `+password`
* doc = await UserModel.findOne().select('+password');
* doc.password; // 'secret'
*
* // With `sanitizeProjection`, Mongoose prevents projecting in `password` and other
* // fields that have `select: false` in the schema.
* doc = await UserModel.findOne().sanitizeProjection(true).select('+password');
* doc.password; // undefined
*
* @method sanitizeProjection
* @memberOf Query
* @instance
* @param {Boolean} value
* @return {Query} this
* @see sanitizeProjection https://thecodebarbarian.com/whats-new-in-mongoose-5-13-sanitizeprojection.html
* @api public
*/

Query.prototype.sanitizeProjection = function sanitizeProjection(value) {
this._mongooseOptions.sanitizeProjection = value;

return this;
};

/**
* Determines the MongoDB nodes from which to read.
*
Expand Down
5 changes: 5 additions & 0 deletions types/query.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -716,6 +716,11 @@ declare module 'mongoose' {
options?: QueryOptions<DocType> | null
): QueryWithHelpers<any, DocType, THelpers, RawDocType, 'replaceOne', TInstanceMethods>;

/**
* Sets this query's `sanitizeProjection` option. With `sanitizeProjection()`, you can pass potentially untrusted user data to `.select()`.
*/
sanitizeProjection(value: boolean): this;

/** Specifies which document fields to include or exclude (also known as the query "projection") */
select<RawDocTypeOverride extends { [P in keyof RawDocType]?: any } = {}>(
arg: string | string[] | Record<string, number | boolean | string | object>
Expand Down

0 comments on commit 0904a18

Please sign in to comment.