fix(adapter-commons): validate query operators nested in arrays - #3700
Merged
Conversation
validateQueryProperty skipped array values because isPlainObject
excludes arrays. That let unknown $ operators through when wrapped
in an extra array level, e.g. $or: [[{ $where: '1==1' }]]. Recurse
into arrays so the operator allow-list applies at every level.
filterQuery left $select, $or, $and, and $sort object values unvalidated, so unknown $ operators could ride through those filters. ObjectIdSchema accepted any object, which let operator documents pass querySyntax on generated MongoDB services. Validate those filter values with validateQueryProperty. The objectid keyword now accepts ObjectId instances and rejects other objects, and ObjectIdSchema uses that for its object branch.
Tightening ObjectIdSchema dropped { _id: { $exists: true } }, which
only passed because the old any-object branch treated the operator
document as an id. Add $exists as a boolean operator on queryProperty
so it is allowed on every querySyntax field, including ObjectIds.
{ userId: { $ne: null } } is the usual Mongo and SQL "field is set"
query. After tightening ObjectIdSchema it only passed when the field
type already included null. Accept null on $ne/$in/$nin in
queryProperty so ObjectId, number, and string fields all allow it.
The default syntax already covers $ne/$in/$nin null and $exists.
Document how to add $like/$regex/array operators, how to allow
{ userId: null } on ObjectId query fields, and how $meta/$slice
map to service operators rather than querySyntax.
{ userId: null } is a real IS NULL query; accept null on the
queryProperty equality branch the same way as $ne/$in/$nin.
Allow $meta/$slice/$elemMatch inside object $select and $sort
(not as field operators). Add more with projectionOperators.
@feathersjs/mongodb defaults operators to include $regex and
$options. Generated Mongo query schemas do the same on the
primary string field.
Keep the allow-list tightenings. Remove $exists, implicit null equality, default Mongo $regex, and projectionOperators so those stay app-level extensions via querySyntax or operators, not new supported syntax.
Assert $regex/$options, $like, $exists, $meta, and nullable ObjectId fields still work when the app opts in, and that unlisted operators such as $where stay rejected.
isObjectId now requires instanceof ObjectId, or _bsontype plus a
non-Object constructor and toHexString, so `{ _bsontype: 'ObjectId' }`
does not pass ObjectIdSchema. Attach $regex/$options to text in the
TypeBox Pick example.
marshallswain
commented
Aug 14, 2026
marshallswain
left a comment
Member
Author
There was a problem hiding this comment.
Suggestions addressed in b06311d: isObjectId now rejects spoofed { _bsontype: 'ObjectId' } JSON, and the TypeBox example attaches $regex/$options to text.
CI failed because people.create() returned no _id after earlier aggregation tests mutated the shared service. Insert the fixture through the collection so the test only covers validateQuery + $regex.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
validateQueryProperty()in@feathersjs/adapter-commonsdid not recurse into array values._.isObject()in@feathersjs/commonsexcludes arrays, soisPlainObject()returned false and the value was returned unvalidated.$or: [[{ $where: '1==1' }]]was accepted;$or: [{ $where: '1==1' }]was correctly rejected). This is a v5 regression: v4cleanQuerymapped over arrays.$or/$and, object$select(including{ x: { $function: … } }for MongoDB$project), and object$sortvalues.ObjectIdSchemaaccepted any object (additionalProperties: true), so generated MongoDB query schemas treated{ _id: { $where } },{ $regex },{ $ne: null }as valid ids. DefaultvalidateQuerythen skipped adapter sanitization.filterQuery()is used byAdapterBase.sanitizeQuery(), so every adapter built on it is covered.ObjectIdSchemais tightened in both@feathersjs/schemaand@feathersjs/typebox; theobjectidkeyword now accepts ObjectId instances and rejects other objects.Not dependent on other PRs.
Other Information
Reported privately by Khiêm Trần (k0m3g4) and Chien Huynh (chienhm). Affects
@feathersjs/adapter-commons5.0.0–5.0.48 (andObjectIdSchema/keywordObjectIdon generated Mongo apps). v4 is not affected by the array regression.On
@feathersjs/memorythe array wrap allowed sift$wherethrough sanitization. Please publish a patch release after merge.