diff options
author | David Storch <david.storch@10gen.com> | 2016-04-21 11:21:24 -0400 |
---|---|---|
committer | David Storch <david.storch@10gen.com> | 2016-04-22 10:44:56 -0400 |
commit | a878b57a5f5e45fb68560e8a9eb2247cf1e0d4b8 (patch) | |
tree | 1ba4c4376055e9cc74f171ff722cfd99f71ca43c | |
parent | bb063b938570345f22a74fa75bbfb04ac0ea85de (diff) | |
download | mongo-a878b57a5f5e45fb68560e8a9eb2247cf1e0d4b8.tar.gz |
SERVER-7005 reject embedded null bytes in $regex
(cherry picked from commit 33471d4424dd81e5310b27867ecb3647c60cf7a4)
Conflicts:
jstests/core/regex.js
-rw-r--r-- | jstests/core/regex.js | 54 | ||||
-rw-r--r-- | src/mongo/db/matcher/expression_leaf.cpp | 10 | ||||
-rw-r--r-- | src/mongo/db/matcher/expression_leaf_test.cpp | 130 |
3 files changed, 88 insertions, 106 deletions
diff --git a/jstests/core/regex.js b/jstests/core/regex.js index f431d506ea6..d6982678f97 100644 --- a/jstests/core/regex.js +++ b/jstests/core/regex.js @@ -1,24 +1,40 @@ -t = db.jstests_regex; +(function() { + 'use strict'; -t.drop(); -t.save( { a: "bcd" } ); -assert.eq( 1, t.count( { a: /b/ } ) , "A" ); -assert.eq( 1, t.count( { a: /bc/ } ) , "B" ); -assert.eq( 1, t.count( { a: /bcd/ } ) , "C" ); -assert.eq( 0, t.count( { a: /bcde/ } ) , "D" ); + var t = db.jstests_regex; -t.drop(); -t.save( { a: { b: "cde" } } ); -assert.eq( 1, t.count( { 'a.b': /de/ } ) , "E" ); + t.drop(); + t.save({a: "bcd"}); + assert.eq(1, t.count({a: /b/}), "A"); + assert.eq(1, t.count({a: /bc/}), "B"); + assert.eq(1, t.count({a: /bcd/}), "C"); + assert.eq(0, t.count({a: /bcde/}), "D"); -t.drop(); -t.save( { a: { b: [ "cde" ] } } ); -assert.eq( 1, t.count( { 'a.b': /de/ } ) , "F" ); + t.drop(); + t.save({a: {b: "cde"}}); + assert.eq(1, t.count({'a.b': /de/}), "E"); -t.drop(); -t.save( { a: [ { b: "cde" } ] } ); -assert.eq( 1, t.count( { 'a.b': /de/ } ) , "G" ); + t.drop(); + t.save({a: {b: ["cde"]}}); + assert.eq(1, t.count({'a.b': /de/}), "F"); -t.drop(); -t.save( { a: [ { b: [ "cde" ] } ] } ); -assert.eq( 1, t.count( { 'a.b': /de/ } ) , "H" ); + t.drop(); + t.save({a: [{b: "cde"}]}); + assert.eq(1, t.count({'a.b': /de/}), "G"); + + t.drop(); + t.save({a: [{b: ["cde"]}]}); + assert.eq(1, t.count({'a.b': /de/}), "H"); + + // Disallow embedded null bytes when using $regex syntax. + t.drop(); + assert.throws(function() { + t.find({a: {$regex: "a\0b", $options: "i"}}).itcount(); + }); + assert.throws(function() { + t.find({a: {$regex: "ab", $options: "i\0"}}).itcount(); + }); + assert.throws(function() { + t.find({key: {$regex: 'abcd\0xyz'}}).explain(); + }); +})(); diff --git a/src/mongo/db/matcher/expression_leaf.cpp b/src/mongo/db/matcher/expression_leaf.cpp index 2316ef278b5..05edbdd094b 100644 --- a/src/mongo/db/matcher/expression_leaf.cpp +++ b/src/mongo/db/matcher/expression_leaf.cpp @@ -269,6 +269,16 @@ Status RegexMatchExpression::init(const StringData& path, return Status(ErrorCodes::BadValue, "Regular expression is too long"); } + if (regex.find('\0') != std::string::npos) { + return Status(ErrorCodes::BadValue, + "Regular expression cannot contain an embedded null byte"); + } + + if (options.find('\0') != std::string::npos) { + return Status(ErrorCodes::BadValue, + "Regular expression options string cannot contain an embedded null byte"); + } + _regex = regex.toString(); _flags = options.toString(); _re.reset(new pcrecpp::RE(_regex.c_str(), flags2options(_flags.c_str()))); diff --git a/src/mongo/db/matcher/expression_leaf_test.cpp b/src/mongo/db/matcher/expression_leaf_test.cpp index 10ae1e8bc10..cb7217c1363 100644 --- a/src/mongo/db/matcher/expression_leaf_test.cpp +++ b/src/mongo/db/matcher/expression_leaf_test.cpp @@ -854,57 +854,6 @@ TEST(ComparisonMatchExpression, ElemMatchKey) { ASSERT_EQUALS("1", details.elemMatchKey()); } -/** - TEST( GteOp, MatchesIndexKeyScalar ) { - BSONObj operand = BSON( "$gte" << 6 ); - GteOp gte; - ASSERT( gte.init( "a", operand[ "$gte" ] ).isOK() ); - IndexSpec indexSpec( BSON( "a" << 1 ) ); - ASSERT( MatchMatchExpression::PartialMatchResult_True == - gte.matchesIndexKey( BSON( "" << 6 ), indexSpec ) ); - ASSERT( MatchMatchExpression::PartialMatchResult_False == - gte.matchesIndexKey( BSON( "" << 5 ), indexSpec ) ); - ASSERT( MatchMatchExpression::PartialMatchResult_False == - gte.matchesIndexKey( BSON( "" << BSON_ARRAY( 7 ) ), indexSpec ) ); - } - - TEST( GteOp, MatchesIndexKeyMissing ) { - BSONObj operand = BSON( "$gte" << 6 ); - GteOp gte; - ASSERT( gte.init( "a", operand[ "$gte" ] ).isOK() ); - IndexSpec indexSpec( BSON( "b" << 1 ) ); - ASSERT( MatchMatchExpression::PartialMatchResult_Unknown == - gte.matchesIndexKey( BSON( "" << 6 ), indexSpec ) ); - ASSERT( MatchMatchExpression::PartialMatchResult_Unknown == - gte.matchesIndexKey( BSON( "" << 4 ), indexSpec ) ); - ASSERT( MatchMatchExpression::PartialMatchResult_Unknown == - gte.matchesIndexKey( BSON( "" << BSON_ARRAY( 8 << 6 ) ), indexSpec ) ); - } - - TEST( GteOp, MatchesIndexKeyArray ) { - BSONObj operand = BSON( "$gte" << BSON_ARRAY( 4 << 5 ) ); - GteOp gte; - ASSERT( gte.init( "a", operand[ "$gte" ] ).isOK() ); - IndexSpec indexSpec( BSON( "a" << 1 ) ); - ASSERT( MatchMatchExpression::PartialMatchResult_Unknown == - gte.matchesIndexKey( BSON( "" << 6 ), indexSpec ) ); - } - - TEST( GteOp, MatchesIndexKeyArrayValue ) { - BSONObj operand = BSON( "$gte" << 6 ); - GteOp gte; - ASSERT( gte.init( "a", operand[ "$gte" ] ).isOK() ); - IndexSpec indexSpec( BSON( "loc" << "mockarrayvalue" << "a" << 1 ) ); - ASSERT( MatchMatchExpression::PartialMatchResult_True == - gte.matchesIndexKey( BSON( "" << "dummygeohash" << "" << 6 ), indexSpec ) ); - ASSERT( MatchMatchExpression::PartialMatchResult_False == - gte.matchesIndexKey( BSON( "" << "dummygeohash" << "" << 3 ), indexSpec ) ); - ASSERT( MatchMatchExpression::PartialMatchResult_True == - gte.matchesIndexKey( BSON( "" << "dummygeohash" << - "" << BSON_ARRAY( 8 << 6 << 4 ) ), indexSpec ) ); - } -*/ - TEST(RegexMatchExpression, MatchesElementExact) { BSONObj match = BSON("a" << "b"); @@ -1140,44 +1089,51 @@ TEST(RegexMatchExpression, Equality1) { ASSERT(!r1.equivalent(&r4)); } -/** - TEST( RegexMatchExpression, MatchesIndexKeyScalar ) { - RegexMatchExpression regex; - ASSERT( regex.init( "a", "xyz", "" ).isOK() ); - IndexSpec indexSpec( BSON( "a" << 1 ) ); - ASSERT( MatchMatchExpression::PartialMatchResult_True == - regex.matchesIndexKey( BSON( "" << "z xyz" ), indexSpec ) ); - ASSERT( MatchMatchExpression::PartialMatchResult_False == - regex.matchesIndexKey( BSON( "" << "xy" ), indexSpec ) ); - ASSERT( MatchMatchExpression::PartialMatchResult_False == - regex.matchesIndexKey( BSON( "" << BSON_ARRAY( "xyz" ) ), indexSpec ) ); - } +TEST(RegexMatchExpression, RegexCannotContainEmbeddedNullByte) { + RegexMatchExpression regex; + { + StringData embeddedNull("a\0b", StringData::LiteralTag()); + ASSERT_NOT_OK(regex.init("path", embeddedNull, "")); + } - TEST( RegexMatchExpression, MatchesIndexKeyMissing ) { - RegexMatchExpression regex; - ASSERT( regex.init( "a", "xyz", "" ).isOK() ); - IndexSpec indexSpec( BSON( "b" << 1 ) ); - ASSERT( MatchMatchExpression::PartialMatchResult_Unknown == - regex.matchesIndexKey( BSON( "" << "z xyz" ), indexSpec ) ); - ASSERT( MatchMatchExpression::PartialMatchResult_Unknown == - regex.matchesIndexKey( BSON( "" << "xy" ), indexSpec ) ); - ASSERT( MatchMatchExpression::PartialMatchResult_Unknown == - regex.matchesIndexKey( BSON( "" << BSON_ARRAY( 8 << "xyz" ) ), indexSpec ) ); - } + { + StringData singleNullByte("\0", StringData::LiteralTag()); + ASSERT_NOT_OK(regex.init("path", singleNullByte, "")); + } - TEST( RegexMatchExpression, MatchesIndexKeyArrayValue ) { - RegexMatchExpression regex; - ASSERT( regex.init( "a", "xyz", "" ).isOK() ); - IndexSpec indexSpec( BSON( "loc" << "mockarrayvalue" << "a" << 1 ) ); - ASSERT( MatchMatchExpression::PartialMatchResult_True == - regex.matchesIndexKey( BSON( "" << "dummygeohash" << "" << "xyz" ), indexSpec ) ); - ASSERT( MatchMatchExpression::PartialMatchResult_False == - regex.matchesIndexKey( BSON( "" << "dummygeohash" << "" << "z" ), indexSpec ) ); - ASSERT( MatchMatchExpression::PartialMatchResult_True == - regex.matchesIndexKey( BSON( "" << "dummygeohash" << - "" << BSON_ARRAY( "r" << 6 << "xyz" ) ), indexSpec ) ); - } -*/ + { + StringData leadingNullByte("\0bbbb", StringData::LiteralTag()); + ASSERT_NOT_OK(regex.init("path", leadingNullByte, "")); + } + + { + StringData trailingNullByte("bbbb\0", StringData::LiteralTag()); + ASSERT_NOT_OK(regex.init("path", trailingNullByte, "")); + } +} + +TEST(RegexMatchExpression, RegexOptionsStringCannotContainEmbeddedNullByte) { + RegexMatchExpression regex; + { + StringData embeddedNull("a\0b", StringData::LiteralTag()); + ASSERT_NOT_OK(regex.init("path", "pattern", embeddedNull)); + } + + { + StringData singleNullByte("\0", StringData::LiteralTag()); + ASSERT_NOT_OK(regex.init("path", "pattern", singleNullByte)); + } + + { + StringData leadingNullByte("\0bbbb", StringData::LiteralTag()); + ASSERT_NOT_OK(regex.init("path", "pattern", leadingNullByte)); + } + + { + StringData trailingNullByte("bbbb\0", StringData::LiteralTag()); + ASSERT_NOT_OK(regex.init("path", "pattern", trailingNullByte)); + } +} TEST(ModMatchExpression, MatchesElement) { BSONObj match = BSON("a" << 1); |