SERVER-108185 Assert encryptedTokens is the correct length for text s… (#39240)

GitOrigin-RevId: b00a4e468eccb33b841278e192b9d823804db2de
This commit is contained in:
Gabriel Marks 2025-07-31 11:41:35 -04:00 committed by MongoDB Bot
parent 5cc52b5a90
commit df068ab24f
3 changed files with 95 additions and 2 deletions

View File

@ -459,6 +459,20 @@ public:
static constexpr std::size_t kCipherLengthESCAndLeafFlag = kCipherLengthESCOnly + 1;
static constexpr std::size_t kCipherLengthESCAndMsize = kCipherLengthESCOnly + 3;
public:
/**
* Assert that _encryptedTokens is of the expected length for text search.
*/
void assertIsValidForTextSearch() const {
uassert(ErrorCodes::BadValue,
fmt::format("Invalid length for EncryptedStateCollectionTokensV2 for text "
"search: Expected {}, got {}",
kCipherLengthESCAndMsize,
_encryptedTokens.size()),
_encryptedTokens.size() == kCipherLengthESCAndMsize);
}
private:
static void assertLength(std::size_t sz) {
uassert(ErrorCodes::BadValue,
fmt::format("Invalid length for EncryptedStateCollectionTokensV2, expected {}, "

View File

@ -927,17 +927,22 @@ void processFieldsForInsert(FLEQueryInterface* queryImpl,
}
} else if (payload.isTextSearchPayload()) {
const auto& tsts = payload.payload.getTextSearchTokenSets().get();
ecocDocuments.push_back(tsts.getExactTokenSet().getEncryptedTokens().generateDocument(
payload.fieldPathName));
auto exactSet = tsts.getExactTokenSet();
exactSet.getEncryptedTokens().assertIsValidForTextSearch();
ecocDocuments.push_back(
exactSet.getEncryptedTokens().generateDocument(payload.fieldPathName));
for (const auto& ts : tsts.getSubstringTokenSets()) {
ts.getEncryptedTokens().assertIsValidForTextSearch();
ecocDocuments.push_back(
ts.getEncryptedTokens().generateDocument(payload.fieldPathName));
}
for (const auto& ts : tsts.getSuffixTokenSets()) {
ts.getEncryptedTokens().assertIsValidForTextSearch();
ecocDocuments.push_back(
ts.getEncryptedTokens().generateDocument(payload.fieldPathName));
}
for (const auto& ts : tsts.getPrefixTokenSets()) {
ts.getEncryptedTokens().assertIsValidForTextSearch();
ecocDocuments.push_back(
ts.getEncryptedTokens().generateDocument(payload.fieldPathName));
}

View File

@ -2350,5 +2350,79 @@ TEST_F(QETextSearchCrudTest, BasicPrefixAndSuffixMultipleInserts) {
{"aaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaa"}});
}
// Test insert update payloads containing text search token sets ('b') with embedded encryptedTokens
// of invalid length in the exact/substring/prefix/suffix token sets are rejected.
TEST_F(QETextSearchCrudTest, InsertPayloadHasInvalidExactEncryptedTokensForTextSearch) {
addSchema({.type = QueryTypeEnum::SubstringPreview,
.lb = 2,
.ub = 10,
.mlen = 400,
.casef = false,
.diacf = false});
auto doc = BSON(kTestFieldName << "abcdef");
auto element = doc.firstElement();
auto buf = generatePlaceholder(element);
auto efc = getEFC();
BSONObjBuilder builder;
builder.append("_id", 1);
builder.append("counter", 1);
builder.append("plainText", "sample");
builder.append(transformElementForInsertUpdate(element, buf, efc).firstElement());
auto result = builder.obj();
auto serverPayload = EDCServerCollection::getEncryptedFieldInfo(result);
ASSERT_EQ(serverPayload.size(), 1);
auto& exactSet = serverPayload[0].payload.getTextSearchTokenSets().value().getExactTokenSet();
exactSet.setEncryptedTokens(
StateCollectionTokensV2(ESCDerivedFromDataTokenAndContentionFactorToken(
exactSet.getEscDerivedToken().asPrfBlock()),
boost::none,
boost::none /* msize */)
.encrypt({{}}));
ASSERT_THROWS_CODE_AND_WHAT(
processInsert(_queryImpl.get(), _edcNs, serverPayload, efc, 0, result, false),
DBException,
ErrorCodes::BadValue,
"Invalid length for EncryptedStateCollectionTokensV2 for text "
"search: Expected 51, got 48");
}
TEST_F(QETextSearchCrudTest, InsertPayloadHasInvalidSubstringEncryptedTokensForTextSearch) {
addSchema({.type = QueryTypeEnum::SubstringPreview,
.lb = 2,
.ub = 10,
.mlen = 400,
.casef = false,
.diacf = false});
auto doc = BSON(kTestFieldName << "abcdef");
auto element = doc.firstElement();
auto buf = generatePlaceholder(element);
auto efc = getEFC();
BSONObjBuilder builder;
builder.append("_id", 1);
builder.append("counter", 1);
builder.append("plainText", "sample");
builder.append(transformElementForInsertUpdate(element, buf, efc).firstElement());
auto result = builder.obj();
auto serverPayload = EDCServerCollection::getEncryptedFieldInfo(result);
ASSERT_EQ(serverPayload.size(), 1);
auto& ts =
serverPayload[0].payload.getTextSearchTokenSets().value().getSubstringTokenSets().back();
ts.setEncryptedTokens(StateCollectionTokensV2(ESCDerivedFromDataTokenAndContentionFactorToken(
ts.getEscDerivedToken().asPrfBlock()),
boost::none,
boost::none /* msize */)
.encrypt({{}}));
ASSERT_THROWS_CODE_AND_WHAT(
processInsert(_queryImpl.get(), _edcNs, serverPayload, efc, 0, result, false),
DBException,
ErrorCodes::BadValue,
"Invalid length for EncryptedStateCollectionTokensV2 for text "
"search: Expected 51, got 48");
}
} // namespace
} // namespace mongo