summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan Boros <ian.boros@mongodb.com>2021-02-03 12:39:38 -0500
committerIan Boros <ian.boros@mongodb.com>2021-02-03 15:01:02 -0500
commit0c7f643a2dfe4000ac9630ed5dace0cb40ec9740 (patch)
tree89deeace80646c22f0a0d9889b3337063c6e4aad
parent0a39553394d3975454e56fa1878b64c2c70cf6ef (diff)
downloadmongo-0c7f643a2dfe4000ac9630ed5dace0cb40ec9740.tar.gz
SERVER-53929 Add stricter parser checks around positional projection
-rw-r--r--jstests/core/elemMatchProjection.js5
-rw-r--r--src/mongo/db/query/projection_parser.cpp12
-rw-r--r--src/mongo/db/query/projection_test.cpp37
3 files changed, 33 insertions, 21 deletions
diff --git a/jstests/core/elemMatchProjection.js b/jstests/core/elemMatchProjection.js
index ccb8687d69f..1afd858611c 100644
--- a/jstests/core/elemMatchProjection.js
+++ b/jstests/core/elemMatchProjection.js
@@ -105,9 +105,12 @@ assert.eq(1,
.x.length,
"single object match with elemMatch");
-const err = assert.throws(() => coll.find({'group': 2, x: 1}, {'x.$': {'$slice': 1}}).toArray());
+let err = assert.throws(() => coll.find({'group': 2, x: 1}, {'x.$': {'$slice': 1}}).toArray());
assert.commandFailedWithCode(err, 31271);
+err = assert.throws(() => coll.find({}, {".$": 1}).toArray());
+assert.commandFailedWithCode(err, 5392900);
+
assert.eq(1,
coll.find({'group': 12, 'x.y.a': 1}, {'x.y.$': 1}).toArray()[0].x.y.length,
"single object match with two level dot notation");
diff --git a/src/mongo/db/query/projection_parser.cpp b/src/mongo/db/query/projection_parser.cpp
index 5d7d2517287..f9360fd4470 100644
--- a/src/mongo/db/query/projection_parser.cpp
+++ b/src/mongo/db/query/projection_parser.cpp
@@ -401,6 +401,7 @@ void parseInclusion(ParseContext* ctx,
}
} else {
verifyComputedFieldsAllowed(ctx->policies);
+ StringData elemFieldName = elem.fieldNameStringData();
uassert(31276,
"Cannot specify more than one positional projection per query.",
@@ -409,10 +410,15 @@ void parseInclusion(ParseContext* ctx,
uassert(31256, "Cannot specify positional operator and $elemMatch.", !ctx->hasElemMatch);
uassert(51050, "Projections with a positional operator require a matcher", ctx->query);
+ // Special case: ".$" is not considered a valid projection.
+ uassert(5392900,
+ str::stream() << "Projection on field " << elemFieldName << " is invalid",
+ elemFieldName != ".$");
+
// Get everything up to the first positional operator.
- // Should at least be ".$"
- StringData elemFieldName = elem.fieldNameStringData();
- invariant(elemFieldName.size() > 2);
+ uassert(5392901,
+ "Expected element field name size to be greater than 2",
+ elemFieldName.size() > 2);
StringData pathWithoutPositionalOperator =
elemFieldName.substr(0, elemFieldName.size() - 2);
diff --git a/src/mongo/db/query/projection_test.cpp b/src/mongo/db/query/projection_test.cpp
index 9050416deef..a3fae50b921 100644
--- a/src/mongo/db/query/projection_test.cpp
+++ b/src/mongo/db/query/projection_test.cpp
@@ -79,7 +79,7 @@ projection_ast::Projection createFindProjection(const char* queryStr, const char
return createProjection(query, projObj, ProjectionPolicies::findProjectionPolicies());
}
-void assertInvalidProjection(const char* queryStr, const char* projStr) {
+void assertInvalidFindProjection(const char* queryStr, const char* projStr, size_t errCode) {
BSONObj query = fromjson(queryStr);
BSONObj projObj = fromjson(projStr);
QueryTestServiceContext serviceCtx;
@@ -90,9 +90,13 @@ void assertInvalidProjection(const char* queryStr, const char* projStr) {
MatchExpressionParser::parse(query, std::move(expCtx));
ASSERT_OK(statusWithMatcher.getStatus());
std::unique_ptr<MatchExpression> queryMatchExpr = std::move(statusWithMatcher.getValue());
- ASSERT_THROWS(
- projection_ast::parse(expCtx, projObj, queryMatchExpr.get(), query, ProjectionPolicies{}),
- DBException);
+ ASSERT_THROWS_CODE(projection_ast::parse(expCtx,
+ projObj,
+ queryMatchExpr.get(),
+ query,
+ ProjectionPolicies::findProjectionPolicies()),
+ DBException,
+ errCode);
}
TEST(QueryProjectionTest, MakeEmptyProjection) {
@@ -155,32 +159,31 @@ TEST(QueryProjectionTest, MakeSingleFieldFalseIdBoolean) {
//
TEST(QueryProjectionTest, InvalidPositionalOperatorProjections) {
- assertInvalidProjection("{}", "{'a.$': 1}");
- assertInvalidProjection("{a: 1}", "{'b.$': 1}");
- assertInvalidProjection("{a: 1}", "{'a.$': 0}");
- assertInvalidProjection("{a: 1}", "{'a.$.d.$': 1}");
- assertInvalidProjection("{a: 1}", "{'a.$.$': 1}");
- assertInvalidProjection("{a: 1, b: 1, c: 1}", "{'abc.$': 1}");
- assertInvalidProjection("{$or: [{a: 1}, {$or: [{b: 1}, {c: 1}]}]}", "{'d.$': 1}");
- assertInvalidProjection("{a: [1, 2, 3]}", "{'.$': 1}");
+ assertInvalidFindProjection("{a: 1}", "{'a.$': 0}", 31395);
+ assertInvalidFindProjection("{a: 1}", "{'a.$.d.$': 1}", 31394);
+ assertInvalidFindProjection("{a: 1}", "{'a.$.$': 1}", 31394);
+ assertInvalidFindProjection("{a: [1, 2, 3]}", "{'.$': 1}", 5392900);
}
TEST(QueryProjectionTest, InvalidElemMatchTextProjection) {
- assertInvalidProjection("{}", "{a: {$elemMatch: {$text: {$search: 'str'}}}}");
+ assertInvalidFindProjection(
+ "{}", "{a: {$elemMatch: {$text: {$search: 'str'}}}}", ErrorCodes::BadValue);
}
TEST(QueryProjectionTest, InvalidElemMatchWhereProjection) {
- assertInvalidProjection("{}", "{a: {$elemMatch: {$where: 'this.a == this.b'}}}");
+ assertInvalidFindProjection(
+ "{}", "{a: {$elemMatch: {$where: 'this.a == this.b'}}}", ErrorCodes::BadValue);
}
TEST(QueryProjectionTest, InvalidElemMatchGeoNearProjection) {
- assertInvalidProjection(
+ assertInvalidFindProjection(
"{}",
- "{a: {$elemMatch: {$nearSphere: {$geometry: {type: 'Point', coordinates: [0, 0]}}}}}");
+ "{a: {$elemMatch: {$nearSphere: {$geometry: {type: 'Point', coordinates: [0, 0]}}}}}",
+ ErrorCodes::BadValue);
}
TEST(QueryProjectionTest, InvalidElemMatchExprProjection) {
- assertInvalidProjection("{}", "{a: {$elemMatch: {$expr: 5}}}");
+ assertInvalidFindProjection("{}", "{a: {$elemMatch: {$expr: 5}}}", ErrorCodes::BadValue);
}
TEST(QueryProjectionTest, ValidPositionalOperatorProjections) {