summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Storch <david.storch@10gen.com>2016-12-05 11:19:48 -0500
committerDavid Storch <david.storch@10gen.com>2016-12-09 17:59:52 -0500
commit7cf929f25638e4ad9525775c8ea0e18f3c86faf5 (patch)
tree42328072009a72ce73f1c5bffe60f0c36ee0d492
parent586ac20773ff7dc18cabf329c238bf261e00387d (diff)
downloadmongo-7cf929f25638e4ad9525775c8ea0e18f3c86faf5.tar.gz
SERVER-24128 reject embedded null bytes in namespace string parsing
-rw-r--r--buildscripts/resmokeconfig/suites/sharding_last_stable_mongos_and_mixed_shards.yml2
-rw-r--r--jstests/core/commands_namespace_parsing.js388
-rw-r--r--jstests/core/views/views_creation.js6
-rw-r--r--jstests/noPassthrough/copydb_illegal_collections.js3
-rw-r--r--jstests/sharding/explain_cmd_invalid_namespace.js185
-rw-r--r--src/mongo/client/fetcher.cpp2
-rw-r--r--src/mongo/db/catalog/create_collection.cpp9
-rw-r--r--src/mongo/db/cloner.cpp2
-rw-r--r--src/mongo/db/commands.cpp6
-rw-r--r--src/mongo/db/commands/collection_to_capped.cpp49
-rw-r--r--src/mongo/db/commands/copydb.cpp28
-rw-r--r--src/mongo/db/commands/copydb_common.cpp13
-rw-r--r--src/mongo/db/commands/copydb_start_commands.cpp10
-rw-r--r--src/mongo/db/commands/create_indexes.cpp2
-rw-r--r--src/mongo/db/commands/dbcommands.cpp31
-rw-r--r--src/mongo/db/commands/dbhash.cpp5
-rw-r--r--src/mongo/db/commands/distinct.cpp14
-rw-r--r--src/mongo/db/commands/find_cmd.cpp2
-rw-r--r--src/mongo/db/commands/geo_near_cmd.cpp2
-rw-r--r--src/mongo/db/commands/getmore_cmd.cpp2
-rw-r--r--src/mongo/db/commands/group_cmd.cpp19
-rw-r--r--src/mongo/db/commands/index_filter_commands.cpp4
-rw-r--r--src/mongo/db/commands/list_indexes.cpp4
-rw-r--r--src/mongo/db/commands/mr.cpp32
-rw-r--r--src/mongo/db/commands/mr_common.cpp13
-rw-r--r--src/mongo/db/commands/parallel_collection_scan.cpp2
-rw-r--r--src/mongo/db/commands/pipeline_command.cpp9
-rw-r--r--src/mongo/db/commands/plan_cache_commands.cpp4
-rw-r--r--src/mongo/db/commands/rename_collection_cmd.cpp43
-rw-r--r--src/mongo/db/commands/rename_collection_common.cpp14
-rw-r--r--src/mongo/db/commands/test_commands.cpp12
-rw-r--r--src/mongo/db/commands/validate.cpp17
-rw-r--r--src/mongo/db/exec/group.cpp3
-rw-r--r--src/mongo/db/exec/group.h3
-rw-r--r--src/mongo/db/namespace_string-inl.h9
-rw-r--r--src/mongo/db/ops/insert.cpp16
-rw-r--r--src/mongo/db/ops/write_ops_parsers.cpp3
-rw-r--r--src/mongo/db/query/get_executor.cpp3
-rw-r--r--src/mongo/db/query/getmore_request.cpp20
-rw-r--r--src/mongo/db/query/getmore_request.h2
-rw-r--r--src/mongo/db/repl/oplog.cpp20
-rw-r--r--src/mongo/db/s/cleanup_orphaned_cmd.cpp19
-rw-r--r--src/mongo/s/commands/cluster_enable_sharding_cmd.cpp8
-rw-r--r--src/mongo/s/commands/cluster_find_cmd.cpp17
-rw-r--r--src/mongo/s/commands/cluster_move_chunk_cmd.cpp6
-rw-r--r--src/mongo/s/commands/cluster_move_primary_cmd.cpp20
-rw-r--r--src/mongo/s/commands/cluster_pipeline_cmd.cpp5
-rw-r--r--src/mongo/s/commands/cluster_remove_shard_cmd.cpp6
-rw-r--r--src/mongo/s/commands/cluster_write_cmd.cpp1
-rw-r--r--src/mongo/s/commands/commands_public.cpp144
-rw-r--r--src/mongo/s/write_ops/batched_delete_request.cpp3
-rw-r--r--src/mongo/s/write_ops/batched_insert_request.cpp3
-rw-r--r--src/mongo/s/write_ops/batched_update_request.cpp3
53 files changed, 809 insertions, 439 deletions
diff --git a/buildscripts/resmokeconfig/suites/sharding_last_stable_mongos_and_mixed_shards.yml b/buildscripts/resmokeconfig/suites/sharding_last_stable_mongos_and_mixed_shards.yml
index 031b3336ee3..2fa9b0857d6 100644
--- a/buildscripts/resmokeconfig/suites/sharding_last_stable_mongos_and_mixed_shards.yml
+++ b/buildscripts/resmokeconfig/suites/sharding_last_stable_mongos_and_mixed_shards.yml
@@ -82,8 +82,6 @@ selector:
- jstests/sharding/sharded_limit_batchsize.js
# Enable when 3.2.11 becomes last-stable
- jstests/sharding/merge_chunks_test.js
- # TODO Change in error reporting, enable when 3.4 becomes last-stable. See SERVER-26440.
- - jstests/sharding/explain_cmd_invalid_namespace.js
# $graphLookup is not supported in versions <= 3.2
- jstests/sharding/read_committed_lookup.js
- jstests/sharding/balancer_shell_commands.js
diff --git a/jstests/core/commands_namespace_parsing.js b/jstests/core/commands_namespace_parsing.js
new file mode 100644
index 00000000000..c5e6cd3a285
--- /dev/null
+++ b/jstests/core/commands_namespace_parsing.js
@@ -0,0 +1,388 @@
+// This file tests that commands namespace parsing rejects embedded null bytes.
+// Note that for each command, a properly formatted command object must be passed to the helper
+// function, regardless of the namespace used in the command object.
+(function() {
+ "use strict";
+
+ const isFullyQualified = true;
+ const isNotFullyQualified = false;
+ const isAdminCommand = true;
+ const isNotAdminCommand = false;
+
+ // If the command expects the namespace to be fully qualified, set `isFullyQualified` to true.
+ // If the command must be run against the admin database, set `isAdminCommand` to true.
+ function assertFailsWithInvalidNamespacesForField(
+ field, command, isFullyQualified, isAdminCommand) {
+ const invalidNamespaces = [];
+ invalidNamespaces.push(isFullyQualified ? "mydb." : "");
+ invalidNamespaces.push(isFullyQualified ? "mydb.\0" : "\0");
+ invalidNamespaces.push(isFullyQualified ? "mydb.a\0b" : "a\0b");
+
+ const cmds = [];
+ for (let ns of invalidNamespaces) {
+ const cmd = Object.extend({}, command, /* deep copy */ true);
+
+ const fieldNames = field.split(".");
+ const lastFieldNameIndex = fieldNames.length - 1;
+ let objToUpdate = cmd;
+ for (let i = 0; i < lastFieldNameIndex; i++) {
+ objToUpdate = objToUpdate[fieldNames[i]];
+ }
+ objToUpdate[fieldNames[lastFieldNameIndex]] = ns;
+
+ cmds.push(cmd);
+ }
+
+ const dbCmd = isAdminCommand ? db.adminCommand : db.runCommand;
+ for (let cmd of cmds) {
+ assert.commandFailedWithCode(dbCmd.apply(db, [cmd]), ErrorCodes.InvalidNamespace);
+ }
+ }
+
+ const isMaster = db.runCommand("ismaster");
+ assert.commandWorked(isMaster);
+ const isMongos = (isMaster.msg === "isdbgrid");
+
+ const isMMAPv1 = (jsTest.options().storageEngine === "mmapv1");
+
+ db.commands_namespace_parsing.drop();
+ assert.writeOK(db.commands_namespace_parsing.insert({a: 1}));
+
+ // Test aggregate fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField(
+ "aggregate", {aggregate: "", pipeline: []}, isNotFullyQualified, isNotAdminCommand);
+
+ // Test count fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField(
+ "count", {count: ""}, isNotFullyQualified, isNotAdminCommand);
+
+ // Test distinct fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField(
+ "distinct", {distinct: "", key: "a"}, isNotFullyQualified, isNotAdminCommand);
+
+ // Test group fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField("group.ns",
+ {group: {ns: "", $reduce: () => {}, initial: {}}},
+ isNotFullyQualified,
+ isNotAdminCommand);
+
+ // Test mapReduce fails with an invalid input collection name.
+ assertFailsWithInvalidNamespacesForField("mapreduce",
+ {
+ mapreduce: "",
+ map: function() {
+ emit(this.a, 1);
+ },
+ reduce: function(key, values) {
+ return Array.sum(values);
+ },
+ out: "commands_namespace_parsing_out"
+ },
+ isNotFullyQualified,
+ isNotAdminCommand);
+ // Test mapReduce fails with an invalid output collection name.
+ assertFailsWithInvalidNamespacesForField("out",
+ {
+ mapreduce: "commands_namespace_parsing",
+ map: function() {
+ emit(this.a, 1);
+ },
+ reduce: function(key, values) {
+ return Array.sum(values);
+ },
+ out: ""
+ },
+ isNotFullyQualified,
+ isNotAdminCommand);
+
+ // Test geoNear fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField(
+ "geoNear", {geoNear: "", near: [0.0, 0.0]}, isNotFullyQualified, isNotAdminCommand);
+
+ if (!isMongos) {
+ // Test geoSearch fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField(
+ "geoSearch",
+ {geoSearch: "", search: {}, near: [0.0, 0.0], maxDistance: 10},
+ isNotFullyQualified,
+ isNotAdminCommand);
+ }
+
+ // Test find fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField(
+ "find", {find: ""}, isNotFullyQualified, isNotAdminCommand);
+
+ // Test insert fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField("insert",
+ {insert: "", documents: [{q: {a: 1}, u: {a: 2}}]},
+ isNotFullyQualified,
+ isNotAdminCommand);
+
+ // Test update fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField("update",
+ {update: "", updates: [{q: {a: 1}, u: {a: 2}}]},
+ isNotFullyQualified,
+ isNotAdminCommand);
+
+ // Test delete fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField("delete",
+ {delete: "", deletes: [{q: {a: 1}, limit: 1}]},
+ isNotFullyQualified,
+ isNotAdminCommand);
+
+ // Test findAndModify fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField("findAndModify",
+ {findAndModify: "", update: {a: 2}},
+ isNotFullyQualified,
+ isNotAdminCommand);
+
+ // Test getMore fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField("collection",
+ {getMore: NumberLong("123456"), collection: ""},
+ isNotFullyQualified,
+ isNotAdminCommand);
+
+ if (!isMongos) {
+ // Test parallelCollectionScan fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField("parallelCollectionScan",
+ {parallelCollectionScan: "", numCursors: 10},
+ isNotFullyQualified,
+ isNotAdminCommand);
+
+ // Test godinsert fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField(
+ "godinsert", {godinsert: "", obj: {_id: 1}}, isNotFullyQualified, isNotAdminCommand);
+ }
+
+ // Test planCacheListFilters fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField(
+ "planCacheListFilters", {planCacheListFilters: ""}, isNotFullyQualified, isNotAdminCommand);
+
+ // Test planCacheSetFilter fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField("planCacheSetFilter",
+ {planCacheSetFilter: "", query: {}, indexes: [{a: 1}]},
+ isNotFullyQualified,
+ isNotAdminCommand);
+
+ // Test planCacheClearFilters fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField("planCacheClearFilters",
+ {planCacheClearFilters: ""},
+ isNotFullyQualified,
+ isNotAdminCommand);
+
+ // Test planCacheListQueryShapes fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField("planCacheListQueryShapes",
+ {planCacheListQueryShapes: ""},
+ isNotFullyQualified,
+ isNotAdminCommand);
+
+ // Test planCacheListPlans fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField("planCacheListPlans",
+ {planCacheListPlans: "", query: {}},
+ isNotFullyQualified,
+ isNotAdminCommand);
+
+ // Test planCacheClear fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField(
+ "planCacheClear", {planCacheClear: ""}, isNotFullyQualified, isNotAdminCommand);
+
+ if (!isMongos) {
+ // Test cleanupOrphaned fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField(
+ "cleanupOrphaned", {cleanupOrphaned: ""}, isFullyQualified, isAdminCommand);
+ }
+
+ if (isMongos) {
+ // Test enableSharding fails with an invalid database name.
+ assertFailsWithInvalidNamespacesForField(
+ "enableSharding", {enableSharding: ""}, isNotFullyQualified, isAdminCommand);
+
+ // Test mergeChunks fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField(
+ "mergeChunks",
+ {mergeChunks: "", bounds: [{_id: MinKey()}, {_id: MaxKey()}]},
+ isFullyQualified,
+ isAdminCommand);
+
+ // Test shardCollection fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField("shardCollection",
+ {shardCollection: "", key: {_id: 1}},
+ isFullyQualified,
+ isAdminCommand);
+
+ // Test split fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField(
+ "split", {split: "", find: {}}, isFullyQualified, isAdminCommand);
+
+ // Test moveChunk fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField(
+ "moveChunk",
+ {moveChunk: "", find: {}, to: "commands_namespace_parsing_out"},
+ isNotFullyQualified,
+ isAdminCommand);
+
+ // Test movePrimary fails with an invalid database name.
+ assertFailsWithInvalidNamespacesForField(
+ "movePrimary", {movePrimary: ""}, isNotFullyQualified, isAdminCommand);
+
+ // Test updateZoneKeyRange fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField(
+ "updateZoneKeyRange",
+ {updateZoneKeyRange: "", min: {_id: MinKey()}, max: {_id: MaxKey()}, zone: "3"},
+ isNotFullyQualified,
+ isAdminCommand);
+ }
+
+ // Test renameCollection fails with an invalid source collection name.
+ assertFailsWithInvalidNamespacesForField(
+ "renameCollection", {renameCollection: "", to: "test.b"}, isFullyQualified, isAdminCommand);
+ // Test renameCollection fails with an invalid target collection name.
+ assertFailsWithInvalidNamespacesForField(
+ "to", {renameCollection: "test.b", to: ""}, isFullyQualified, isAdminCommand);
+
+ // Test copydb fails with an invalid fromdb name.
+ assertFailsWithInvalidNamespacesForField(
+ "fromdb", {copydb: 1, fromdb: "", todb: "b"}, isNotFullyQualified, isAdminCommand);
+ // Test copydb fails with an invalid todb name.
+ assertFailsWithInvalidNamespacesForField(
+ "todb", {copydb: 1, fromdb: "a", todb: ""}, isNotFullyQualified, isAdminCommand);
+
+ // Test drop fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField(
+ "drop", {drop: ""}, isNotFullyQualified, isNotAdminCommand);
+
+ // Test create fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField(
+ "create", {create: ""}, isNotFullyQualified, isNotAdminCommand);
+
+ if (!isMongos) {
+ // Test cloneCollection fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField("cloneCollection",
+ {cloneCollection: "", from: "fakehost"},
+ isNotFullyQualified,
+ isNotAdminCommand);
+
+ // Test cloneCollectionAsCapped fails with an invalid source collection name.
+ assertFailsWithInvalidNamespacesForField(
+ "cloneCollectionAsCapped",
+ {cloneCollectionAsCapped: "", toCollection: "b", size: 1024},
+ isNotFullyQualified,
+ isNotAdminCommand);
+ // Test cloneCollectionAsCapped fails with an invalid target collection name.
+ assertFailsWithInvalidNamespacesForField(
+ "toCollection",
+ {cloneCollectionAsCapped: "commands_namespace_parsing", toCollection: "", size: 1024},
+ isNotFullyQualified,
+ isNotAdminCommand);
+
+ // Test convertToCapped fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField("convertToCapped",
+ {convertToCapped: "", size: 1024},
+ isNotFullyQualified,
+ isNotAdminCommand);
+ }
+
+ // Test filemd5 fails with an invalid collection name.
+ // Note: for this command, it is OK to pass 'root: ""', so do not use the helper function.
+ assert.commandFailedWithCode(db.runCommand({filemd5: ObjectId(), root: "\0"}),
+ ErrorCodes.InvalidNamespace);
+ assert.commandFailedWithCode(db.runCommand({filemd5: ObjectId(), root: "a\0b"}),
+ ErrorCodes.InvalidNamespace);
+
+ // Test createIndexes fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField(
+ "createIndexes",
+ {createIndexes: "", indexes: [{key: {a: 1}, name: "a1"}]},
+ isNotFullyQualified,
+ isNotAdminCommand);
+
+ // Test listIndexes fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField(
+ "listIndexes", {listIndexes: ""}, isNotFullyQualified, isNotAdminCommand);
+
+ // Test dropIndexes fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField(
+ "dropIndexes", {dropIndexes: "", index: "*"}, isNotFullyQualified, isNotAdminCommand);
+
+ if (!isMongos) {
+ // Test compact fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField(
+ "compact", {compact: ""}, isNotFullyQualified, isNotAdminCommand);
+ }
+
+ // Test collMod fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField(
+ "collMod",
+ {collMod: "", index: {keyPattern: {a: 1}, expireAfterSeconds: 60}},
+ isNotFullyQualified,
+ isNotAdminCommand);
+
+ // Test reIndex fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField(
+ "reIndex", {reIndex: ""}, isNotFullyQualified, isNotAdminCommand);
+
+ if (isMMAPv1 && !isMongos) {
+ // Test touch fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField(
+ "touch", {touch: "", data: true, index: true}, isNotFullyQualified, isNotAdminCommand);
+ }
+
+ // Test collStats fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField(
+ "collStats", {collStats: ""}, isNotFullyQualified, isNotAdminCommand);
+
+ // Test dataSize fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField(
+ "dataSize", {dataSize: ""}, isFullyQualified, isNotAdminCommand);
+
+ // Test explain of aggregate fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField("aggregate",
+ {aggregate: "", pipeline: [], explain: true},
+ isNotFullyQualified,
+ isNotAdminCommand);
+
+ // Test explain of count fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField(
+ "explain.count", {explain: {count: ""}}, isNotFullyQualified, isNotAdminCommand);
+
+ // Test explain of distinct fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField("explain.distinct",
+ {explain: {distinct: "", key: "a"}},
+ isNotFullyQualified,
+ isNotAdminCommand);
+
+ // Test explain of group fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField(
+ "explain.group.ns",
+ {explain: {group: {ns: "", $reduce: () => {}, initial: {}}}},
+ isNotFullyQualified,
+ isNotAdminCommand);
+
+ // Test explain of find fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField(
+ "explain.find", {explain: {find: ""}}, isNotFullyQualified, isNotAdminCommand);
+
+ // Test explain of findAndModify fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField("explain.findAndModify",
+ {explain: {findAndModify: "", update: {a: 2}}},
+ isNotFullyQualified,
+ isNotAdminCommand);
+
+ // Test explain of delete fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField(
+ "explain.delete",
+ {explain: {delete: "", deletes: [{q: {a: 1}, limit: 1}]}},
+ isNotFullyQualified,
+ isNotAdminCommand);
+
+ // Test explain of update fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField(
+ "explain.update",
+ {explain: {update: "", updates: [{q: {a: 1}, u: {a: 2}}]}},
+ isNotFullyQualified,
+ isNotAdminCommand);
+
+ // Test validate fails with an invalid collection name.
+ assertFailsWithInvalidNamespacesForField(
+ "validate", {validate: ""}, isNotFullyQualified, isNotAdminCommand);
+})();
diff --git a/jstests/core/views/views_creation.js b/jstests/core/views/views_creation.js
index 2f8bcf52142..c4ade5e728e 100644
--- a/jstests/core/views/views_creation.js
+++ b/jstests/core/views/views_creation.js
@@ -29,7 +29,7 @@
// Collections that start with 'system.' that are not special to MongoDB fail with a different
// error code.
assert.commandFailedWithCode(viewsDB.runCommand({create: "system.foo", viewOn: "collection"}),
- ErrorCodes.BadValue,
+ ErrorCodes.InvalidNamespace,
"Created an illegal view named 'system.foo'");
// Create a collection for test purposes.
@@ -79,8 +79,8 @@
assert.commandFailed(viewsDB.runCommand({create: "", viewOn: "collection", pipeline: pipe}));
assert.commandFailedWithCode(
viewsDB.runCommand({create: "system.local.new", viewOn: "collection", pipeline: pipe}),
- ErrorCodes.BadValue);
+ ErrorCodes.InvalidNamespace);
assert.commandFailedWithCode(
viewsDB.runCommand({create: "dollar$", viewOn: "collection", pipeline: pipe}),
- ErrorCodes.BadValue);
+ ErrorCodes.InvalidNamespace);
}());
diff --git a/jstests/noPassthrough/copydb_illegal_collections.js b/jstests/noPassthrough/copydb_illegal_collections.js
index 61f396631d3..cbce5abecaf 100644
--- a/jstests/noPassthrough/copydb_illegal_collections.js
+++ b/jstests/noPassthrough/copydb_illegal_collections.js
@@ -15,8 +15,7 @@
var db2 = rst.getPrimary().getDB("db2");
var res = db1.adminCommand({copydb: 1, fromdb: db1._name, todb: db2._name});
- var badValueCode = 2;
- assert.commandFailedWithCode(res, badValueCode);
+ assert.commandFailedWithCode(res, ErrorCodes.InvalidNamespace);
assert.gt(res["errmsg"].indexOf("cannot write to 'db2.system.replset'"), -1);
rst.awaitReplication();
})();
diff --git a/jstests/sharding/explain_cmd_invalid_namespace.js b/jstests/sharding/explain_cmd_invalid_namespace.js
deleted file mode 100644
index 989edec7660..00000000000
--- a/jstests/sharding/explain_cmd_invalid_namespace.js
+++ /dev/null
@@ -1,185 +0,0 @@
-// This test addresses the invalid namespace issue encountered in SERVER-26440, and checks the other
-// explainable commands to ensure none are affected by the same bug.
-
-(function() {
- "use strict";
-
- // Start the sharding test.
- let st = new ShardingTest({mongos: 1, shards: 1});
-
- // Enable sharding on the "test" database.
- assert.commandWorked(st.s.adminCommand({enableSharding: "test"}));
-
- // Connect to the "test" database.
- let testDB = st.s.getDB("test");
-
- // Create a non-empty test collection.
- assert.writeOK(testDB.server26440.insert({a: 1}));
-
- // Test explain of aggregate fails with an invalid collection name.
- // TODO SERVER-24128: The following two commands fail as defined in non-auth configurations,
- // but fail differently in auth configurations. We should fail identically in both
- // configurations.
- // assert.commandFailedWithCode(testDB.runCommand({aggregate: "", pipeline:[], explain:true}),
- // ErrorCodes.UnknownError);
- // assert.commandFailedWithCode(testDB.runCommand({aggregate: "\0", pipeline:[], explain:true}),
- // ErrorCodes.UnknownError);
- // TODO SERVER-24128: Make namespace parsing for group reject names with embedded null bytes.
- // TODO SERVER-24128: The following command runs successfully in non-auth configurations, but
- // fails in auth configurations. We should fail in both configurations.
- // assert.commandFailedWithCode(testDB.runCommand({aggregate: "a\0b", pipeline:[],
- // explain:true}), ErrorCodes.InvalidNamespace);
-
- // Test explain of count fails with an invalid collection name.
- assert.commandFailedWithCode(testDB.runCommand({explain: {count: ""}}),
- ErrorCodes.InvalidNamespace);
- assert.commandFailedWithCode(testDB.runCommand({explain: {count: "\0"}}),
- ErrorCodes.InvalidNamespace);
- assert.commandFailedWithCode(testDB.runCommand({explain: {count: "a\0b"}}), 17295);
-
- // Test explain of distinct fails with an invalid collection name.
- assert.commandFailedWithCode(testDB.runCommand({explain: {distinct: "", key: "a"}}),
- ErrorCodes.InvalidNamespace);
- assert.commandFailedWithCode(testDB.runCommand({explain: {distinct: "\0", key: "a"}}), 17295);
- assert.commandFailedWithCode(testDB.runCommand({explain: {distinct: "a\0b", key: "a"}}), 17295);
-
- // Test explain of group fails with an invalid collection name.
- // TODO SERVER-24128: Currently, we massert() and print a stack trace. Instead, we should fail
- // gracefully with a user assertion.
- // TODO SERVER-24128: The following command fails in non-auth configurations, but fails
- // differently in auth configurations. We should fail identically in both configurations.
- // assert.commandFailedWithCode(
- // testDB.runCommand({explain: {group: {ns: "", $reduce: () => {}, initial: {}}}}),
- // ErrorCodes.InvalidNamespace);
- // TODO SERVER-24128: Make namespace parsing for explain of group reject names with embedded
- // null bytes.
- // TODO SERVER-24128: The following two commands run successfully in non-auth configurations,
- // but fail in auth configurations. We should fail in both configurations.
- // assert.commandFailedWithCode(
- // testDB.runCommand({explain: {group: {ns: "\0", $reduce: () => {}, initial: {}}}}),
- // ErrorCodes.InvalidNamespace);
- // TODO SERVER-24128: Make namespace parsing for explain of group reject names with embedded
- // null bytes.
- // assert.commandFailedWithCode(
- // testDB.runCommand({explain: {group: {ns: "a\0b", $reduce: () => {}, initial: {}}}}),
- // ErrorCodes.InvalidNamespace);
-
- // Test explain of find fails with an invalid collection name.
- assert.commandFailedWithCode(testDB.runCommand({explain: {find: ""}}),
- ErrorCodes.InvalidNamespace);
- assert.commandFailedWithCode(testDB.runCommand({explain: {find: "\0"}}),
- ErrorCodes.InvalidNamespace);
- // TODO SERVER-24128: Make namespace parsing for explain of find reject names with embedded null
- // bytes.
- // assert.commandFailedWithCode(testDB.runCommand({explain: {find: "a\0b"}}),
- // ErrorCodes.InvalidNamespace);
-
- // Test explain of findAndModify fails with an invalid collection name.
- assert.commandFailedWithCode(testDB.runCommand({explain: {findAndModify: "", update: {a: 2}}}),
- ErrorCodes.InvalidNamespace);
- assert.commandFailedWithCode(
- testDB.runCommand({explain: {findAndModify: "\0", update: {a: 2}}}), 17295);
- assert.commandFailedWithCode(
- testDB.runCommand({explain: {findAndModify: "a\0b", update: {a: 2}}}), 17295);
-
- // Test explain of delete fails with an invalid collection name.
- // TODO SERVER-24128: Currently, we massert() and print a stack trace. Instead, we should fail
- // gracefully with a user assertion.
- assert.commandFailedWithCode(
- testDB.runCommand({explain: {delete: "", deletes: [{q: {a: 1}, limit: 1}]}}), 28538);
- assert.commandFailedWithCode(
- testDB.runCommand({explain: {delete: "\0", deletes: [{q: {a: 1}, limit: 1}]}}), 17295);
- assert.commandFailedWithCode(
- testDB.runCommand({explain: {delete: "a\0b", deletes: [{q: {a: 1}, limit: 1}]}}), 17295);
-
- // Test explain of update fails with an invalid collection name.
- // TODO SERVER-24128: Currently, we massert() and print a stack trace. Instead, we should fail
- // gracefully with a user assertion.
- assert.commandFailedWithCode(
- testDB.runCommand({explain: {update: "", updates: [{q: {a: 1}, u: {a: 2}}]}}), 28538);
- assert.commandFailedWithCode(
- testDB.runCommand({explain: {update: "\0", updates: [{q: {a: 1}, u: {a: 2}}]}}), 17295);
- assert.commandFailedWithCode(
- testDB.runCommand({explain: {update: "a\0b", updates: [{q: {a: 1}, u: {a: 2}}]}}), 17295);
-
- // Test aggregate fails with an invalid collection name.
- // TODO SERVER-24128: The following two commands fail as defined in non-auth configurations,
- // but fail differently in auth configurations. We should fail identically in both
- // configurations.
- // assert.commandFailedWithCode(testDB.runCommand({aggregate: "", pipeline:[]}),
- // ErrorCodes.UnknownError);
- // assert.commandFailedWithCode(testDB.runCommand({aggregate: "\0", pipeline:[]}),
- // ErrorCodes.UnknownError);
- // TODO SERVER-24128: Make namespace parsing for group reject names with embedded null bytes.
- // TODO SERVER-24128: The following command runs successfully in non-auth configurations, but
- // fails in auth configurations. We should fail in both configurations.
- // assert.commandFailedWithCode(testDB.runCommand({aggregate: "a\0b", pipeline:[]}),
- // ErrorCodes.InvalidNamespace);
-
- // Test count fails with an invalid collection name.
- assert.commandFailedWithCode(testDB.runCommand({count: ""}), ErrorCodes.InvalidNamespace);
- assert.commandFailedWithCode(testDB.runCommand({count: "\0"}), ErrorCodes.InvalidNamespace);
- // TODO SERVER-24128: Make namespace parsing for count reject names with embedded null bytes.
- // assert.commandFailedWithCode(testDB.runCommand({count: "a\0b"}),
- // ErrorCodes.InvalidNamespace);
-
- // Test distinct fails with an invalid collection name.
- // TODO SERVER-24128: Currently, we massert() and print a stack trace. Instead, we should fail
- // gracefully with a user assertion.
- assert.commandFailedWithCode(testDB.runCommand({distinct: "", key: "a"}), 28538);
- // TODO SERVER-24128: Currently, we massert() and print a stack trace. Instead, we should fail
- // gracefully with a user assertion.
- assert.commandFailedWithCode(testDB.runCommand({distinct: "\0", key: "a"}), 28538);
- // TODO SERVER-24128: Make namespace parsing for distinct reject names with embedded null bytes.
- // assert.commandFailedWithCode(testDB.runCommand({distinct: "a\0b", key: "a"}),
- // ErrorCodes.InvalidNamespace);
-
- // Test group fails with an invalid collection name.
- // TODO SERVER-24128: Currently, we massert() and print a stack trace. Instead, we should fail
- // gracefully with a user assertion.
- // TODO SERVER-24128: The following command fails as defined below in non-auth configurations,
- // but fails differently in auth configurations. We should fail identically in both
- // configurations.
- // assert.commandFailedWithCode(
- // testDB.runCommand({group: {ns: "", $reduce: () => {}, initial: {}}}), 28538);
- // TODO SERVER-24128: Make namespace parsing for group reject names with embedded null bytes.
- // TODO SERVER-24128: The following two commands run successfully in non-auth configurations,
- // but fail in auth configurations. We should fail in both configurations.
- // assert.commandFailedWithCode(testDB.runCommand({group: {ns: "\0", $reduce: () => {}, initial:
- // {}}}), ErrorCodes.InvalidNamespace);
- // TODO SERVER-24128: Make namespace parsing for group reject names with embedded null bytes.
- // assert.commandFailedWithCode(testDB.runCommand({group: {ns: "a\0b", $reduce: () => {},
- // initial:
- // {}}}), ErrorCodes.InvalidNamespace);
-
- // Test find fails with an invalid collection name.
- assert.commandFailedWithCode(testDB.runCommand({find: ""}), ErrorCodes.InvalidNamespace);
- assert.commandFailedWithCode(testDB.runCommand({find: "\0"}), ErrorCodes.InvalidNamespace);
- // TODO SERVER-24128: Make namespace parsing for find reject names with embedded null bytes.
- // assert.commandFailedWithCode(testDB.runCommand({find: "a\0b"}), ErrorCodes.InvalidNamespace);
-
- // Test findAndModify fails with an invalid collection name.
- assert.commandFailedWithCode(testDB.runCommand({findAndModify: "", update: {a: 2}}),
- ErrorCodes.InvalidNamespace);
- assert.commandFailedWithCode(testDB.runCommand({findAndModify: "\0", update: {a: 2}}), 17295);
- assert.commandFailedWithCode(testDB.runCommand({findAndModify: "a\0b", update: {a: 2}}), 17295);
-
- // Test delete fails with an invalid collection name.
- assert.commandFailedWithCode(testDB.runCommand({delete: "", deletes: [{q: {a: 1}, limit: 1}]}),
- ErrorCodes.InvalidNamespace);
- assert.commandFailedWithCode(
- testDB.runCommand({delete: "\0", deletes: [{q: {a: 1}, limit: 1}]}), 17295);
- assert.commandFailedWithCode(
- testDB.runCommand({delete: "a\0b", deletes: [{q: {a: 1}, limit: 1}]}), 17295);
-
- // Test update fails with an invalid collection name.
- assert.commandFailedWithCode(testDB.runCommand({update: "", updates: [{q: {a: 1}, u: {a: 2}}]}),
- ErrorCodes.InvalidNamespace);
- assert.commandFailedWithCode(
- testDB.runCommand({update: "\0", updates: [{q: {a: 1}, u: {a: 2}}]}), 17295);
- assert.commandFailedWithCode(
- testDB.runCommand({update: "a\0b", updates: [{q: {a: 1}, u: {a: 2}}]}), 17295);
-
- // Stop the sharding test.
- st.stop();
-})();
diff --git a/src/mongo/client/fetcher.cpp b/src/mongo/client/fetcher.cpp
index 741efe9afab..9c73f127809 100644
--- a/src/mongo/client/fetcher.cpp
+++ b/src/mongo/client/fetcher.cpp
@@ -118,7 +118,7 @@ Status parseCursorResponse(const BSONObj& obj,
<< "' field must be a string: "
<< obj);
}
- NamespaceString tempNss(namespaceElement.valuestrsafe());
+ const NamespaceString tempNss(namespaceElement.valueStringData());
if (!tempNss.isValid()) {
return Status(ErrorCodes::BadValue,
str::stream() << "'" << kCursorFieldName << "." << kNamespaceFieldName
diff --git a/src/mongo/db/catalog/create_collection.cpp b/src/mongo/db/catalog/create_collection.cpp
index 743c1481213..227481c4104 100644
--- a/src/mongo/db/catalog/create_collection.cpp
+++ b/src/mongo/db/catalog/create_collection.cpp
@@ -48,14 +48,17 @@ Status createCollection(OperationContext* txn,
// Extract ns from first cmdObj element.
BSONElement firstElt = it.next();
- uassert(15888, "must pass name of collection to create", firstElt.valuestrsafe()[0] != '\0');
+ uassert(ErrorCodes::TypeMismatch,
+ str::stream() << "Expected first element to be of type String in: " << cmdObj,
+ firstElt.type() == BSONType::String);
+ uassert(15888, "must pass name of collection to create", !firstElt.valueStringData().empty());
- Status status = userAllowedCreateNS(dbName, firstElt.valuestr());
+ Status status = userAllowedCreateNS(dbName, firstElt.valueStringData());
if (!status.isOK()) {
return status;
}
- NamespaceString nss(dbName, firstElt.valuestrsafe());
+ const NamespaceString nss(dbName, firstElt.valueStringData());
// Build options object from remaining cmdObj elements.
BSONObjBuilder optionsBuilder;
diff --git a/src/mongo/db/cloner.cpp b/src/mongo/db/cloner.cpp
index 6b3184eae4b..fbba5e00365 100644
--- a/src/mongo/db/cloner.cpp
+++ b/src/mongo/db/cloner.cpp
@@ -461,7 +461,7 @@ bool Cloner::copyCollection(OperationContext* txn,
uassert(ErrorCodes::CommandNotSupportedOnView,
str::stream() << "copyCollection not supported for views. ns: "
- << col["name"].valuestrsafe(),
+ << col["name"].valueStringData(),
!(status.isOK() && namespaceType == "view"));
}
diff --git a/src/mongo/db/commands.cpp b/src/mongo/db/commands.cpp
index 42bad714800..8bb1892afe5 100644
--- a/src/mongo/db/commands.cpp
+++ b/src/mongo/db/commands.cpp
@@ -106,13 +106,13 @@ string Command::parseNs(const string& dbname, const BSONObj& cmdObj) const {
if (first.type() != mongo::String)
return dbname;
- return str::stream() << dbname << '.' << cmdObj.firstElement().valuestr();
+ return str::stream() << dbname << '.' << cmdObj.firstElement().valueStringData();
}
ResourcePattern Command::parseResourcePattern(const std::string& dbname,
const BSONObj& cmdObj) const {
- std::string ns = parseNs(dbname, cmdObj);
- if (ns.find('.') == std::string::npos) {
+ const std::string ns = parseNs(dbname, cmdObj);
+ if (!NamespaceString::validCollectionComponent(ns)) {
return ResourcePattern::forDatabaseName(ns);
}
return ResourcePattern::forExactNamespace(NamespaceString(ns));
diff --git a/src/mongo/db/commands/collection_to_capped.cpp b/src/mongo/db/commands/collection_to_capped.cpp
index 5484395cd6e..fda2e9b687e 100644
--- a/src/mongo/db/commands/collection_to_capped.cpp
+++ b/src/mongo/db/commands/collection_to_capped.cpp
@@ -72,12 +72,17 @@ public:
targetActions.addAction(ActionType::insert);
targetActions.addAction(ActionType::createIndex);
targetActions.addAction(ActionType::convertToCapped);
- std::string collection = cmdObj.getStringField("toCollection");
- uassert(16708, "bad 'toCollection' value", !collection.empty());
- out->push_back(
- Privilege(ResourcePattern::forExactNamespace(NamespaceString(dbname, collection)),
- targetActions));
+ const auto nssElt = cmdObj["toCollection"];
+ uassert(ErrorCodes::TypeMismatch,
+ "'toCollection' must be of type String",
+ nssElt.type() == BSONType::String);
+ const NamespaceString nss(dbname, nssElt.valueStringData());
+ uassert(ErrorCodes::InvalidNamespace,
+ str::stream() << "Invalid target namespace: " << nss.ns(),
+ nss.isValid());
+
+ out->push_back(Privilege(ResourcePattern::forExactNamespace(nss), targetActions));
}
bool run(OperationContext* txn,
const string& dbname,
@@ -85,12 +90,30 @@ public:
int,
string& errmsg,
BSONObjBuilder& result) {
- string from = jsobj.getStringField("cloneCollectionAsCapped");
- string to = jsobj.getStringField("toCollection");
+ const auto fromElt = jsobj["cloneCollectionAsCapped"];
+ const auto toElt = jsobj["toCollection"];
+
+ uassert(ErrorCodes::TypeMismatch,
+ "'cloneCollectionAsCapped' must be of type String",
+ fromElt.type() == BSONType::String);
+ uassert(ErrorCodes::TypeMismatch,
+ "'toCollection' must be of type String",
+ toElt.type() == BSONType::String);
+
+ const StringData from(fromElt.valueStringData());
+ const StringData to(toElt.valueStringData());
+
+ uassert(ErrorCodes::InvalidNamespace,
+ str::stream() << "Invalid source collection name: " << from,
+ NamespaceString::validCollectionName(from));
+ uassert(ErrorCodes::InvalidNamespace,
+ str::stream() << "Invalid target collection name: " << to,
+ NamespaceString::validCollectionName(to));
+
double size = jsobj.getField("size").number();
bool temp = jsobj.getField("temp").trueValue();
- if (from.empty() || to.empty() || size == 0) {
+ if (size == 0) {
errmsg = "invalid command spec";
return false;
}
@@ -116,7 +139,8 @@ public:
str::stream() << "database " << dbname << " not found"));
}
- Status status = cloneCollectionAsCapped(txn, db, from, to, size, temp);
+ Status status =
+ cloneCollectionAsCapped(txn, db, from.toString(), to.toString(), size, temp);
return appendCommandStatus(result, status);
}
} cmdCloneCollectionAsCapped;
@@ -152,16 +176,15 @@ public:
int,
string& errmsg,
BSONObjBuilder& result) {
- string shortSource = jsobj.getStringField("convertToCapped");
+ const NamespaceString nss(parseNsCollectionRequired(dbname, jsobj));
double size = jsobj.getField("size").number();
- if (shortSource.empty() || size == 0) {
+ if (size == 0) {
errmsg = "invalid command spec";
return false;
}
- return appendCommandStatus(
- result, convertToCapped(txn, NamespaceString(dbname, shortSource), size));
+ return appendCommandStatus(result, convertToCapped(txn, nss, size));
}
} cmdConvertToCapped;
diff --git a/src/mongo/db/commands/copydb.cpp b/src/mongo/db/commands/copydb.cpp
index 2e80deab625..00f26f8bd4d 100644
--- a/src/mongo/db/commands/copydb.cpp
+++ b/src/mongo/db/commands/copydb.cpp
@@ -134,24 +134,36 @@ public:
}
CloneOptions cloneOptions;
- cloneOptions.fromDB = cmdObj.getStringField("fromdb");
+ const auto fromdbElt = cmdObj["fromdb"];
+ uassert(ErrorCodes::TypeMismatch,
+ "'fromdb' must be of type String",
+ fromdbElt.type() == BSONType::String);
+ cloneOptions.fromDB = fromdbElt.str();
cloneOptions.slaveOk = cmdObj["slaveOk"].trueValue();
cloneOptions.useReplAuth = false;
cloneOptions.snapshot = true;
- string todb = cmdObj.getStringField("todb");
- if (fromhost.empty() || todb.empty() || cloneOptions.fromDB.empty()) {
+ const auto todbElt = cmdObj["todb"];
+ uassert(ErrorCodes::TypeMismatch,
+ "'todb' must be of type String",
+ todbElt.type() == BSONType::String);
+ const std::string todb = todbElt.str();
+
+ uassert(ErrorCodes::InvalidNamespace,
+ str::stream() << "Invalid 'todb' name: " << todb,
+ NamespaceString::validDBName(todb, NamespaceString::DollarInDbNameBehavior::Allow));
+ uassert(ErrorCodes::InvalidNamespace,
+ str::stream() << "Invalid 'fromdb' name: " << cloneOptions.fromDB,
+ NamespaceString::validDBName(cloneOptions.fromDB,
+ NamespaceString::DollarInDbNameBehavior::Allow));
+
+ if (fromhost.empty()) {
errmsg =
"params missing - {copydb: 1, fromhost: <connection string>, "
"fromdb: <db>, todb: <db>}";
return false;
}
- if (!NamespaceString::validDBName(todb, NamespaceString::DollarInDbNameBehavior::Allow)) {
- errmsg = "invalid todb name: " + todb;
- return false;
- }
-
Cloner cloner;
// Get MONGODB-CR parameters
diff --git a/src/mongo/db/commands/copydb_common.cpp b/src/mongo/db/commands/copydb_common.cpp
index ab6f5078429..583c4054cfc 100644
--- a/src/mongo/db/commands/copydb_common.cpp
+++ b/src/mongo/db/commands/copydb_common.cpp
@@ -44,9 +44,18 @@ namespace mongo {
namespace copydb {
Status checkAuthForCopydbCommand(Client* client, const std::string& dbname, const BSONObj& cmdObj) {
+ const auto fromdbElt = cmdObj["fromdb"];
+ if (fromdbElt.type() != BSONType::String) {
+ return Status(ErrorCodes::TypeMismatch, "'fromdb' must be of type String");
+ }
+ const auto todbElt = cmdObj["todb"];
+ if (todbElt.type() != BSONType::String) {
+ return Status(ErrorCodes::TypeMismatch, "'todb' must be of type String");
+ }
+
bool fromSelf = StringData(cmdObj.getStringField("fromhost")).empty();
- StringData fromdb = cmdObj.getStringField("fromdb");
- StringData todb = cmdObj.getStringField("todb");
+ const StringData fromdb = fromdbElt.valueStringData();
+ const StringData todb = todbElt.valueStringData();
// get system collections
std::vector<std::string> legalClientSystemCollections;
diff --git a/src/mongo/db/commands/copydb_start_commands.cpp b/src/mongo/db/commands/copydb_start_commands.cpp
index 3e7f0ee7e60..3dc9769024a 100644
--- a/src/mongo/db/commands/copydb_start_commands.cpp
+++ b/src/mongo/db/commands/copydb_start_commands.cpp
@@ -176,7 +176,15 @@ public:
int,
string& errmsg,
BSONObjBuilder& result) {
- const string fromDb = cmdObj.getStringField("fromdb");
+ const auto fromdbElt = cmdObj["fromdb"];
+ uassert(ErrorCodes::TypeMismatch,
+ "'renameCollection' must be of type String",
+ fromdbElt.type() == BSONType::String);
+ const string fromDb = fromdbElt.str();
+ uassert(
+ ErrorCodes::InvalidNamespace,
+ str::stream() << "Invalid 'fromdb' name: " << fromDb,
+ NamespaceString::validDBName(fromDb, NamespaceString::DollarInDbNameBehavior::Allow));
string fromHost = cmdObj.getStringField("fromhost");
if (fromHost.empty()) {
diff --git a/src/mongo/db/commands/create_indexes.cpp b/src/mongo/db/commands/create_indexes.cpp
index 570d95a7bdb..4a1707b9124 100644
--- a/src/mongo/db/commands/create_indexes.cpp
+++ b/src/mongo/db/commands/create_indexes.cpp
@@ -231,7 +231,7 @@ public:
int options,
string& errmsg,
BSONObjBuilder& result) {
- const NamespaceString ns(parseNs(dbname, cmdObj));
+ const NamespaceString ns(parseNsCollectionRequired(dbname, cmdObj));
Status status = userAllowedWriteNS(ns);
if (!status.isOK())
diff --git a/src/mongo/db/commands/dbcommands.cpp b/src/mongo/db/commands/dbcommands.cpp
index 4333c271f7a..5eea6f7e39f 100644
--- a/src/mongo/db/commands/dbcommands.cpp
+++ b/src/mongo/db/commands/dbcommands.cpp
@@ -539,7 +539,7 @@ public:
virtual Status checkAuthForCommand(Client* client,
const std::string& dbname,
const BSONObj& cmdObj) {
- NamespaceString nss(parseNs(dbname, cmdObj));
+ const NamespaceString nss(parseNs(dbname, cmdObj));
return AuthorizationSession::get(client)->checkAuthForCreate(nss, cmdObj);
}
@@ -549,7 +549,7 @@ public:
int,
string& errmsg,
BSONObjBuilder& result) {
- const NamespaceString ns(parseNs(dbname, cmdObj));
+ const NamespaceString ns(parseNsCollectionRequired(dbname, cmdObj));
if (cmdObj.hasField("autoIndexId")) {
const char* deprecationWarning =
@@ -666,7 +666,13 @@ public:
}
virtual std::string parseNs(const std::string& dbname, const BSONObj& cmdObj) const {
- std::string collectionName = cmdObj.getStringField("root");
+ std::string collectionName;
+ if (const auto rootElt = cmdObj["root"]) {
+ uassert(ErrorCodes::InvalidNamespace,
+ "'root' must be of type String",
+ rootElt.type() == BSONType::String);
+ collectionName = rootElt.str();
+ }
if (collectionName.empty())
collectionName = "fs";
collectionName += ".chunks";
@@ -685,7 +691,7 @@ public:
int,
string& errmsg,
BSONObjBuilder& result) {
- const std::string ns = parseNs(dbname, jsobj);
+ const NamespaceString nss(parseNs(dbname, jsobj));
md5digest d;
md5_state_t st;
@@ -713,7 +719,7 @@ public:
BSONObj sort = BSON("files_id" << 1 << "n" << 1);
MONGO_WRITE_CONFLICT_RETRY_LOOP_BEGIN {
- auto qr = stdx::make_unique<QueryRequest>(NamespaceString(ns));
+ auto qr = stdx::make_unique<QueryRequest>(nss);
qr->setFilter(query);
qr->setSort(sort);
@@ -728,7 +734,7 @@ public:
// Check shard version at startup.
// This will throw before we've done any work if shard version is outdated
// We drop and re-acquire these locks every document because md5'ing is expensive
- unique_ptr<AutoGetCollectionForRead> ctx(new AutoGetCollectionForRead(txn, ns));
+ unique_ptr<AutoGetCollectionForRead> ctx(new AutoGetCollectionForRead(txn, nss));
Collection* coll = ctx->getCollection();
auto statusWithPlanExecutor = getExecutor(txn,
@@ -756,7 +762,7 @@ public:
break; // skipped chunk is probably on another shard
}
log() << "should have chunk: " << n << " have:" << myn;
- dumpChunks(txn, ns, query, sort);
+ dumpChunks(txn, nss.ns(), query, sort);
uassert(10040, "chunks out of order", n == myn);
}
@@ -774,7 +780,7 @@ public:
try {
// RELOCKED
- ctx.reset(new AutoGetCollectionForRead(txn, ns));
+ ctx.reset(new AutoGetCollectionForRead(txn, nss));
} catch (const SendStaleConfigException& ex) {
LOG(1) << "chunk metadata changed during filemd5, will retarget and continue";
break;
@@ -1009,7 +1015,7 @@ public:
int,
string& errmsg,
BSONObjBuilder& result) {
- const NamespaceString nss(parseNs(dbname, jsobj));
+ const NamespaceString nss(parseNsCollectionRequired(dbname, jsobj));
if (nss.coll().empty()) {
errmsg = "No collection name specified";
@@ -1048,7 +1054,7 @@ public:
virtual Status checkAuthForCommand(Client* client,
const std::string& dbname,
const BSONObj& cmdObj) {
- NamespaceString nss(parseNs(dbname, cmdObj));
+ const NamespaceString nss(parseNs(dbname, cmdObj));
return AuthorizationSession::get(client)->checkAuthForCollMod(nss, cmdObj);
}
@@ -1058,7 +1064,7 @@ public:
int,
string& errmsg,
BSONObjBuilder& result) {
- const NamespaceString nss = parseNsCollectionRequired(dbname, jsobj);
+ const NamespaceString nss(parseNsCollectionRequired(dbname, jsobj));
return appendCommandStatus(result, collMod(txn, nss, jsobj, &result));
}
@@ -1107,6 +1113,9 @@ public:
}
const string ns = parseNs(dbname, jsobj);
+ uassert(ErrorCodes::InvalidNamespace,
+ str::stream() << "Invalid db name: " << ns,
+ NamespaceString::validDBName(ns, NamespaceString::DollarInDbNameBehavior::Allow));
// TODO (Kal): OldClientContext legacy, needs to be removed
{
diff --git a/src/mongo/db/commands/dbhash.cpp b/src/mongo/db/commands/dbhash.cpp
index acfa7435c72..6d68cd93f00 100644
--- a/src/mongo/db/commands/dbhash.cpp
+++ b/src/mongo/db/commands/dbhash.cpp
@@ -102,7 +102,10 @@ public:
}
list<string> colls;
- const string ns = parseNs(dbname, cmdObj);
+ const std::string ns = parseNs(dbname, cmdObj);
+ uassert(ErrorCodes::InvalidNamespace,
+ str::stream() << "Invalid db name: " << ns,
+ NamespaceString::validDBName(ns, NamespaceString::DollarInDbNameBehavior::Allow));
// We lock the entire database in S-mode in order to ensure that the contents will not
// change for the snapshot.
diff --git a/src/mongo/db/commands/distinct.cpp b/src/mongo/db/commands/distinct.cpp
index e15c0e4c497..2543e4879bc 100644
--- a/src/mongo/db/commands/distinct.cpp
+++ b/src/mongo/db/commands/distinct.cpp
@@ -117,8 +117,7 @@ public:
ExplainCommon::Verbosity verbosity,
const rpc::ServerSelectionMetadata&,
BSONObjBuilder* out) const {
- const string ns = parseNs(dbname, cmdObj);
- const NamespaceString nss(ns);
+ const NamespaceString nss(parseNsCollectionRequired(dbname, cmdObj));
const ExtensionsCallbackReal extensionsCallback(txn, &nss);
auto parsedDistinct = ParsedDistinct::parse(txn, nss, cmdObj, extensionsCallback, true);
@@ -134,7 +133,7 @@ public:
"http://dochub.mongodb.org/core/3.4-feature-compatibility.");
}
- AutoGetCollectionOrViewForRead ctx(txn, ns);
+ AutoGetCollectionOrViewForRead ctx(txn, nss);
Collection* collection = ctx.getCollection();
if (ctx.getView()) {
@@ -151,7 +150,7 @@ public:
}
auto executor = getExecutorDistinct(
- txn, collection, ns, &parsedDistinct.getValue(), PlanExecutor::YIELD_AUTO);
+ txn, collection, nss.ns(), &parsedDistinct.getValue(), PlanExecutor::YIELD_AUTO);
if (!executor.isOK()) {
return executor.getStatus();
}
@@ -166,8 +165,7 @@ public:
int options,
string& errmsg,
BSONObjBuilder& result) {
- const string ns = parseNs(dbname, cmdObj);
- const NamespaceString nss(ns);
+ const NamespaceString nss(parseNsCollectionRequired(dbname, cmdObj));
const ExtensionsCallbackReal extensionsCallback(txn, &nss);
auto parsedDistinct = ParsedDistinct::parse(txn, nss, cmdObj, extensionsCallback, false);
@@ -185,7 +183,7 @@ public:
"http://dochub.mongodb.org/core/3.4-feature-compatibility."));
}
- AutoGetCollectionOrViewForRead ctx(txn, ns);
+ AutoGetCollectionOrViewForRead ctx(txn, nss);
Collection* collection = ctx.getCollection();
if (ctx.getView()) {
@@ -214,7 +212,7 @@ public:
}
auto executor = getExecutorDistinct(
- txn, collection, ns, &parsedDistinct.getValue(), PlanExecutor::YIELD_AUTO);
+ txn, collection, nss.ns(), &parsedDistinct.getValue(), PlanExecutor::YIELD_AUTO);
if (!executor.isOK()) {
return appendCommandStatus(result, executor.getStatus());
}
diff --git a/src/mongo/db/commands/find_cmd.cpp b/src/mongo/db/commands/find_cmd.cpp
index dc9e5e977cd..bdb0ea33fd6 100644
--- a/src/mongo/db/commands/find_cmd.cpp
+++ b/src/mongo/db/commands/find_cmd.cpp
@@ -124,7 +124,7 @@ public:
Status checkAuthForCommand(Client* client,
const std::string& dbname,
const BSONObj& cmdObj) override {
- NamespaceString nss(parseNs(dbname, cmdObj));
+ const NamespaceString nss(parseNs(dbname, cmdObj));
auto hasTerm = cmdObj.hasField(kTermField);
return AuthorizationSession::get(client)->checkAuthForFind(nss, hasTerm);
}
diff --git a/src/mongo/db/commands/geo_near_cmd.cpp b/src/mongo/db/commands/geo_near_cmd.cpp
index d3f450b3855..eae4d16be7e 100644
--- a/src/mongo/db/commands/geo_near_cmd.cpp
+++ b/src/mongo/db/commands/geo_near_cmd.cpp
@@ -110,7 +110,7 @@ public:
return false;
}
- const NamespaceString nss(parseNs(dbname, cmdObj));
+ const NamespaceString nss(parseNsCollectionRequired(dbname, cmdObj));
AutoGetCollectionForRead ctx(txn, nss);
Collection* collection = ctx.getCollection();
diff --git a/src/mongo/db/commands/getmore_cmd.cpp b/src/mongo/db/commands/getmore_cmd.cpp
index cb93562e5df..6c956180070 100644
--- a/src/mongo/db/commands/getmore_cmd.cpp
+++ b/src/mongo/db/commands/getmore_cmd.cpp
@@ -129,7 +129,7 @@ public:
}
std::string parseNs(const std::string& dbname, const BSONObj& cmdObj) const override {
- return GetMoreRequest::parseNs(dbname, cmdObj);
+ return GetMoreRequest::parseNs(dbname, cmdObj).ns();
}
Status checkAuthForCommand(Client* client,
diff --git a/src/mongo/db/commands/group_cmd.cpp b/src/mongo/db/commands/group_cmd.cpp
index 88d03549317..0df1c64d113 100644
--- a/src/mongo/db/commands/group_cmd.cpp
+++ b/src/mongo/db/commands/group_cmd.cpp
@@ -98,18 +98,25 @@ private:
virtual Status checkAuthForCommand(Client* client,
const std::string& dbname,
const BSONObj& cmdObj) {
- std::string ns = parseNs(dbname, cmdObj);
+ const NamespaceString nss(parseNs(dbname, cmdObj));
+
if (!AuthorizationSession::get(client)->isAuthorizedForActionsOnNamespace(
- NamespaceString(ns), ActionType::find)) {
+ nss, ActionType::find)) {
return Status(ErrorCodes::Unauthorized, "unauthorized");
}
return Status::OK();
}
virtual std::string parseNs(const std::string& dbname, const BSONObj& cmdObj) const {
- const BSONObj& p = cmdObj.firstElement().embeddedObjectUserCheck();
- uassert(17211, "ns has to be set", p["ns"].type() == String);
- return dbname + "." + p["ns"].String();
+ const auto nsElt = cmdObj.firstElement().embeddedObjectUserCheck()["ns"];
+ uassert(ErrorCodes::InvalidNamespace,
+ "'ns' must be of type String",
+ nsElt.type() == BSONType::String);
+ const NamespaceString nss(dbname, nsElt.valueStringData());
+ uassert(ErrorCodes::InvalidNamespace,
+ str::stream() << "Invalid namespace: " << nss.ns(),
+ nss.isValid());
+ return nss.ns();
}
virtual Status explain(OperationContext* txn,
@@ -230,7 +237,7 @@ private:
Status _parseRequest(const std::string& dbname,
const BSONObj& cmdObj,
GroupRequest* request) const {
- request->ns = parseNs(dbname, cmdObj);
+ request->ns = NamespaceString(parseNs(dbname, cmdObj));
// By default, group requests are regular group not explain of group.
request->explain = false;
diff --git a/src/mongo/db/commands/index_filter_commands.cpp b/src/mongo/db/commands/index_filter_commands.cpp
index d9a5c5d900c..a591d48491c 100644
--- a/src/mongo/db/commands/index_filter_commands.cpp
+++ b/src/mongo/db/commands/index_filter_commands.cpp
@@ -120,8 +120,8 @@ bool IndexFilterCommand::run(OperationContext* txn,
int options,
string& errmsg,
BSONObjBuilder& result) {
- string ns = parseNs(dbname, cmdObj);
- Status status = runIndexFilterCommand(txn, ns, cmdObj, &result);
+ const NamespaceString nss(parseNsCollectionRequired(dbname, cmdObj));
+ Status status = runIndexFilterCommand(txn, nss.ns(), cmdObj, &result);
return appendCommandStatus(result, status);
}
diff --git a/src/mongo/db/commands/list_indexes.cpp b/src/mongo/db/commands/list_indexes.cpp
index 2fc5e07545b..59528d8bf11 100644
--- a/src/mongo/db/commands/list_indexes.cpp
+++ b/src/mongo/db/commands/list_indexes.cpp
@@ -100,7 +100,7 @@ public:
// Check for the listIndexes ActionType on the database, or find on system.indexes for pre
// 3.0 systems.
- NamespaceString ns(parseNs(dbname, cmdObj));
+ const NamespaceString ns(parseNsCollectionRequired(dbname, cmdObj));
if (authzSession->isAuthorizedForActionsOnResource(ResourcePattern::forExactNamespace(ns),
ActionType::listIndexes) ||
authzSession->isAuthorizedForActionsOnResource(
@@ -122,7 +122,7 @@ public:
int,
string& errmsg,
BSONObjBuilder& result) {
- const NamespaceString ns(parseNs(dbname, cmdObj));
+ const NamespaceString ns(parseNsCollectionRequired(dbname, cmdObj));
const long long defaultBatchSize = std::numeric_limits<long long>::max();
long long batchSize;
diff --git a/src/mongo/db/commands/mr.cpp b/src/mongo/db/commands/mr.cpp
index 5e5306f5452..06a0fc43878 100644
--- a/src/mongo/db/commands/mr.cpp
+++ b/src/mongo/db/commands/mr.cpp
@@ -279,7 +279,14 @@ void JSReducer::_reduce(const BSONList& tuples, BSONObj& key, int& endSizeEstima
Config::Config(const string& _dbname, const BSONObj& cmdObj) {
dbname = _dbname;
- ns = dbname + "." + cmdObj.firstElement().valuestrsafe();
+ uassert(ErrorCodes::TypeMismatch,
+ str::stream() << "'mapReduce' must be of type String",
+ cmdObj.firstElement().type() == BSONType::String);
+ const NamespaceString nss(dbname, cmdObj.firstElement().valueStringData());
+ uassert(ErrorCodes::InvalidNamespace,
+ str::stream() << "Invalid namespace: " << nss.ns(),
+ nss.isValid());
+ ns = nss.ns();
verbose = cmdObj["verbose"].trueValue();
jsMode = cmdObj["jsMode"].trueValue();
@@ -306,9 +313,12 @@ Config::Config(const string& _dbname, const BSONObj& cmdObj) {
}
if (outputOptions.outType != INMEMORY) { // setup temp collection name
- tempNamespace = str::stream()
- << (outputOptions.outDB.empty() ? dbname : outputOptions.outDB) << ".tmp.mr."
- << cmdObj.firstElement().String() << "_" << JOB_NUMBER.fetchAndAdd(1);
+ tempNamespace =
+ NamespaceString(outputOptions.outDB.empty() ? dbname : outputOptions.outDB,
+ str::stream() << "tmp.mr." << cmdObj.firstElement().valueStringData()
+ << "_"
+ << JOB_NUMBER.fetchAndAdd(1))
+ .ns();
incLong = tempNamespace + "_inc";
}
@@ -1723,13 +1733,19 @@ public:
ShardedConnectionInfo::addHook();
// legacy name
- string shardedOutputCollection = cmdObj["shardedOutputCollection"].valuestrsafe();
+ const auto shardedOutputCollectionElt = cmdObj["shardedOutputCollection"];
+ uassert(ErrorCodes::InvalidNamespace,
+ "'shardedOutputCollection' must be of type String",
+ shardedOutputCollectionElt.type() == BSONType::String);
+ const std::string shardedOutputCollection = shardedOutputCollectionElt.str();
verify(shardedOutputCollection.size() > 0);
- string inputNS;
+
+ std::string inputNS;
if (cmdObj["inputDB"].type() == String) {
- inputNS = cmdObj["inputDB"].String() + "." + shardedOutputCollection;
+ inputNS =
+ NamespaceString(cmdObj["inputDB"].valueStringData(), shardedOutputCollection).ns();
} else {
- inputNS = dbname + "." + shardedOutputCollection;
+ inputNS = NamespaceString(dbname, shardedOutputCollection).ns();
}
CurOp* curOp = CurOp::get(txn);
diff --git a/src/mongo/db/commands/mr_common.cpp b/src/mongo/db/commands/mr_common.cpp
index d180f276249..1356a6f6fb2 100644
--- a/src/mongo/db/commands/mr_common.cpp
+++ b/src/mongo/db/commands/mr_common.cpp
@@ -88,9 +88,12 @@ Config::OutputOptions Config::parseOutputOptions(const std::string& dbname, cons
}
if (outputOptions.outType != INMEMORY) {
- outputOptions.finalNamespace = mongoutils::str::stream()
- << (outputOptions.outDB.empty() ? dbname : outputOptions.outDB) << "."
- << outputOptions.collectionName;
+ const StringData outDb(outputOptions.outDB.empty() ? dbname : outputOptions.outDB);
+ const NamespaceString nss(outDb, outputOptions.collectionName);
+ uassert(ErrorCodes::InvalidNamespace,
+ str::stream() << "Invalid 'out' namespace: " << nss.ns(),
+ nss.isValid());
+ outputOptions.finalNamespace = nss.ns();
}
return outputOptions;
@@ -103,7 +106,7 @@ void addPrivilegesRequiredForMapReduce(Command* commandTemplate,
Config::OutputOptions outputOptions = Config::parseOutputOptions(dbname, cmdObj);
ResourcePattern inputResource(commandTemplate->parseResourcePattern(dbname, cmdObj));
- uassert(17142,
+ uassert(ErrorCodes::InvalidNamespace,
mongoutils::str::stream() << "Invalid input resource " << inputResource.toString(),
inputResource.isExactNamespacePattern());
out->push_back(Privilege(inputResource, ActionType::find));
@@ -123,7 +126,7 @@ void addPrivilegesRequiredForMapReduce(Command* commandTemplate,
ResourcePattern outputResource(
ResourcePattern::forExactNamespace(NamespaceString(outputOptions.finalNamespace)));
- uassert(17143,
+ uassert(ErrorCodes::InvalidNamespace,
mongoutils::str::stream() << "Invalid target namespace "
<< outputResource.ns().ns(),
outputResource.ns().isValid());
diff --git a/src/mongo/db/commands/parallel_collection_scan.cpp b/src/mongo/db/commands/parallel_collection_scan.cpp
index 6fee00a712f..75b0e59512b 100644
--- a/src/mongo/db/commands/parallel_collection_scan.cpp
+++ b/src/mongo/db/commands/parallel_collection_scan.cpp
@@ -90,7 +90,7 @@ public:
int options,
string& errmsg,
BSONObjBuilder& result) {
- const NamespaceString ns(parseNs(dbname, cmdObj));
+ const NamespaceString ns(parseNsCollectionRequired(dbname, cmdObj));
AutoGetCollectionForRead ctx(txn, ns.ns());
diff --git a/src/mongo/db/commands/pipeline_command.cpp b/src/mongo/db/commands/pipeline_command.cpp
index 2eb1c49649d..7fe8d54992d 100644
--- a/src/mongo/db/commands/pipeline_command.cpp
+++ b/src/mongo/db/commands/pipeline_command.cpp
@@ -337,7 +337,7 @@ public:
Status checkAuthForCommand(Client* client,
const std::string& dbname,
const BSONObj& cmdObj) final {
- NamespaceString nss(parseNs(dbname, cmdObj));
+ const NamespaceString nss(parseNsCollectionRequired(dbname, cmdObj));
return AuthorizationSession::get(client)->checkAuthForAggregate(nss, cmdObj);
}
@@ -606,12 +606,7 @@ public:
int options,
string& errmsg,
BSONObjBuilder& result) {
- const std::string ns = parseNs(db, cmdObj);
- if (nsToCollectionSubstring(ns).empty()) {
- errmsg = "missing collection name";
- return false;
- }
- NamespaceString nss(ns);
+ const NamespaceString nss(parseNsCollectionRequired(db, cmdObj));
// Parse the options for this request.
auto request = AggregationRequest::parseFromBSON(nss, cmdObj);
diff --git a/src/mongo/db/commands/plan_cache_commands.cpp b/src/mongo/db/commands/plan_cache_commands.cpp
index 25b76b6fbe9..fe713a0667f 100644
--- a/src/mongo/db/commands/plan_cache_commands.cpp
+++ b/src/mongo/db/commands/plan_cache_commands.cpp
@@ -115,8 +115,8 @@ bool PlanCacheCommand::run(OperationContext* txn,
int options,
string& errmsg,
BSONObjBuilder& result) {
- string ns = parseNs(dbname, cmdObj);
- Status status = runPlanCacheCommand(txn, ns, cmdObj, &result);
+ const NamespaceString nss(parseNsCollectionRequired(dbname, cmdObj));
+ Status status = runPlanCacheCommand(txn, nss.ns(), cmdObj, &result);
return appendCommandStatus(result, status);
}
diff --git a/src/mongo/db/commands/rename_collection_cmd.cpp b/src/mongo/db/commands/rename_collection_cmd.cpp
index 1b213898805..99f5617cf94 100644
--- a/src/mongo/db/commands/rename_collection_cmd.cpp
+++ b/src/mongo/db/commands/rename_collection_cmd.cpp
@@ -91,31 +91,39 @@ public:
int,
string& errmsg,
BSONObjBuilder& result) {
- string source = cmdObj.getStringField(getName());
- string target = cmdObj.getStringField("to");
-
- if (!NamespaceString::validCollectionComponent(target.c_str())) {
- errmsg = "invalid collection name: " + target;
- return false;
- }
- if (source.empty() || target.empty()) {
- errmsg = "invalid command syntax";
- return false;
- }
+ const auto sourceNsElt = cmdObj[getName()];
+ const auto targetNsElt = cmdObj["to"];
+
+ uassert(ErrorCodes::TypeMismatch,
+ "'renameCollection' must be of type String",
+ sourceNsElt.type() == BSONType::String);
+ uassert(ErrorCodes::TypeMismatch,
+ "'to' must be of type String",
+ targetNsElt.type() == BSONType::String);
+
+ const NamespaceString source(sourceNsElt.valueStringData());
+ const NamespaceString target(targetNsElt.valueStringData());
+
+ uassert(ErrorCodes::InvalidNamespace,
+ str::stream() << "Invalid source namespace: " << source.ns(),
+ source.isValid());
+ uassert(ErrorCodes::InvalidNamespace,
+ str::stream() << "Invalid target namespace: " << target.ns(),
+ target.isValid());
if ((repl::getGlobalReplicationCoordinator()->getReplicationMode() !=
repl::ReplicationCoordinator::modeNone)) {
- if (NamespaceString(source).isOplog()) {
+ if (source.isOplog()) {
errmsg = "can't rename live oplog while replicating";
return false;
}
- if (NamespaceString(target).isOplog()) {
+ if (target.isOplog()) {
errmsg = "can't rename to live oplog while replicating";
return false;
}
}
- if (NamespaceString::oplog(source) != NamespaceString::oplog(target)) {
+ if (source.isOplog() != target.isOplog()) {
errmsg = "If either the source or target of a rename is an oplog name, both must be";
return false;
}
@@ -132,16 +140,15 @@ public:
return false;
}
- if (NamespaceString(source).coll() == "system.indexes" ||
- NamespaceString(target).coll() == "system.indexes") {
+ if (source.isSystemDotIndexes() || target.isSystemDotIndexes()) {
errmsg = "renaming system.indexes is not allowed";
return false;
}
return appendCommandStatus(result,
renameCollection(txn,
- NamespaceString(source),
- NamespaceString(target),
+ source,
+ target,
cmdObj["dropTarget"].trueValue(),
cmdObj["stayTemp"].trueValue()));
}
diff --git a/src/mongo/db/commands/rename_collection_common.cpp b/src/mongo/db/commands/rename_collection_common.cpp
index 7bfadbf779f..99b0002521b 100644
--- a/src/mongo/db/commands/rename_collection_common.cpp
+++ b/src/mongo/db/commands/rename_collection_common.cpp
@@ -45,8 +45,18 @@ namespace rename_collection {
Status checkAuthForRenameCollectionCommand(Client* client,
const std::string& dbname,
const BSONObj& cmdObj) {
- NamespaceString sourceNS = NamespaceString(cmdObj.getStringField("renameCollection"));
- NamespaceString targetNS = NamespaceString(cmdObj.getStringField("to"));
+ const auto sourceNsElt = cmdObj["renameCollection"];
+ const auto targetNsElt = cmdObj["to"];
+
+ uassert(ErrorCodes::TypeMismatch,
+ "'renameCollection' must be of type String",
+ sourceNsElt.type() == BSONType::String);
+ uassert(ErrorCodes::TypeMismatch,
+ "'to' must be of type String",
+ targetNsElt.type() == BSONType::String);
+
+ const NamespaceString sourceNS(sourceNsElt.valueStringData());
+ const NamespaceString targetNS(targetNsElt.valueStringData());
bool dropTarget = cmdObj["dropTarget"].trueValue();
if (sourceNS.db() == targetNS.db() && !sourceNS.isSystem() && !targetNS.isSystem()) {
diff --git a/src/mongo/db/commands/test_commands.cpp b/src/mongo/db/commands/test_commands.cpp
index 359e97292c5..ccba3bfb41a 100644
--- a/src/mongo/db/commands/test_commands.cpp
+++ b/src/mongo/db/commands/test_commands.cpp
@@ -81,22 +81,20 @@ public:
int,
string& errmsg,
BSONObjBuilder& result) {
- string coll = cmdObj["godinsert"].valuestrsafe();
- log() << "test only command godinsert invoked coll:" << coll;
- uassert(13049, "godinsert must specify a collection", !coll.empty());
- string ns = dbname + "." + coll;
+ const NamespaceString nss(parseNsCollectionRequired(dbname, cmdObj));
+ log() << "test only command godinsert invoked coll:" << nss.coll();
BSONObj obj = cmdObj["obj"].embeddedObjectUserCheck();
ScopedTransaction transaction(txn, MODE_IX);
Lock::DBLock lk(txn->lockState(), dbname, MODE_X);
- OldClientContext ctx(txn, ns);
+ OldClientContext ctx(txn, nss.ns());
Database* db = ctx.db();
WriteUnitOfWork wunit(txn);
UnreplicatedWritesBlock unreplicatedWritesBlock(txn);
- Collection* collection = db->getCollection(ns);
+ Collection* collection = db->getCollection(nss);
if (!collection) {
- collection = db->createCollection(txn, ns);
+ collection = db->createCollection(txn, nss.ns());
if (!collection) {
errmsg = "could not create collection";
return false;
diff --git a/src/mongo/db/commands/validate.cpp b/src/mongo/db/commands/validate.cpp
index f1d5ff4a349..9622947e59f 100644
--- a/src/mongo/db/commands/validate.cpp
+++ b/src/mongo/db/commands/validate.cpp
@@ -87,9 +87,8 @@ public:
return true;
}
- string ns = dbname + "." + cmdObj.firstElement().valuestrsafe();
+ const NamespaceString nss(parseNsCollectionRequired(dbname, cmdObj));
- NamespaceString ns_string(ns);
const bool full = cmdObj["full"].trueValue();
const bool scanData = cmdObj["scandata"].trueValue();
@@ -101,20 +100,20 @@ public:
level = kValidateRecordStore;
}
- if (!ns_string.isNormal() && full) {
+ if (!nss.isNormal() && full) {
errmsg = "Can only run full validate on a regular collection";
return false;
}
if (!serverGlobalParams.quiet) {
- LOG(0) << "CMD: validate " << ns;
+ LOG(0) << "CMD: validate " << nss.ns();
}
- AutoGetDb ctx(txn, ns_string.db(), MODE_IX);
- Lock::CollectionLock collLk(txn->lockState(), ns_string.ns(), MODE_X);
- Collection* collection = ctx.getDb() ? ctx.getDb()->getCollection(ns_string) : NULL;
+ AutoGetDb ctx(txn, nss.db(), MODE_IX);
+ Lock::CollectionLock collLk(txn->lockState(), nss.ns(), MODE_X);
+ Collection* collection = ctx.getDb() ? ctx.getDb()->getCollection(nss) : NULL;
if (!collection) {
- if (ctx.getDb() && ctx.getDb()->getViewCatalog()->lookup(txn, ns_string.ns())) {
+ if (ctx.getDb() && ctx.getDb()->getViewCatalog()->lookup(txn, nss.ns())) {
errmsg = "Cannot validate a view";
return appendCommandStatus(result, {ErrorCodes::CommandNotSupportedOnView, errmsg});
}
@@ -123,7 +122,7 @@ public:
return false;
}
- result.append("ns", ns);
+ result.append("ns", nss.ns());
ValidateResults results;
Status status = collection->validate(txn, level, &results, &result);
diff --git a/src/mongo/db/exec/group.cpp b/src/mongo/db/exec/group.cpp
index 5ec8226c431..37ce67ad9fc 100644
--- a/src/mongo/db/exec/group.cpp
+++ b/src/mongo/db/exec/group.cpp
@@ -96,9 +96,8 @@ Status GroupStage::initGroupScripting() {
const std::string userToken =
AuthorizationSession::get(Client::getCurrent())->getAuthenticatedUserNamesToken();
- const NamespaceString nss(_request.ns);
_scope = getGlobalScriptEngine()->getPooledScope(
- getOpCtx(), nss.db().toString(), "group" + userToken);
+ getOpCtx(), _request.ns.db().toString(), "group" + userToken);
if (!_request.reduceScope.isEmpty()) {
_scope->init(&_request.reduceScope);
}
diff --git a/src/mongo/db/exec/group.h b/src/mongo/db/exec/group.h
index 7ec5efe647f..95b8a788a1d 100644
--- a/src/mongo/db/exec/group.h
+++ b/src/mongo/db/exec/group.h
@@ -30,6 +30,7 @@
#include "mongo/bson/simple_bsonobj_comparator.h"
#include "mongo/db/exec/plan_stage.h"
+#include "mongo/db/namespace_string.h"
#include "mongo/scripting/engine.h"
namespace mongo {
@@ -41,7 +42,7 @@ class Collection;
*/
struct GroupRequest {
// Namespace to operate on (e.g. "foo.bar").
- std::string ns;
+ NamespaceString ns;
// A predicate describing the set of documents to group.
BSONObj query;
diff --git a/src/mongo/db/namespace_string-inl.h b/src/mongo/db/namespace_string-inl.h
index 0ff45fca39e..1377d38ce41 100644
--- a/src/mongo/db/namespace_string-inl.h
+++ b/src/mongo/db/namespace_string-inl.h
@@ -122,14 +122,17 @@ inline NamespaceString::NamespaceString() : _ns(), _dotIndex(0) {}
inline NamespaceString::NamespaceString(StringData nsIn) {
_ns = nsIn.toString(); // copy to our buffer
_dotIndex = _ns.find('.');
+ uassert(ErrorCodes::InvalidNamespace,
+ "namespaces cannot have embedded null characters",
+ _ns.find('\0') == std::string::npos);
}
inline NamespaceString::NamespaceString(StringData dbName, StringData collectionName)
: _ns(dbName.size() + collectionName.size() + 1, '\0') {
- uassert(17235,
+ uassert(ErrorCodes::InvalidNamespace,
"'.' is an invalid character in a database name",
dbName.find('.') == std::string::npos);
- uassert(17246,
+ uassert(ErrorCodes::InvalidNamespace,
"Collection names cannot start with '.'",
collectionName.empty() || collectionName[0] != '.');
std::string::iterator it = std::copy(dbName.begin(), dbName.end(), _ns.begin());
@@ -139,7 +142,7 @@ inline NamespaceString::NamespaceString(StringData dbName, StringData collection
_dotIndex = dbName.size();
dassert(it == _ns.end());
dassert(_ns[_dotIndex] == '.');
- uassert(17295,
+ uassert(ErrorCodes::InvalidNamespace,
"namespaces cannot have embedded null characters",
_ns.find('\0') == std::string::npos);
}
diff --git a/src/mongo/db/ops/insert.cpp b/src/mongo/db/ops/insert.cpp
index db4a490392a..cfc9b10f35f 100644
--- a/src/mongo/db/ops/insert.cpp
+++ b/src/mongo/db/ops/insert.cpp
@@ -142,7 +142,7 @@ Status userAllowedWriteNS(const NamespaceString& ns) {
Status userAllowedWriteNS(StringData db, StringData coll) {
if (coll == "system.profile") {
- return Status(ErrorCodes::BadValue,
+ return Status(ErrorCodes::InvalidNamespace,
str::stream() << "cannot write to '" << db << ".system.profile'");
}
return userAllowedCreateNS(db, coll);
@@ -152,19 +152,19 @@ Status userAllowedCreateNS(StringData db, StringData coll) {
// validity checking
if (db.size() == 0)
- return Status(ErrorCodes::BadValue, "db cannot be blank");
+ return Status(ErrorCodes::InvalidNamespace, "db cannot be blank");
if (!NamespaceString::validDBName(db, NamespaceString::DollarInDbNameBehavior::Allow))
- return Status(ErrorCodes::BadValue, "invalid db name");
+ return Status(ErrorCodes::InvalidNamespace, "invalid db name");
if (coll.size() == 0)
- return Status(ErrorCodes::BadValue, "collection cannot be blank");
+ return Status(ErrorCodes::InvalidNamespace, "collection cannot be blank");
if (!NamespaceString::validCollectionName(coll))
- return Status(ErrorCodes::BadValue, "invalid collection name");
+ return Status(ErrorCodes::InvalidNamespace, "invalid collection name");
if (db.size() + 1 /* dot */ + coll.size() > NamespaceString::MaxNsCollectionLen)
- return Status(ErrorCodes::BadValue,
+ return Status(ErrorCodes::InvalidNamespace,
str::stream() << "fully qualified namespace " << db << '.' << coll
<< " is too long "
<< "(max is "
@@ -174,7 +174,7 @@ Status userAllowedCreateNS(StringData db, StringData coll) {
// check spceial areas
if (db == "system")
- return Status(ErrorCodes::BadValue, "cannot use 'system' database");
+ return Status(ErrorCodes::InvalidNamespace, "cannot use 'system' database");
if (coll.startsWith("system.")) {
@@ -202,7 +202,7 @@ Status userAllowedCreateNS(StringData db, StringData coll) {
if (coll == "system.replset")
return Status::OK();
}
- return Status(ErrorCodes::BadValue,
+ return Status(ErrorCodes::InvalidNamespace,
str::stream() << "cannot write to '" << db << "." << coll << "'");
}
diff --git a/src/mongo/db/ops/write_ops_parsers.cpp b/src/mongo/db/ops/write_ops_parsers.cpp
index 3e7281fcdca..e67de9c2e7a 100644
--- a/src/mongo/db/ops/write_ops_parsers.cpp
+++ b/src/mongo/db/ops/write_ops_parsers.cpp
@@ -90,6 +90,9 @@ void parseWriteCommand(StringData dbName,
// The key is the command name and the value is the collection name
checkBSONType(String, field);
op->ns = NamespaceString(dbName, field.valueStringData());
+ uassert(ErrorCodes::InvalidNamespace,
+ str::stream() << "Invalid namespace: " << op->ns.ns(),
+ op->ns.isValid());
firstElement = false;
continue;
}
diff --git a/src/mongo/db/query/get_executor.cpp b/src/mongo/db/query/get_executor.cpp
index 3cc981a03a8..3ba0d9bfcb6 100644
--- a/src/mongo/db/query/get_executor.cpp
+++ b/src/mongo/db/query/get_executor.cpp
@@ -999,7 +999,8 @@ StatusWith<unique_ptr<PlanExecutor>> getExecutorGroup(OperationContext* txn,
unique_ptr<PlanStage> root =
make_unique<GroupStage>(txn, request, ws.get(), new EOFStage(txn));
- return PlanExecutor::make(txn, std::move(ws), std::move(root), request.ns, yieldPolicy);
+ return PlanExecutor::make(
+ txn, std::move(ws), std::move(root), request.ns.ns(), yieldPolicy);
}
const NamespaceString nss(request.ns);
diff --git a/src/mongo/db/query/getmore_request.cpp b/src/mongo/db/query/getmore_request.cpp
index 6244d77ff56..48f68d338ee 100644
--- a/src/mongo/db/query/getmore_request.cpp
+++ b/src/mongo/db/query/getmore_request.cpp
@@ -70,7 +70,7 @@ GetMoreRequest::GetMoreRequest(NamespaceString namespaceString,
Status GetMoreRequest::isValid() const {
if (!nss.isValid()) {
- return Status(ErrorCodes::BadValue,
+ return Status(ErrorCodes::InvalidNamespace,
str::stream() << "Invalid namespace for getMore: " << nss.ns());
}
@@ -89,11 +89,11 @@ Status GetMoreRequest::isValid() const {
}
// static
-std::string GetMoreRequest::parseNs(const std::string& dbname, const BSONObj& cmdObj) {
+NamespaceString GetMoreRequest::parseNs(const std::string& dbname, const BSONObj& cmdObj) {
BSONElement collElt = cmdObj["collection"];
const std::string coll = (collElt.type() == BSONType::String) ? collElt.String() : "";
- return str::stream() << dbname << "." << coll;
+ return NamespaceString(dbname, coll);
}
// static
@@ -103,7 +103,7 @@ StatusWith<GetMoreRequest> GetMoreRequest::parseFromBSON(const std::string& dbna
// Required fields.
boost::optional<CursorId> cursorid;
- boost::optional<std::string> fullns;
+ boost::optional<NamespaceString> nss;
// Optional fields.
boost::optional<long long> batchSize;
@@ -127,7 +127,7 @@ StatusWith<GetMoreRequest> GetMoreRequest::parseFromBSON(const std::string& dbna
<< cmdObj};
}
- fullns = parseNs(dbname, cmdObj);
+ nss = parseNs(dbname, cmdObj);
} else if (str::equals(fieldName, kBatchSizeField)) {
if (!el.isNumber()) {
return {ErrorCodes::TypeMismatch,
@@ -171,17 +171,13 @@ StatusWith<GetMoreRequest> GetMoreRequest::parseFromBSON(const std::string& dbna
str::stream() << "Field 'getMore' missing in: " << cmdObj};
}
- if (!fullns) {
+ if (!nss) {
return {ErrorCodes::FailedToParse,
str::stream() << "Field 'collection' missing in: " << cmdObj};
}
- GetMoreRequest request(NamespaceString(*fullns),
- *cursorid,
- batchSize,
- awaitDataTimeout,
- term,
- lastKnownCommittedOpTime);
+ GetMoreRequest request(
+ std::move(*nss), *cursorid, batchSize, awaitDataTimeout, term, lastKnownCommittedOpTime);
Status validStatus = request.isValid();
if (!validStatus.isOK()) {
return validStatus;
diff --git a/src/mongo/db/query/getmore_request.h b/src/mongo/db/query/getmore_request.h
index 16455bfb055..8fa2d0fc5dd 100644
--- a/src/mongo/db/query/getmore_request.h
+++ b/src/mongo/db/query/getmore_request.h
@@ -69,7 +69,7 @@ struct GetMoreRequest {
*/
BSONObj toBSON() const;
- static std::string parseNs(const std::string& dbname, const BSONObj& cmdObj);
+ static NamespaceString parseNs(const std::string& dbname, const BSONObj& cmdObj);
const NamespaceString nss;
const CursorId cursorid;
diff --git a/src/mongo/db/repl/oplog.cpp b/src/mongo/db/repl/oplog.cpp
index a9c29c2791d..8c58b6d383e 100644
--- a/src/mongo/db/repl/oplog.cpp
+++ b/src/mongo/db/repl/oplog.cpp
@@ -639,9 +639,17 @@ std::map<std::string, ApplyOpMetadata> opsMap = {
{ErrorCodes::NamespaceNotFound, ErrorCodes::IndexNotFound}}},
{"renameCollection",
{[](OperationContext* txn, const char* ns, BSONObj& cmd) -> Status {
+ const auto sourceNsElt = cmd.firstElement();
+ const auto targetNsElt = cmd["to"];
+ uassert(ErrorCodes::TypeMismatch,
+ "'renameCollection' must be of type String",
+ sourceNsElt.type() == BSONType::String);
+ uassert(ErrorCodes::TypeMismatch,
+ "'to' must be of type String",
+ targetNsElt.type() == BSONType::String);
return renameCollection(txn,
- NamespaceString(cmd.firstElement().valuestrsafe()),
- NamespaceString(cmd["to"].valuestrsafe()),
+ NamespaceString(sourceNsElt.valueStringData()),
+ NamespaceString(targetNsElt.valueStringData()),
cmd["dropTarget"].trueValue(),
cmd["stayTemp"].trueValue());
},
@@ -686,6 +694,9 @@ Status applyOperation_inlock(OperationContext* txn,
if (fieldO.isABSONObj())
o = fieldO.embeddedObject();
+ uassert(ErrorCodes::InvalidNamespace,
+ "'ns' must be of type String",
+ fieldNs.type() == BSONType::String);
const StringData ns = fieldNs.valueStringData();
BSONObj o2;
@@ -1003,7 +1014,10 @@ Status applyCommand_inlock(OperationContext* txn,
BSONObj o = fieldO.embeddedObject();
- const NamespaceString nss(fieldNs.valuestrsafe());
+ uassert(ErrorCodes::InvalidNamespace,
+ "'ns' must be of type String",
+ fieldNs.type() == BSONType::String);
+ const NamespaceString nss(fieldNs.valueStringData());
if (!nss.isValid()) {
return {ErrorCodes::InvalidNamespace, "invalid ns: " + std::string(nss.ns())};
}
diff --git a/src/mongo/db/s/cleanup_orphaned_cmd.cpp b/src/mongo/db/s/cleanup_orphaned_cmd.cpp
index a9b361027bb..038ecbfbdeb 100644
--- a/src/mongo/db/s/cleanup_orphaned_cmd.cpp
+++ b/src/mongo/db/s/cleanup_orphaned_cmd.cpp
@@ -214,15 +214,10 @@ public:
return false;
}
- if (ns == "") {
- errmsg = "no collection name specified";
- return false;
- }
-
- if (!NamespaceString(ns).isValid()) {
- errmsg = "invalid namespace";
- return false;
- }
+ const NamespaceString nss(ns);
+ uassert(ErrorCodes::InvalidNamespace,
+ str::stream() << "Invalid namespace: " << nss.ns(),
+ nss.isValid());
BSONObj startingFromKey;
if (!FieldParser::extract(cmdObj, startingFromKeyField, &startingFromKey, &errmsg)) {
@@ -243,7 +238,7 @@ public:
}
ChunkVersion shardVersion;
- Status status = shardingState->refreshMetadataNow(txn, NamespaceString(ns), &shardVersion);
+ Status status = shardingState->refreshMetadataNow(txn, nss, &shardVersion);
if (!status.isOK()) {
if (status.code() == ErrorCodes::RemoteChangeDetected) {
warning() << "Shard version in transition detected while refreshing "
@@ -255,8 +250,8 @@ public:
}
BSONObj stoppedAtKey;
- CleanupResult cleanupResult = cleanupOrphanedData(
- txn, NamespaceString(ns), startingFromKey, writeConcern, &stoppedAtKey, &errmsg);
+ CleanupResult cleanupResult =
+ cleanupOrphanedData(txn, nss, startingFromKey, writeConcern, &stoppedAtKey, &errmsg);
if (cleanupResult == CleanupResult_Error) {
return false;
diff --git a/src/mongo/s/commands/cluster_enable_sharding_cmd.cpp b/src/mongo/s/commands/cluster_enable_sharding_cmd.cpp
index e6352a95fa1..1254fca23b5 100644
--- a/src/mongo/s/commands/cluster_enable_sharding_cmd.cpp
+++ b/src/mongo/s/commands/cluster_enable_sharding_cmd.cpp
@@ -95,10 +95,10 @@ public:
BSONObjBuilder& result) {
const std::string dbname = parseNs("", cmdObj);
- if (dbname.empty() || !nsIsDbOnly(dbname)) {
- errmsg = "invalid db name specified: " + dbname;
- return false;
- }
+ uassert(
+ ErrorCodes::InvalidNamespace,
+ str::stream() << "invalid db name specified: " << dbname,
+ NamespaceString::validDBName(dbname, NamespaceString::DollarInDbNameBehavior::Allow));
if (dbname == "admin" || dbname == "config" || dbname == "local") {
errmsg = "can't shard " + dbname + " database";
diff --git a/src/mongo/s/commands/cluster_find_cmd.cpp b/src/mongo/s/commands/cluster_find_cmd.cpp
index 9ef1eab35fe..52edfe5e20e 100644
--- a/src/mongo/s/commands/cluster_find_cmd.cpp
+++ b/src/mongo/s/commands/cluster_find_cmd.cpp
@@ -96,7 +96,7 @@ public:
Status checkAuthForCommand(Client* client,
const std::string& dbname,
const BSONObj& cmdObj) final {
- NamespaceString nss(parseNs(dbname, cmdObj));
+ const NamespaceString nss(parseNs(dbname, cmdObj));
auto hasTerm = cmdObj.hasField(kTermField);
return AuthorizationSession::get(client)->checkAuthForFind(nss, hasTerm);
}
@@ -107,13 +107,7 @@ public:
ExplainCommon::Verbosity verbosity,
const rpc::ServerSelectionMetadata& serverSelectionMetadata,
BSONObjBuilder* out) const final {
- const string fullns = parseNs(dbname, cmdObj);
- const NamespaceString nss(fullns);
- if (!nss.isValid()) {
- return {ErrorCodes::InvalidNamespace,
- str::stream() << "Invalid collection name: " << nss.ns()};
- }
-
+ const NamespaceString nss(parseNsCollectionRequired(dbname, cmdObj));
// Parse the command BSON to a QueryRequest.
bool isExplain = true;
auto qr = QueryRequest::makeFromFindCommand(std::move(nss), cmdObj, isExplain);
@@ -160,12 +154,7 @@ public:
// We count find command as a query op.
globalOpCounters.gotQuery();
- const NamespaceString nss(parseNs(dbname, cmdObj));
- if (!nss.isValid()) {
- return appendCommandStatus(result,
- {ErrorCodes::InvalidNamespace,
- str::stream() << "Invalid collection name: " << nss.ns()});
- }
+ const NamespaceString nss(parseNsCollectionRequired(dbname, cmdObj));
const bool isExplain = false;
auto qr = QueryRequest::makeFromFindCommand(nss, cmdObj, isExplain);
diff --git a/src/mongo/s/commands/cluster_move_chunk_cmd.cpp b/src/mongo/s/commands/cluster_move_chunk_cmd.cpp
index 4404d9663a0..81a258f4051 100644
--- a/src/mongo/s/commands/cluster_move_chunk_cmd.cpp
+++ b/src/mongo/s/commands/cluster_move_chunk_cmd.cpp
@@ -109,7 +109,11 @@ public:
auto scopedCM = uassertStatusOK(ScopedChunkManager::refreshAndGet(txn, nss));
- const string toString = cmdObj["to"].valuestrsafe();
+ const auto toElt = cmdObj["to"];
+ uassert(ErrorCodes::TypeMismatch,
+ "'to' must be of type String",
+ toElt.type() == BSONType::String);
+ const std::string toString = toElt.str();
if (!toString.size()) {
errmsg = "you have to specify where you want to move the chunk";
return false;
diff --git a/src/mongo/s/commands/cluster_move_primary_cmd.cpp b/src/mongo/s/commands/cluster_move_primary_cmd.cpp
index 63d94120343..b908137ceda 100644
--- a/src/mongo/s/commands/cluster_move_primary_cmd.cpp
+++ b/src/mongo/s/commands/cluster_move_primary_cmd.cpp
@@ -93,7 +93,11 @@ public:
}
virtual std::string parseNs(const std::string& dbname, const BSONObj& cmdObj) const {
- return cmdObj.firstElement().str();
+ const auto nsElt = cmdObj.firstElement();
+ uassert(ErrorCodes::InvalidNamespace,
+ "'movePrimary' must be of type String",
+ nsElt.type() == BSONType::String);
+ return nsElt.str();
}
virtual bool run(OperationContext* txn,
@@ -104,10 +108,10 @@ public:
BSONObjBuilder& result) {
const string dbname = parseNs("", cmdObj);
- if (dbname.empty() || !nsIsDbOnly(dbname)) {
- errmsg = "invalid db name specified: " + dbname;
- return false;
- }
+ uassert(
+ ErrorCodes::InvalidNamespace,
+ str::stream() << "invalid db name specified: " << dbname,
+ NamespaceString::validDBName(dbname, NamespaceString::DollarInDbNameBehavior::Allow));
if (dbname == "admin" || dbname == "config" || dbname == "local") {
errmsg = "can't move primary for " + dbname + " database";
@@ -124,7 +128,11 @@ public:
shared_ptr<DBConfig> config = status.getValue();
- const string to = cmdObj["to"].valuestrsafe();
+ const auto toElt = cmdObj["to"];
+ uassert(ErrorCodes::TypeMismatch,
+ "'to' must be of type String",
+ toElt.type() == BSONType::String);
+ const std::string to = toElt.str();
if (!to.size()) {
errmsg = "you have to specify where you want to move it";
return false;
diff --git a/src/mongo/s/commands/cluster_pipeline_cmd.cpp b/src/mongo/s/commands/cluster_pipeline_cmd.cpp
index 11564077cb0..46ab48b39a2 100644
--- a/src/mongo/s/commands/cluster_pipeline_cmd.cpp
+++ b/src/mongo/s/commands/cluster_pipeline_cmd.cpp
@@ -68,7 +68,7 @@ public:
Status checkAuthForCommand(Client* client,
const std::string& dbname,
const BSONObj& cmdObj) final {
- NamespaceString nss(parseNs(dbname, cmdObj));
+ const NamespaceString nss(parseNsCollectionRequired(dbname, cmdObj));
return AuthorizationSession::get(client)->checkAuthForAggregate(nss, cmdObj);
}
@@ -78,8 +78,7 @@ public:
int options,
std::string& errmsg,
BSONObjBuilder& result) {
- const std::string fullns = parseNs(dbname, cmdObj);
- const NamespaceString nss(fullns);
+ const NamespaceString nss(parseNsCollectionRequired(dbname, cmdObj));
ClusterAggregate::Namespaces nsStruct;
nsStruct.requestedNss = nss;
diff --git a/src/mongo/s/commands/cluster_remove_shard_cmd.cpp b/src/mongo/s/commands/cluster_remove_shard_cmd.cpp
index 42acb26fd11..6aeee9bd7e7 100644
--- a/src/mongo/s/commands/cluster_remove_shard_cmd.cpp
+++ b/src/mongo/s/commands/cluster_remove_shard_cmd.cpp
@@ -85,7 +85,11 @@ public:
int options,
std::string& errmsg,
BSONObjBuilder& result) {
- const string target = cmdObj.firstElement().valuestrsafe();
+ uassert(ErrorCodes::TypeMismatch,
+ str::stream() << "Field '" << cmdObj.firstElement().fieldName()
+ << "' must be of type String",
+ cmdObj.firstElement().type() == BSONType::String);
+ const string target = cmdObj.firstElement().str();
const auto shardStatus = grid.shardRegistry()->getShard(txn, ShardId(target));
if (!shardStatus.isOK()) {
diff --git a/src/mongo/s/commands/cluster_write_cmd.cpp b/src/mongo/s/commands/cluster_write_cmd.cpp
index c7784629993..4751322a0d9 100644
--- a/src/mongo/s/commands/cluster_write_cmd.cpp
+++ b/src/mongo/s/commands/cluster_write_cmd.cpp
@@ -150,7 +150,6 @@ public:
// Disable the last error object for the duration of the write
LastError::Disabled disableLastError(cmdLastError);
- // TODO: if we do namespace parsing, push this to the type
if (!request.parseBSON(dbname, cmdObj, &errmsg) || !request.isValid(&errmsg)) {
// Batch parse failure
response.setOk(false);
diff --git a/src/mongo/s/commands/commands_public.cpp b/src/mongo/s/commands/commands_public.cpp
index 4c2506e19ab..74e94ffc96e 100644
--- a/src/mongo/s/commands/commands_public.cpp
+++ b/src/mongo/s/commands/commands_public.cpp
@@ -234,14 +234,14 @@ public:
const string& dbName,
BSONObj& cmdObj,
vector<ShardId>& shardIds) {
- const string fullns = dbName + '.' + cmdObj.firstElement().valuestrsafe();
+ const NamespaceString nss(parseNsCollectionRequired(dbName, cmdObj));
auto status = Grid::get(txn)->catalogCache()->getDatabase(txn, dbName);
uassertStatusOK(status.getStatus());
shared_ptr<DBConfig> conf = status.getValue();
- if (!conf->isShardingEnabled() || !conf->isSharded(fullns)) {
+ if (!conf->isShardingEnabled() || !conf->isSharded(nss.ns())) {
shardIds.push_back(conf->getPrimaryId());
} else {
Grid::get(txn)->shardRegistry()->getAllShardIds(&shardIds);
@@ -259,10 +259,10 @@ public:
int options,
string& errmsg,
BSONObjBuilder& result) {
- const string fullns = parseNs(dbName, cmdObj);
+ const NamespaceString nss(parseNs(dbName, cmdObj));
auto conf = uassertStatusOK(Grid::get(txn)->catalogCache()->getDatabase(txn, dbName));
- if (!conf->isSharded(fullns)) {
+ if (!conf->isSharded(nss.ns())) {
return passthrough(txn, conf.get(), cmdObj, options, result);
}
@@ -427,7 +427,7 @@ public:
virtual Status checkAuthForCommand(Client* client,
const std::string& dbname,
const BSONObj& cmdObj) {
- NamespaceString nss(parseNs(dbname, cmdObj));
+ const NamespaceString nss(parseNsCollectionRequired(dbname, cmdObj));
return AuthorizationSession::get(client)->checkAuthForCollMod(nss, cmdObj);
}
@@ -458,7 +458,7 @@ public:
int options,
string& errmsg,
BSONObjBuilder& output) {
- const NamespaceString nss = parseNsCollectionRequired(dbName, cmdObj);
+ const NamespaceString nss(parseNsCollectionRequired(dbName, cmdObj));
auto conf = uassertStatusOK(Grid::get(txn)->catalogCache()->getDatabase(txn, dbName));
if (!conf->isShardingEnabled() || !conf->isSharded(nss.ns())) {
@@ -514,7 +514,7 @@ public:
virtual Status checkAuthForCommand(Client* client,
const std::string& dbname,
const BSONObj& cmdObj) {
- NamespaceString nss(parseNs(dbname, cmdObj));
+ const NamespaceString nss(parseNsCollectionRequired(dbname, cmdObj));
return AuthorizationSession::get(client)->checkAuthForCreate(nss, cmdObj);
}
virtual bool supportsWriteConcern(const BSONObj& cmd) const override {
@@ -567,20 +567,20 @@ public:
return appendCommandStatus(result, status.getStatus());
}
- const NamespaceString fullns = parseNsCollectionRequired(dbName, cmdObj);
+ const NamespaceString nss(parseNsCollectionRequired(dbName, cmdObj));
- log() << "DROP: " << fullns;
+ log() << "DROP: " << nss.ns();
const auto& db = status.getValue();
- if (!db->isShardingEnabled() || !db->isSharded(fullns.ns())) {
+ if (!db->isShardingEnabled() || !db->isSharded(nss.ns())) {
log() << "\tdrop going to do passthrough";
return passthrough(txn, db.get(), cmdObj, result);
}
- uassertStatusOK(Grid::get(txn)->catalogClient(txn)->dropCollection(txn, fullns));
+ uassertStatusOK(Grid::get(txn)->catalogClient(txn)->dropCollection(txn, nss));
// Force a full reload next time the just dropped namespace is accessed
- db->invalidateNs(fullns.ns());
+ db->invalidateNs(nss.ns());
return true;
}
@@ -606,17 +606,34 @@ public:
int,
string& errmsg,
BSONObjBuilder& result) {
- const string fullnsFrom = cmdObj.firstElement().valuestrsafe();
- const string dbNameFrom = nsToDatabase(fullnsFrom);
+ const auto fullNsFromElt = cmdObj.firstElement();
+ uassert(ErrorCodes::InvalidNamespace,
+ "'renameCollection' must be of type String",
+ fullNsFromElt.type() == BSONType::String);
+ const NamespaceString fullnsFrom(fullNsFromElt.valueStringData());
+ uassert(ErrorCodes::InvalidNamespace,
+ str::stream() << "Invalid source namespace: " << fullnsFrom.ns(),
+ fullnsFrom.isValid());
+ const string dbNameFrom = fullnsFrom.db().toString();
+
auto confFrom =
uassertStatusOK(Grid::get(txn)->catalogCache()->getDatabase(txn, dbNameFrom));
- const string fullnsTo = cmdObj["to"].valuestrsafe();
- const string dbNameTo = nsToDatabase(fullnsTo);
+ const auto fullnsToElt = cmdObj["to"];
+ uassert(ErrorCodes::InvalidNamespace,
+ "'to' must be of type String",
+ fullnsToElt.type() == BSONType::String);
+ const NamespaceString fullnsTo(fullnsToElt.valueStringData());
+ uassert(ErrorCodes::InvalidNamespace,
+ str::stream() << "Invalid target namespace: " << fullnsTo.ns(),
+ fullnsTo.isValid());
+ const string dbNameTo = fullnsTo.db().toString();
auto confTo = uassertStatusOK(Grid::get(txn)->catalogCache()->getDatabase(txn, dbNameTo));
- uassert(13138, "You can't rename a sharded collection", !confFrom->isSharded(fullnsFrom));
- uassert(13139, "You can't rename to a sharded collection", !confTo->isSharded(fullnsTo));
+ uassert(
+ 13138, "You can't rename a sharded collection", !confFrom->isSharded(fullnsFrom.ns()));
+ uassert(
+ 13139, "You can't rename to a sharded collection", !confTo->isSharded(fullnsTo.ns()));
auto shardTo = confTo->getPrimaryId();
auto shardFrom = confFrom->getPrimaryId();
@@ -650,10 +667,13 @@ public:
int,
string& errmsg,
BSONObjBuilder& result) {
- const string todb = cmdObj.getStringField("todb");
- uassert(ErrorCodes::EmptyFieldName, "missing todb argument", !todb.empty());
+ const auto todbElt = cmdObj["todb"];
uassert(ErrorCodes::InvalidNamespace,
- "invalid todb argument",
+ "'todb' must be of type String",
+ todbElt.type() == BSONType::String);
+ const std::string todb = todbElt.str();
+ uassert(ErrorCodes::InvalidNamespace,
+ "Invalid todb argument",
NamespaceString::validDBName(todb, NamespaceString::DollarInDbNameBehavior::Allow));
auto scopedToDb = uassertStatusOK(ScopedShardDatabase::getOrCreate(txn, todb));
@@ -665,8 +685,15 @@ public:
if (!fromhost.empty()) {
return adminPassthrough(txn, scopedToDb.db(), cmdObj, result);
} else {
- const string fromdb = cmdObj.getStringField("fromdb");
- uassert(13399, "need a fromdb argument", !fromdb.empty());
+ const auto fromDbElt = cmdObj["fromdb"];
+ uassert(ErrorCodes::InvalidNamespace,
+ "'fromdb' must be of type String",
+ fromDbElt.type() == BSONType::String);
+ const std::string fromdb = fromDbElt.str();
+ uassert(ErrorCodes::InvalidNamespace,
+ "invalid fromdb argument",
+ NamespaceString::validDBName(fromdb,
+ NamespaceString::DollarInDbNameBehavior::Allow));
shared_ptr<DBConfig> confFrom =
uassertStatusOK(Grid::get(txn)->catalogCache()->getDatabase(txn, fromdb));
@@ -714,10 +741,10 @@ public:
int,
string& errmsg,
BSONObjBuilder& result) {
- const string fullns = parseNs(dbName, cmdObj);
+ const NamespaceString nss(parseNsCollectionRequired(dbName, cmdObj));
auto conf = uassertStatusOK(Grid::get(txn)->catalogCache()->getDatabase(txn, dbName));
- if (!conf->isShardingEnabled() || !conf->isSharded(fullns)) {
+ if (!conf->isShardingEnabled() || !conf->isSharded(nss.ns())) {
result.appendBool("sharded", false);
result.append("primary", conf->getPrimaryId().toString());
@@ -726,7 +753,7 @@ public:
result.appendBool("sharded", true);
- shared_ptr<ChunkManager> cm = conf->getChunkManager(txn, fullns);
+ shared_ptr<ChunkManager> cm = conf->getChunkManager(txn, nss.ns());
massert(12594, "how could chunk manager be null!", cm);
BSONObjBuilder shardStats;
@@ -840,7 +867,7 @@ public:
unscaledCollSize += shardAvgObjSize * shardObjCount;
}
- result.append("ns", fullns);
+ result.append("ns", nss.ns());
for (map<string, long long>::iterator i = counts.begin(); i != counts.end(); ++i)
result.appendNumber(i->first, i->second);
@@ -973,6 +1000,10 @@ public:
return true;
}
+ virtual std::string parseNs(const std::string& dbname, const BSONObj& cmdObj) const {
+ return parseNsCollectionRequired(dbname, cmdObj).ns();
+ }
+
} convertToCappedCmd;
class GroupCmd : public NotAllowedOnShardedCollectionCmd {
@@ -994,8 +1025,16 @@ public:
return true;
}
- virtual std::string parseNs(const std::string& dbName, const BSONObj& cmdObj) const {
- return dbName + "." + cmdObj.firstElement().embeddedObjectUserCheck()["ns"].valuestrsafe();
+ virtual std::string parseNs(const std::string& dbname, const BSONObj& cmdObj) const {
+ const auto nsElt = cmdObj.firstElement().embeddedObjectUserCheck()["ns"];
+ uassert(ErrorCodes::InvalidNamespace,
+ "'ns' must be of type String",
+ nsElt.type() == BSONType::String);
+ const NamespaceString nss(dbname, nsElt.valueStringData());
+ uassert(ErrorCodes::InvalidNamespace,
+ str::stream() << "Invalid namespace: " << nss.ns(),
+ nss.isValid());
+ return nss.ns();
}
Status explain(OperationContext* txn,
@@ -1141,15 +1180,15 @@ public:
int options,
string& errmsg,
BSONObjBuilder& result) {
- const string fullns = parseNs(dbName, cmdObj);
+ const NamespaceString nss(parseNsCollectionRequired(dbName, cmdObj));
auto status = Grid::get(txn)->catalogCache()->getDatabase(txn, dbName);
if (!status.isOK()) {
- return appendEmptyResultSet(result, status.getStatus(), fullns);
+ return appendEmptyResultSet(result, status.getStatus(), nss.ns());
}
shared_ptr<DBConfig> conf = status.getValue();
- if (!conf->isShardingEnabled() || !conf->isSharded(fullns)) {
+ if (!conf->isShardingEnabled() || !conf->isSharded(nss.ns())) {
if (passthrough(txn, conf.get(), cmdObj, options, result)) {
return true;
@@ -1191,13 +1230,13 @@ public:
return false;
}
- shared_ptr<ChunkManager> cm = conf->getChunkManager(txn, fullns);
+ shared_ptr<ChunkManager> cm = conf->getChunkManager(txn, nss.ns());
massert(10420, "how could chunk manager be null!", cm);
BSONObj query = getQuery(cmdObj);
auto queryCollation = getCollation(cmdObj);
if (!queryCollation.isOK()) {
- return appendEmptyResultSet(result, queryCollation.getStatus(), fullns);
+ return appendEmptyResultSet(result, queryCollation.getStatus(), nss.ns());
}
// Construct collator for deduping.
@@ -1206,7 +1245,7 @@ public:
auto statusWithCollator = CollatorFactoryInterface::get(txn->getServiceContext())
->makeFromBSON(queryCollation.getValue());
if (!statusWithCollator.isOK()) {
- return appendEmptyResultSet(result, statusWithCollator.getStatus(), fullns);
+ return appendEmptyResultSet(result, statusWithCollator.getStatus(), nss.ns());
}
collator = std::move(statusWithCollator.getValue());
}
@@ -1227,7 +1266,7 @@ public:
continue;
}
- ShardConnection conn(shardStatus.getValue()->getConnString(), fullns);
+ ShardConnection conn(shardStatus.getValue()->getConnString(), nss.ns());
BSONObj res;
bool ok = conn->runCommand(conf->name(), cmdObj, res, options);
conn.done();
@@ -1262,7 +1301,7 @@ public:
ExplainCommon::Verbosity verbosity,
const rpc::ServerSelectionMetadata& serverSelectionMetadata,
BSONObjBuilder* out) const {
- const NamespaceString nss = parseNsCollectionRequired(dbname, cmdObj);
+ const NamespaceString nss(parseNsCollectionRequired(dbname, cmdObj));
// Extract the targeting query.
BSONObj targetingQuery;
@@ -1349,7 +1388,13 @@ public:
}
virtual std::string parseNs(const std::string& dbname, const BSONObj& cmdObj) const {
- std::string collectionName = cmdObj.getStringField("root");
+ std::string collectionName;
+ if (const auto rootElt = cmdObj["root"]) {
+ uassert(ErrorCodes::InvalidNamespace,
+ "'root' must be of type String",
+ rootElt.type() == BSONType::String);
+ collectionName = rootElt.str();
+ }
if (collectionName.empty())
collectionName = "fs";
collectionName += ".chunks";
@@ -1372,14 +1417,14 @@ public:
int,
string& errmsg,
BSONObjBuilder& result) {
- const string fullns = parseNs(dbName, cmdObj);
+ const NamespaceString nss(parseNs(dbName, cmdObj));
auto conf = uassertStatusOK(Grid::get(txn)->catalogCache()->getDatabase(txn, dbName));
- if (!conf->isShardingEnabled() || !conf->isSharded(fullns)) {
+ if (!conf->isShardingEnabled() || !conf->isSharded(nss.ns())) {
return passthrough(txn, conf.get(), cmdObj, result);
}
- shared_ptr<ChunkManager> cm = conf->getChunkManager(txn, fullns);
+ shared_ptr<ChunkManager> cm = conf->getChunkManager(txn, nss.ns());
massert(13091, "how could chunk manager be null!", cm);
if (SimpleBSONObjComparator::kInstance.evaluate(cm->getShardKeyPattern().toBSON() ==
BSON("files_id" << 1))) {
@@ -1387,7 +1432,7 @@ public:
vector<Strategy::CommandResult> results;
Strategy::commandOp(
- txn, dbName, cmdObj, 0, fullns, finder, CollationSpec::kSimpleSpec, &results);
+ txn, dbName, cmdObj, 0, nss.ns(), finder, CollationSpec::kSimpleSpec, &results);
verify(results.size() == 1); // querying on shard key so should only talk to one shard
BSONObj res = results.begin()->result;
@@ -1424,7 +1469,7 @@ public:
dbName,
shardCmd,
0,
- fullns,
+ nss.ns(),
finder,
CollationSpec::kSimpleSpec,
&results);
@@ -1513,20 +1558,20 @@ public:
int options,
string& errmsg,
BSONObjBuilder& result) {
- const string fullns = parseNs(dbName, cmdObj);
+ const NamespaceString nss(parseNsCollectionRequired(dbName, cmdObj));
auto conf = uassertStatusOK(Grid::get(txn)->catalogCache()->getDatabase(txn, dbName));
- if (!conf->isShardingEnabled() || !conf->isSharded(fullns)) {
+ if (!conf->isShardingEnabled() || !conf->isSharded(nss.ns())) {
return passthrough(txn, conf.get(), cmdObj, options, result);
}
- shared_ptr<ChunkManager> cm = conf->getChunkManager(txn, fullns);
+ shared_ptr<ChunkManager> cm = conf->getChunkManager(txn, nss.ns());
massert(13500, "how could chunk manager be null!", cm);
BSONObj query = getQuery(cmdObj);
auto collation = getCollation(cmdObj);
if (!collation.isOK()) {
- return appendEmptyResultSet(result, collation.getStatus(), fullns);
+ return appendEmptyResultSet(result, collation.getStatus(), nss.ns());
}
set<ShardId> shardIds;
cm->getShardIdsForQuery(txn, query, collation.getValue(), &shardIds);
@@ -1588,7 +1633,7 @@ public:
// TODO: maybe shrink results if size() > limit
}
- result.append("ns", fullns);
+ result.append("ns", nss.ns());
result.append("near", nearStr);
int outCount = 0;
@@ -1758,7 +1803,8 @@ public:
// Check for the listIndexes ActionType on the database, or find on system.indexes for pre
// 3.0 systems.
- NamespaceString ns(parseNs(dbname, cmdObj));
+ const NamespaceString ns(parseNsCollectionRequired(dbname, cmdObj));
+
if (authzSession->isAuthorizedForActionsOnResource(ResourcePattern::forExactNamespace(ns),
ActionType::listIndexes) ||
authzSession->isAuthorizedForActionsOnResource(
diff --git a/src/mongo/s/write_ops/batched_delete_request.cpp b/src/mongo/s/write_ops/batched_delete_request.cpp
index c11717adbe8..1819423660b 100644
--- a/src/mongo/s/write_ops/batched_delete_request.cpp
+++ b/src/mongo/s/write_ops/batched_delete_request.cpp
@@ -114,6 +114,9 @@ bool BatchedDeleteRequest::parseBSON(StringData dbName, const BSONObj& source, s
if (fieldState == FieldParser::FIELD_INVALID)
return false;
_ns = NamespaceString(dbName, collNameTemp);
+ uassert(ErrorCodes::InvalidNamespace,
+ str::stream() << "Invalid namespace: " << _ns.ns(),
+ _ns.isValid());
_isNSSet = fieldState == FieldParser::FIELD_SET;
} else if (fieldName == deletes.name()) {
fieldState = FieldParser::extract(field, deletes, &_deletes, errMsg);
diff --git a/src/mongo/s/write_ops/batched_insert_request.cpp b/src/mongo/s/write_ops/batched_insert_request.cpp
index 6d17bfd5b1e..d4ee4c0527a 100644
--- a/src/mongo/s/write_ops/batched_insert_request.cpp
+++ b/src/mongo/s/write_ops/batched_insert_request.cpp
@@ -120,6 +120,9 @@ bool BatchedInsertRequest::parseBSON(StringData dbName, const BSONObj& source, s
if (fieldState == FieldParser::FIELD_INVALID)
return false;
_ns = NamespaceString(dbName, temp);
+ uassert(ErrorCodes::InvalidNamespace,
+ str::stream() << "Invalid namespace: " << _ns.ns(),
+ _ns.isValid());
_isNSSet = fieldState == FieldParser::FIELD_SET;
} else if (documents() == sourceEl.fieldName()) {
FieldParser::FieldState fieldState =
diff --git a/src/mongo/s/write_ops/batched_update_request.cpp b/src/mongo/s/write_ops/batched_update_request.cpp
index e35bd3678d1..3c7f4fe9664 100644
--- a/src/mongo/s/write_ops/batched_update_request.cpp
+++ b/src/mongo/s/write_ops/batched_update_request.cpp
@@ -122,6 +122,9 @@ bool BatchedUpdateRequest::parseBSON(StringData dbName, const BSONObj& source, s
if (fieldState == FieldParser::FIELD_INVALID)
return false;
_ns = NamespaceString(dbName, collNameTemp);
+ uassert(ErrorCodes::InvalidNamespace,
+ str::stream() << "Invalid namespace: " << _ns.ns(),
+ _ns.isValid());
_isNSSet = fieldState == FieldParser::FIELD_SET;
} else if (fieldName == updates.name()) {
fieldState = FieldParser::extract(elem, updates, &_updates, errMsg);