Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions packages/build/src/packaging/download-crypt-library.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,13 @@ export async function downloadCryptLibrary(
// Download mongodb for latest server version, including rapid releases
// (for the platforms that they exist for, i.e. for ppc64le/s390x only pick stable releases).
let versionSpec = '8.0.12'; // TODO(MONGOSH-2192): Switch back to 'continuous' and deal with affected platform support.
if (/ppc64|s390x/.test(opts.arch || process.arch)) {

// For 8.2.0-rc4, we use the equivalent crypt shared library version for testing.
if (process.env.MONGOSH_SERVER_TEST_VERSION === '8.2.0-rc4-enterprise') {
versionSpec = '8.2.0-rc4';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fwiw, I'd have a mild preference for testing this in a way that would be reflective of real user behavior, i.e. adding the ability to pass through a version of the crypt_shared library to download through this function, then downloading that file and using it for only the QE text search tests.

If not that, then I'd maybe spent a minute to think about whether there's an easy way to make sure that this branch here only is taken in tests, because setting this environment variable could be done quite easily on accident

} else if (/ppc64|s390x/.test(opts.arch || process.arch)) {
versionSpec = '8.0.12';
}
if ((opts.platform || process.platform) === 'darwin') {
} else if ((opts.platform || process.platform) === 'darwin') {
versionSpec = '8.0.5'; // TBD(MONGOSH-2192,SERVER-101020): Figure out at what point we use a later version.
}
const { downloadedBinDir: libdir, version } =
Expand Down
156 changes: 156 additions & 0 deletions packages/e2e-tests/test/e2e-fle.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -914,6 +914,162 @@ describe('FLE tests', function () {
});
});

