From 957f73433f2cd97e756b307343798ae08c4bc8cb Mon Sep 17 00:00:00 2001 From: David Storch Date: Thu, 21 Apr 2016 11:21:24 -0400 Subject: SERVER-7005 reject embedded null bytes in $regex (cherry picked from commit 33471d4424dd81e5310b27867ecb3647c60cf7a4) --- jstests/core/regex.js | 51 ++++++---- src/mongo/db/matcher/expression_leaf.cpp | 10 ++ src/mongo/db/matcher/expression_leaf_test.cpp | 130 +++++++++----------------- 3 files changed, 85 insertions(+), 106 deletions(-) diff --git a/jstests/core/regex.js b/jstests/core/regex.js index 235c1936885..983ead6d733 100644 --- a/jstests/core/regex.js +++ b/jstests/core/regex.js @@ -1,24 +1,37 @@ -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(); + }); +})(); diff --git a/src/mongo/db/matcher/expression_leaf.cpp b/src/mongo/db/matcher/expression_leaf.cpp index e271af3b2bb..cfcd3d03591 100644 --- a/src/mongo/db/matcher/expression_leaf.cpp +++ b/src/mongo/db/matcher/expression_leaf.cpp @@ -271,6 +271,16 @@ Status RegexMatchExpression::init(StringData path, StringData regex, StringData 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 b2946f5f546..d4d01eb7325 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); -- cgit v1.2.1