How to avoid accidentally returning an arbitrary document when using findOne with a non-existing field in Mongoose?

When you use findOne to return a single document, but the specified field in the filter does not exist (for example because of a typo), MongoDB returns an arbitrary document. This can cause some really bad bugs and security issues.

How do I mitigate this?

it returns null if there is no field or value in the collection,

db.collection('collection').findOne({'abcd':'abcd'})

what is your query syntax?

1 Like

I use Mongoose’s findOne. I thought it behaves the same as the native one, is that not the case?

const googleSignupToken = await GoogleSignupToken.findOne({ token: token }).exec();

When I accidentally wrote { tokenId: token } as the filter (tokenId doesn’t exist as a key), it returned the first document in the collection. Which in this case is a devastating bug. Does the native findOne behave differently?

It seems like yes, it return null for findOne() and empty array with find().toArray()

Thank you. Then I have to find out why Mongoose behaves this way!

1 Like

I found the answer on Stackoverflow. To disable this default behavior, we have to disable an option called strictQuery:

2 Likes

Hi @Florian_Walther,

Thank you for finding and sharing the solution for this default Mongoose behaviour, which is definitely a very unexpected deviation from the normal MongoDB driver behaviour.

It looks like the strictQuery behaviour changed in Mongoose 6 and this has caused some confusion.

The Mongoose maintainer has created an issue to have strictQuery off by default in Mongoose 7 (as it was prior to Mongoose 6), so you may want to watch & upvote https://github.com/Automattic/mongoose/issues/11861.

Regards,
Stennie

1 Like

Thank you. I upvoted it :+1:
Yea I noticed that many people were complaining about this change and even the maintainer admitted that it was a mistake. I find it quite dangerous but I’m happy that there is a way to disable it.

1 Like

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.