diff options
author | Alya Berciu <alyacarina@gmail.com> | 2021-06-03 14:33:55 +0100 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2021-06-09 10:45:11 +0000 |
commit | a11aba2bf9f18a8e73a23099519ad45a2939d238 (patch) | |
tree | 64b6c9346414a781a85fe0f110e2773d26f26d40 | |
parent | cd5391608ae87d378431c42f3eee1a2fa51b5bc9 (diff) | |
download | mongo-a11aba2bf9f18a8e73a23099519ad45a2939d238.tar.gz |
SERVER-57382 Add _id $-prefix validation to storage validation
-rw-r--r-- | jstests/core/field_name_validation.js | 9 | ||||
-rw-r--r-- | src/mongo/db/ops/insert.cpp | 31 | ||||
-rw-r--r-- | src/mongo/db/update/storage_validation.cpp | 35 | ||||
-rw-r--r-- | src/mongo/db/update/storage_validation.h | 6 |
4 files changed, 44 insertions, 37 deletions
diff --git a/jstests/core/field_name_validation.js b/jstests/core/field_name_validation.js index 936da4147de..1dc9219f74e 100644 --- a/jstests/core/field_name_validation.js +++ b/jstests/core/field_name_validation.js @@ -109,6 +109,15 @@ assert.eq(1, coll.find({a: {b: 2}}).itcount()); assert.commandWorked(coll.update({"a.b": 2}, {"a.b": 3})); assert.eq(0, coll.find({"a.b": 3}).itcount()); +// Upserting _id fields containing $-prefixed fields is not allowed. +assert.writeErrorWithCode(coll.update({"a.b": 1}, {_id: {$invalid: 1}}, {upsert: true}), + ErrorCodes.DollarPrefixedFieldName); +assert.writeErrorWithCode(coll.update({"a.b": 1}, {$set: {_id: {$invalid: 1}}}, {upsert: true}), + ErrorCodes.DollarPrefixedFieldName); +assert.writeErrorWithCode( + coll.update({"a.b": 1}, {$setOnInsert: {_id: {$invalid: 1}}}, {upsert: true}), + ErrorCodes.DollarPrefixedFieldName); + if (isDotsAndDollarsEnabled) { // Replacement-style updates can contain nested $-prefixed fields. assert.commandWorked(coll.update({"a.b": 1}, {a: {$c: 1}})); diff --git a/src/mongo/db/ops/insert.cpp b/src/mongo/db/ops/insert.cpp index 1a7d6027c0a..c508f95d0c6 100644 --- a/src/mongo/db/ops/insert.cpp +++ b/src/mongo/db/ops/insert.cpp @@ -38,6 +38,7 @@ #include "mongo/db/query/dbref.h" #include "mongo/db/query/query_feature_flags_gen.h" #include "mongo/db/repl/replication_coordinator.h" +#include "mongo/db/update/storage_validation.h" #include "mongo/db/vector_clock_mutable.h" #include "mongo/db/views/durable_view_catalog.h" #include "mongo/util/fail_point.h" @@ -134,33 +135,9 @@ StatusWith<BSONObj> fixDocumentForInsert(OperationContext* opCtx, // also, disallow undefined and arrays // Make sure _id isn't duplicated (SERVER-19361). if (fieldName == "_id") { - if (e.type() == RegEx) { - return StatusWith<BSONObj>(ErrorCodes::BadValue, - "can't use a regex for _id"); - } - if (e.type() == Undefined) { - return StatusWith<BSONObj>(ErrorCodes::BadValue, - "can't use a undefined for _id"); - } - if (e.type() == Array) { - return StatusWith<BSONObj>(ErrorCodes::BadValue, - "can't use an array for _id"); - } - if (e.type() == Object) { - BSONObj o = e.Obj(); - Status s = o.storageValidEmbedded(); - if (!s.isOK()) { - if (feature_flags::gFeatureFlagDotsAndDollars.isEnabledAndIgnoreFCV() && - s.code() == ErrorCodes::DollarPrefixedFieldName) { - return StatusWith<BSONObj>( - s.code(), - str::stream() - << "_id fields may not contain '$'-prefixed fields: " - << s.reason()); - } else { - return StatusWith<BSONObj>(s); - } - } + auto status = storage_validation::storageValidIdField(e); + if (!status.isOK()) { + return StatusWith<BSONObj>(status); } if (hadId) { return StatusWith<BSONObj>( diff --git a/src/mongo/db/update/storage_validation.cpp b/src/mongo/db/update/storage_validation.cpp index cac577f414c..a314e8c3065 100644 --- a/src/mongo/db/update/storage_validation.cpp +++ b/src/mongo/db/update/storage_validation.cpp @@ -125,6 +125,30 @@ void validateDollarPrefixElement(mutablebson::ConstElement elem) { } } // namespace +Status storageValidIdField(const mongo::BSONElement& element) { + switch (element.type()) { + case BSONType::RegEx: + case BSONType::Array: + case BSONType::Undefined: + return Status(ErrorCodes::InvalidIdField, + str::stream() + << "The '_id' value cannot be of type " << typeName(element.type())); + case BSONType::Object: { + auto status = element.Obj().storageValidEmbedded(); + if (!status.isOK() && status.code() == ErrorCodes::DollarPrefixedFieldName && + feature_flags::gFeatureFlagDotsAndDollars.isEnabledAndIgnoreFCV()) { + return Status(status.code(), + str::stream() << "_id fields may not contain '$'-prefixed fields: " + << status.reason()); + } + return status; + } + default: + break; + } + return Status::OK(); +} + void storageValid(const mutablebson::Document& doc, const bool allowTopLevelDollarPrefixes, const bool shouldValidate, @@ -132,16 +156,7 @@ void storageValid(const mutablebson::Document& doc, auto currElem = doc.root().leftChild(); while (currElem.ok()) { if (currElem.getFieldName() == idFieldName && shouldValidate) { - switch (currElem.getType()) { - case BSONType::RegEx: - case BSONType::Array: - case BSONType::Undefined: - uasserted(ErrorCodes::InvalidIdField, - str::stream() << "The '_id' value cannot be of type " - << typeName(currElem.getType())); - default: - break; - } + uassertStatusOK(storageValidIdField(currElem.getValue())); } // Validate this child element. diff --git a/src/mongo/db/update/storage_validation.h b/src/mongo/db/update/storage_validation.h index 9e3ae50ee84..951f5fe7e95 100644 --- a/src/mongo/db/update/storage_validation.h +++ b/src/mongo/db/update/storage_validation.h @@ -36,6 +36,12 @@ namespace mongo { namespace storage_validation { /** + * Returns a status to indicate whether or not 'element' is a valid _id field for storage in a + * collection. + */ +Status storageValidIdField(const mongo::BSONElement& element); + +/** * Validates that the MutableBSON document 'doc' is acceptable for storage in a collection. The * check is performed recursively on subdocuments. Uasserts if the validation fails or if the depth * exceeds the maximum allowable depth. |