Skip to content

Commit a10d2c9

Browse files
fix(NODE-7548): SCRAM authentication fails on non-Node runtimes (#4932)
1 parent 5c986b1 commit a10d2c9

22 files changed

Lines changed: 185 additions & 57 deletions

File tree

etc/bundle-driver.mjs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,14 @@ await esbuild.build({
1717
entryPoints: [path.join(rootDir, 'test/mongodb_all.ts')],
1818
bundle: true,
1919
outfile: outputBundleFile,
20-
platform: 'node',
20+
platform: 'browser',
2121
format: 'cjs',
22-
target: 'node20',
22+
target: 'chrome112',
2323
external: [
2424
'@aws-sdk/credential-providers',
2525
'@mongodb-js/saslprep',
2626
'@mongodb-js/zstd',
2727
'@napi-rs/snappy*',
28-
'bson',
2928
'gcp-metadata',
3029
'kerberos',
3130
'mongodb-client-encryption',

src/bson.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ export const readInt32LE = (buffer: Uint8Array, offset: number): number => {
5555
return NumberUtils.getInt32LE(buffer, offset);
5656
};
5757

58+
// readUint8, reads a single unsigned byte from buffer at given offset
59+
export const readUint8 = (buffer: Uint8Array, offset: number): number => {
60+
validateBufferInputs(buffer, offset, 1);
61+
return buffer[offset];
62+
};
63+
5864
export const setUint32LE = (destination: Uint8Array, offset: number, value: number): 4 => {
5965
destination[offset] = value;
6066
value >>>= 8;

src/cmap/auth/scram.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -166,11 +166,10 @@ async function continueScramConversation(
166166
const clientKey = await HMAC(cryptoMethod, saltedPassword, 'Client Key');
167167
const serverKey = await HMAC(cryptoMethod, saltedPassword, 'Server Key');
168168
const storedKey = await H(cryptoMethod, clientKey);
169-
const authMessage = [
170-
clientFirstMessageBare(username, nonce),
171-
payload.toString('utf8'),
172-
withoutProof
173-
].join(',');
169+
const firstMessageBytes = clientFirstMessageBare(username, nonce);
170+
const firstMessage = ByteUtils.toUTF8(firstMessageBytes, 0, firstMessageBytes.length, false);
171+
const payloadString = ByteUtils.toUTF8(payload.buffer, 0, payload.position, false);
172+
const authMessage = [firstMessage, payloadString, withoutProof].join(',');
174173

175174
const clientSignature = await HMAC(cryptoMethod, storedKey, authMessage);
176175
const clientProof = `p=${xor(clientKey, clientSignature)}`;
@@ -205,7 +204,7 @@ async function continueScramConversation(
205204
}
206205

207206
function parsePayload(payload: Binary) {
208-
const payloadStr = payload.toString('utf8');
207+
const payloadStr = ByteUtils.toUTF8(payload.buffer, 0, payload.position, false);
209208
const dict: Document = {};
210209
const parts = payloadStr.split(',');
211210
for (let i = 0; i < parts.length; i++) {

test/integration/change-streams/change_stream.test.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,12 @@ import {
2121
type MongoClient,
2222
MongoServerError,
2323
ReadPreference,
24-
type ResumeToken
24+
type ResumeToken,
25+
runNodelessTests
2526
} from '../../mongodb';
2627
import * as mock from '../../tools/mongodb-mock/index';
2728
import { TestBuilder, UnifiedTestSuiteBuilder } from '../../tools/unified_suite_builder';
28-
import { type FailCommandFailPoint, sleep } from '../../tools/utils';
29+
import { ensureTypeByName, type FailCommandFailPoint, sleep } from '../../tools/utils';
2930
import { delay, filterForCommands } from '../shared';
3031

3132
const initIteratorMode = async (cs: ChangeStream) => {
@@ -1826,7 +1827,11 @@ describe('Change Streams', function () {
18261827
expect(result).to.exist;
18271828

18281829
const change = await willBeChange;
1829-
expect(change).to.have.nested.property('fullDocument.a').that.is.instanceOf(Long);
1830+
if (runNodelessTests) {
1831+
ensureTypeByName(change?.fullDocument?.a, '_Long');
1832+
} else {
1833+
expect(change).to.have.nested.property('fullDocument.a').that.is.instanceOf(Long);
1834+
}
18301835
}
18311836
});
18321837
});

test/integration/client-side-encryption/driver.test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -725,14 +725,16 @@ describe('CSOT', function () {
725725
'test.test': {
726726
bsonType: 'object',
727727
encryptMetadata: {
728-
keyId: [new UUID(dataKey)]
728+
// NODE-7581: should be able to pass dataKey as-is, as UUID, w/o needing toHexString
729+
keyId: [new UUID(dataKey.toHexString(true))]
729730
},
730731
properties: {
731732
a: {
732733
encrypt: {
733734
bsonType: 'int',
734735
algorithm: 'AEAD_AES_256_CBC_HMAC_SHA_512-Random',
735-
keyId: [new UUID(dataKey)]
736+
// NODE-7581: should be able to pass dataKey as-is, as UUID, w/o needing toHexString
737+
keyId: [new UUID(dataKey.toHexString(true))]
736738
}
737739
}
738740
}

test/integration/crud/insert.test.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as Script from 'node:vm';
22

3+
import { ByteUtils } from 'bson';
34
import { expect } from 'chai';
45
import * as process from 'process';
56
import { satisfies } from 'semver';
@@ -180,7 +181,7 @@ describe('crud - insert', function () {
180181
test.equal(date.toString(), doc.date.toString());
181182
test.equal(date.getTime(), doc.date.getTime());
182183
test.equal(motherOfAllDocuments.oid.toHexString(), doc.oid.toHexString());
183-
test.equal(motherOfAllDocuments.binary.toString('hex'), doc.binary.value().toString('hex'));
184+
test.equal(motherOfAllDocuments.binary.toString('hex'), ByteUtils.toHex(doc.binary.value()));
184185

185186
test.equal(motherOfAllDocuments.int, doc.int);
186187
test.equal(motherOfAllDocuments.long, doc.long);
@@ -281,8 +282,8 @@ describe('crud - insert', function () {
281282
test.equal(date.getTime(), doc.date.getTime());
282283
test.equal(motherOfAllDocuments.oid.toHexString(), doc.oid.toHexString());
283284
test.equal(
284-
motherOfAllDocuments.binary.value().toString('hex'),
285-
doc.binary.value().toString('hex')
285+
ByteUtils.toHex(motherOfAllDocuments.binary.value()),
286+
ByteUtils.toHex(doc.binary.value())
286287
);
287288

288289
test.equal(motherOfAllDocuments.int, doc.int);

test/integration/node-specific/bson-options/promote_buffers.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { expect } from 'chai';
22

3+
import { runOnlyInNodeMetadata } from '../../../tools/utils';
34
import { setupDatabase } from '../../shared';
45

5-
describe('Promote Buffers', function () {
6+
describe('Promote Buffers', runOnlyInNodeMetadata, function () {
67
before(function () {
78
return setupDatabase(this.configuration);
89
});

test/integration/node-specific/bson-options/raw.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { expect } from 'chai';
22

33
import { type Collection, type MongoClient, ObjectId } from '../../../mongodb';
4+
import { runOnlyInNodeMetadata } from '../../../tools/utils';
45

5-
describe('raw bson support', () => {
6+
describe('raw bson support', runOnlyInNodeMetadata, () => {
67
describe('raw', () => {
78
describe('option inheritance', () => {
89
// define client and option for tests to use

test/integration/node-specific/bson-options/utf8_validation.test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ import {
1111
MongoServerError,
1212
OpMsgResponse
1313
} from '../../../mongodb';
14-
describe('class MongoDBResponse', () => {
14+
import { runOnlyInNodeMetadata } from '../../../tools/utils';
15+
16+
describe('class MongoDBResponse', runOnlyInNodeMetadata, () => {
1517
let client;
1618

1719
afterEach(async () => {
@@ -72,7 +74,7 @@ describe('class MongoDBResponse', () => {
7274
);
7375
});
7476

75-
describe('parsing of utf8-invalid documents with cursors', function () {
77+
describe('parsing of utf8-invalid documents with cursors', runOnlyInNodeMetadata, function () {
7678
let client: MongoClient;
7779
let collection: Collection;
7880
const compressionPredicate = () =>

test/mongodb_bundled.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ export const {
109109
decompress,
110110
decorateWithExplain,
111111
DEFAULT_ALLOWED_HOSTS,
112+
DEFAULT_KEEP_ALIVE_INITIAL_DELAY_MS,
112113
DEFAULT_MAX_DOCUMENT_LENGTH,
113114
DEFAULT_PK_FACTORY,
114115
DeleteManyOperation,
@@ -176,6 +177,7 @@ export const {
176177
ListIndexesOperation,
177178
Long,
178179
makeClientMetadata,
180+
makeSocket,
179181
MAX_SUPPORTED_SERVER_VERSION,
180182
MAX_SUPPORTED_WIRE_VERSION,
181183
MaxKey,
@@ -421,6 +423,7 @@ export type {
421423
import type {
422424
AbstractCursor as _AbstractCursor,
423425
AuthMechanism as _AuthMechanism,
426+
Binary as _Binary,
424427
ChangeStream as _ChangeStream,
425428
ClientEncryption as _ClientEncryption,
426429
ClientSession as _ClientSession,
@@ -482,6 +485,7 @@ import type {
482485
// To re-sort: Highlight the list and use VSCode's "Sort Lines Ascending" command
483486
export type AbstractCursor = _AbstractCursor;
484487
export type AuthMechanism = _AuthMechanism;
488+
export type Binary = _Binary;
485489
export type ChangeStream = _ChangeStream;
486490
export type ClientEncryption = _ClientEncryption;
487491
export type ClientSession = _ClientSession;

0 commit comments

Comments
 (0)