context('8.2+', function () {
skipIfServerVersion(testServer, '< 8.2');

context(
'Queryable Encryption Prefix/Suffix/Substring Support',
function () {
// Substring prefix support is enterprise-only 8.2+
skipIfCommunityServer(testServer);

let shell: TestShell;
let uri: string;

const testCollection = 'qeSubstringTest';

before(async function () {
shell = this.startTestShell({
args: ['--nodb', `--cryptSharedLibPath=${cryptLibrary}`],
});
uri = JSON.stringify(await testServer.connectionString());
await shell.waitForPrompt();

// Shared setup for all substring search tests - create collection once
await shell.executeLine(`{
opts = {
keyVaultNamespace: '${dbname}.__keyVault',
kmsProviders: { local: { key: 'A'.repeat(128) } },
bypassQueryAnalysis: false
};

autoMongo = Mongo(${uri}, { ...opts });
autoMongo.getDB('${dbname}').test.drop();

keyId = autoMongo.getKeyVault().createKey('local');

substringOptions = {
strMinQueryLength: 2,
strMaxQueryLength: 10,
strMaxLength: 60,
};

autoMongo.getClientEncryption().createEncryptedCollection('${dbname}', '${testCollection}', {
provider: 'local',
createCollectionOptions: {
encryptedFields: {
fields: [{
keyId,
path: 'data',
bsonType: 'string',
queries: [{
queryType: 'substringPreview',
...substringOptions,
caseSensitive: false,
diacriticSensitive: false,
contention: 4
}]
}]
}
}
});

coll = autoMongo.getDB('${dbname}').${testCollection};

// Setup explicit encryption client
explicitMongo = Mongo(${uri}, { ...opts, bypassQueryAnalysis: true });
ce = explicitMongo.getClientEncryption();
ecoll = explicitMongo.getDB('${dbname}').${testCollection};

explicitOpts = {
algorithm: 'TextPreview',
contentionFactor: 4,
textOptions: { caseSensitive: false, diacriticSensitive: false, substring: substringOptions }
};
}`);
});

after(async function () {
await shell.executeLine(`ecoll.${testCollection}.drop()`);
});

afterEach(async function () {
await shell.executeLine(`ecoll.${testCollection}.deleteMany({})`);
});

it.skip('allows queryable encryption with prefix searches', async function () {
// Insert test data for prefix searches
await shell.executeLine(`{
coll.insertOne({ data: 'admin_user_123.txt' });
coll.insertOne({ data: 'admin_super_456.pdf' });
coll.insertOne({ data: 'user_regular_789.pdf' });
coll.insertOne({ data: 'guest_access_000.txt' });

// Add explicit encryption data
ecoll.insertOne({ data: ce.encrypt(keyId, 'admin_explicit_test.pdf', explicitOpts) });
}`);
const prefixResults = await shell.executeLine(
'coll.find({$expr: { $and: [{$encStrStartsWith: {prefix: "admin_", input: "$data"}}] }}, { __safeContent__: 0 }).toArray()'
);
expect(prefixResults).to.have.length(3);
expect(prefixResults).to.include('admin_user_123.txt');
expect(prefixResults).to.include('admin_super_456.pdf');
expect(prefixResults).to.include('admin_explicit_test.pdf');

const explicitPrefixResult = await shell.executeLine(`
ecoll.find({$expr: { $and: [{$encStrStartsWith: {prefix: "admin_", input: "$data"}}] }},
{ __safeContent__: 0 })
`);
expect(explicitPrefixResult).to.include('admin_user_123.txt');
expect(explicitPrefixResult).to.include('admin_super_456.pdf');
expect(explicitPrefixResult).to.include('admin_explicit_test.pdf');
});

it.skip('allows queryable encryption with suffix searches', async function () {
// Insert test data for suffix searches
await shell.executeLine(`{
coll.insertOne({ data: 'admin_user_123.txt' });
coll.insertOne({ data: 'admin_super_456.pdf' });
coll.insertOne({ data: 'user_regular_789.pdf' });
coll.insertOne({ data: 'guest_access_000.txt' });

// Add explicit encryption data
ecoll.insertOne({ data: ce.encrypt(keyId, 'admin_explicit_test.pdf', explicitOpts) });
}`);

const explicitSuffixResult = await shell.executeLine(`
ecoll.find({$expr: { $and: [{$encStrEndsWith: {suffix: ".pdf", input: "$data"}}] }}, { __safeContent__: 0 }).toArray()
`);
expect(explicitSuffixResult).to.include('admin_super_456.pdf');
expect(explicitSuffixResult).to.include('user_regular_789.pdf');
expect(explicitSuffixResult).to.include('admin_explicit_test.pdf');
});

it('allows queryable encryption with substring searches', async function () {
// Insert test data for substring searches
// Insert test data for prefix searches
Copy link
Preview

Copilot AI Sep 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment 'Insert test data for prefix searches' is incorrect for a substring search test. It should be 'Insert test data for substring searches'.

Suggested change
// Insert test data for prefix searches
// Insert test data for substring searches

Copilot uses AI. Check for mistakes.

await shell.executeLine(`{
coll.insertOne({ data: 'admin_user_123.txt' });
coll.insertOne({ data: 'admin_super_456.pdf' });
coll.insertOne({ data: 'user_regular_789.pdf' });
coll.insertOne({ data: 'guest_access_000.txt' });

// Add explicit encryption data
ecoll.insertOne({ data: ce.encrypt(keyId, 'explicit_user', explicitOpts) });
}`);

const testingSubstringResult = await shell.executeLine(
'ecoll.find({$expr: { $and: [{$encStrContains: {substring: "user", input: "$data"}}] }}, { __safeContent__: 0 }).toArray()'
);

expect(testingSubstringResult).to.include('user_regular_789.pdf');
expect(testingSubstringResult).to.include('admin_user_123.txt');
expect(testingSubstringResult).to.include('explicit_user');
});
}
);
});

context('pre-6.0', function () {
skipIfServerVersion(testServer, '>= 6.0'); // FLE2 available on 6.0+

Expand Down
Loading