Skip to content

Commit d881559

Browse files
author
Ian Boros
committed
SERVER-34662 fix $changeStream parsing to check that its argument is an object
1 parent a1601dd commit d881559

File tree

4 files changed

+31
-1
lines changed

4 files changed

+31
-1
lines changed

jstests/change_streams/change_stream.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,17 @@
1616
assertDropAndRecreateCollection(db, "t1");
1717
assertDropAndRecreateCollection(db, "t2");
1818

19+
// Test that $changeStream only accepts an object as its argument.
20+
function checkArgFails(arg) {
21+
assert.commandFailedWithCode(
22+
db.runCommand({aggregate: "t1", pipeline: [{$changeStream: arg}], cursor: {}}), 50808);
23+
}
24+
25+
checkArgFails(1);
26+
checkArgFails("invalid");
27+
checkArgFails(false);
28+
checkArgFails([1, 2, "invalid", {x: 1}]);
29+
1930
// Test that a change stream cannot be opened on collections in the "admin", "config", or
2031
// "local" databases.
2132
assertInvalidChangeStreamNss("admin", "testColl");

jstests/libs/override_methods/implicit_whole_db_changestreams.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ const ChangeStreamPassthroughHelpers = {
2525
// Determine whether this command is a valid $changeStream aggregation on a single
2626
// collection or database.
2727
if (!(cmdObj && cmdObj.aggregate && Array.isArray(cmdObj.pipeline) &&
28-
cmdObj.pipeline.length > 0 && cmdObj.pipeline[0].$changeStream)) {
28+
cmdObj.pipeline.length > 0 && typeof cmdObj.pipeline[0].$changeStream == "object" &&
29+
cmdObj.pipeline[0].$changeStream.constructor === Object)) {
2930
return false;
3031
}
3132
// Single-collection and whole-db streams cannot be opened on internal databases.

src/mongo/db/pipeline/document_source_change_stream.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,10 @@ void parseResumeOptions(const intrusive_ptr<ExpressionContext>& expCtx,
462462

463463
list<intrusive_ptr<DocumentSource>> DocumentSourceChangeStream::createFromBson(
464464
BSONElement elem, const intrusive_ptr<ExpressionContext>& expCtx) {
465+
uassert(50808,
466+
"$changeStream stage expects a document as argument.",
467+
elem.type() == BSONType::Object);
468+
465469
// A change stream is a tailable + awaitData cursor.
466470
expCtx->tailableMode = TailableModeEnum::kTailableAndAwaitData;
467471

src/mongo/db/pipeline/document_source_change_stream_test.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,20 @@ class ChangeStreamStageTest : public ChangeStreamStageTestNoSetup {
268268
}
269269
};
270270

271+
TEST_F(ChangeStreamStageTest, ShouldRejectNonObjectArg) {
272+
auto expCtx = getExpCtx();
273+
274+
ASSERT_THROWS_CODE(DSChangeStream::createFromBson(
275+
BSON(DSChangeStream::kStageName << "invalid").firstElement(), expCtx),
276+
AssertionException,
277+
50808);
278+
279+
ASSERT_THROWS_CODE(DSChangeStream::createFromBson(
280+
BSON(DSChangeStream::kStageName << 12345).firstElement(), expCtx),
281+
AssertionException,
282+
50808);
283+
}
284+
271285
TEST_F(ChangeStreamStageTest, ShouldRejectUnrecognizedOption) {
272286
auto expCtx = getExpCtx();
273287

0 commit comments

Comments
 (0)