From bc0368fef7103edc55591a5ba1d025f68d39a2b6 Mon Sep 17 00:00:00 2001 From: Grace Chong Date: Tue, 23 Nov 2021 13:55:28 -0500 Subject: [PATCH 01/10] feat(NODE-3627) Enable flexible bson validation for server error key containing invalid utf8 --- src/bson.ts | 1 + src/cmap/commands.ts | 5 +- src/connection_string.ts | 4 ++ test/types/bson.test-d.ts | 3 +- test/unit/commands.test.ts | 138 +++++++++++++++++++++++++++++++++++++ 5 files changed, 149 insertions(+), 2 deletions(-) create mode 100644 test/unit/commands.test.ts diff --git a/src/bson.ts b/src/bson.ts index 3b445109daf..48705674c62 100644 --- a/src/bson.ts +++ b/src/bson.ts @@ -54,6 +54,7 @@ export interface BSONSerializeOptions > { /** Return BSON filled buffers from operations */ raw?: boolean; + validation?: { utf8: boolean | Record | Record }; } export function pluckBSONSerializeOptions(options: BSONSerializeOptions): BSONSerializeOptions { diff --git a/src/cmap/commands.ts b/src/cmap/commands.ts index 3fa3a714112..3609e7867f8 100644 --- a/src/cmap/commands.ts +++ b/src/cmap/commands.ts @@ -469,6 +469,7 @@ export interface MessageHeader { export interface OpResponseOptions extends BSONSerializeOptions { raw?: boolean; documentsReturnedIn?: string | null; + validation?: { utf8: boolean | Record | Record }; } /** @internal */ @@ -837,13 +838,15 @@ export class BinMsg { const promoteValues = options.promoteValues ?? this.opts.promoteValues; const promoteBuffers = options.promoteBuffers ?? this.opts.promoteBuffers; const bsonRegExp = options.bsonRegExp ?? this.opts.bsonRegExp; + const validation = options.validation ?? { utf8: { writeError: false } }; // Set up the options const _options: BSONSerializeOptions = { promoteLongs, promoteValues, promoteBuffers, - bsonRegExp + bsonRegExp, + validation }; while (this.index < this.data.length) { diff --git a/src/connection_string.ts b/src/connection_string.ts index ed897000dca..a0921e6de1e 100644 --- a/src/connection_string.ts +++ b/src/connection_string.ts @@ -864,6 +864,10 @@ export const OPTIONS = { default: false, type: 'boolean' }, + validation: { + default: { utf8: { writeErrors: false } }, + type: 'record' + }, readConcern: { transform({ values: [value], options }) { if (value instanceof ReadConcern || isRecord(value, ['level'] as const)) { diff --git a/test/types/bson.test-d.ts b/test/types/bson.test-d.ts index a98699c15e2..f33a784ff10 100644 --- a/test/types/bson.test-d.ts +++ b/test/types/bson.test-d.ts @@ -21,7 +21,8 @@ type PermittedBSONOptionKeys = | 'promoteValues' | 'bsonRegExp' | 'fieldsAsRaw' - | 'raw'; + | 'raw' + | 'validation'; const keys = null as unknown as PermittedBSONOptionKeys; // creates an explicit allow list assertion diff --git a/test/unit/commands.test.ts b/test/unit/commands.test.ts new file mode 100644 index 00000000000..9a18201785c --- /dev/null +++ b/test/unit/commands.test.ts @@ -0,0 +1,138 @@ +import { expect } from 'chai'; +import { BinMsg, MessageHeader } from '../../src/cmap/commands'; +import { BSONError } from 'bson'; +import * as BSON from '../../src/bson'; + +const msgHeader: MessageHeader = { + length: 735, + requestId: 14704565, + responseTo: 4, + opCode: 2013 +}; + +// when top-level key writeErrors contains an error message that has invalid utf8 +const msgBodyInvalidUtf8WriteErrors = Buffer.from( + '0000000000ca020000106e00000000000477726974654572726f727300a50200000330009d02000010696e646578000000000010636f646500f82a0000036b65795061747465726e000f0000001074657874000100000000036b657956616c756500610100000274657874005201000064e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e298830000026572726d736700f1000000453131303030206475706c6963617465206b6579206572726f7220636f6c6c656374696f6e3a20626967646174612e7465737420696e6465783a20746578745f3120647570206b65793a207b20746578743a202264e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e2982e2e2e22207d000000016f6b00000000000000f03f00', + 'hex' +); + +const badutf8inputtodeserialize = Buffer.from( + 'ca020000106e00000000000477726974654572726f727300a50200000330009d02000010696e646578000000000010636f646500f82a0000036b65795061747465726e000f0000001074657874000100000000036b657956616c756500610100000274657874005201000064e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e298830000026572726d736700f1000000453131303030206475706c6963617465206b6579206572726f7220636f6c6c656374696f6e3a20626967646174612e7465737420696e6465783a20746578745f3120647570206b65793a207b20746578743a202264e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e2982e2e2e22207d000000016f6b00000000000000f03f00', + 'hex' +); + +const invalidUtf8InWriteErrorsJSON = { + n: 0, + writeErrors: [ + { + index: 0, + code: 11000, + keyPattern: { + text: 1 + }, + keyValue: { + text: 'd☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃' + }, + errmsg: + 'E11000 duplicate key error collection: bigdata.test index: text_1 dup key: { text: "d☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃�..." }' + } + ], + ok: 1 +}; + +// when another top-level key besides writeErrors has invalid utf8 +const nKeyWithInvalidUtf8 = Buffer.from( + 'cc020000026e0005000000f09f98ff000477726974654572726f727300a60200000330009e02000010696e646578000000000010636f646500f82a0000036b65795061747465726e000f0000001074657874000100000000036b657956616c756500610100000274657874005201000064e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e298830000026572726d736700f2000000453131303030206475706c6963617465206b6579206572726f7220636f6c6c656374696f6e3a20626967646174612e7465737420696e6465783a20746578745f3120647570206b65793a207b20746578743a202264e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883efbfbd2e2e2e22207d000000106f6b000100000000', + 'hex' +); + +const msgBodyNKeyWithInvalidUtf8 = Buffer.from( + '0000000000cc020000026e0005000000f09f98ff000477726974654572726f727300a60200000330009e02000010696e646578000000000010636f646500f82a0000036b65795061747465726e000f0000001074657874000100000000036b657956616c756500610100000274657874005201000064e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e298830000026572726d736700f2000000453131303030206475706c6963617465206b6579206572726f7220636f6c6c656374696f6e3a20626967646174612e7465737420696e6465783a20746578745f3120647570206b65793a207b20746578743a202264e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883efbfbd2e2e2e22207d000000106f6b000100000000', + 'hex' +); + +const invalidUtf8InOtherKeyJSON = { + n: '��', + writeErrors: [ + { + index: 0, + code: 11000, + keyPattern: { + text: 1 + }, + keyValue: { + text: 'd☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃' + }, + errmsg: + 'E11000 duplicate key error collection: bigdata.test index: text_1 dup key: { text: "d☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃☃�..." }' + } + ], + ok: 1 +}; + +describe.only('BinMsg', function () { + it('should not throw invalid utf8 error when validation disabled for writeErrors', function () { + const binMsgValidWriteError = new BinMsg( + Buffer.alloc(0), + msgHeader, + msgBodyInvalidUtf8WriteErrors, + { + validation: { utf8: { writeErrors: false } } + } + ); + expect(() => + binMsgValidWriteError.parse({ validation: { utf8: { writeErrors: false } } }) + ).to.not.throw(); + expect( + BSON.deserialize(badutf8inputtodeserialize, { validation: { utf8: { writeErrors: false } } }) + ).to.deep.equals(invalidUtf8InWriteErrorsJSON); + }); + + it('should throw invalid utf8 error when validation not specified or enabled for writeErrors', function () { + const binMsgValidWriteError = new BinMsg( + Buffer.alloc(0), + msgHeader, + msgBodyInvalidUtf8WriteErrors + ); + const options = { + bsonRegExp: false, + promoteBuffers: false, + promoteLongs: true, + promoteValues: true + }; + expect(() => binMsgValidWriteError.parse(options)).to.throw( + BSONError, + 'Invalid UTF-8 string in BSON document' + ); + expect(() => + binMsgValidWriteError.parse({ validation: { utf8: { writeErrors: true } } }) + ).to.throw(BSONError, 'Invalid UTF-8 string in BSON document'); + }); + + it('should not throw when another key has invalid utf8 and writeErrors is validated', function () { + const binMsgAnotherKeyWithInvalidUtf8 = new BinMsg( + Buffer.alloc(0), + msgHeader, + msgBodyNKeyWithInvalidUtf8 + ); + expect(() => + binMsgAnotherKeyWithInvalidUtf8.parse({ validation: { utf8: { writeErrors: true } } }) + ).to.not.throw(); + expect( + BSON.deserialize(nKeyWithInvalidUtf8, { + validation: { utf8: { writeErrors: true } } + }) + ).to.deep.equals(invalidUtf8InOtherKeyJSON); + }); + + it('should throw error when another key has invalid utf8 and writeErrors is not validated', function () { + const binMsgAnotherKeyWithInvalidUtf8 = new BinMsg( + Buffer.alloc(0), + msgHeader, + msgBodyNKeyWithInvalidUtf8 + ); + expect(() => + binMsgAnotherKeyWithInvalidUtf8.parse({ validation: { utf8: { writeErrors: false } } }) + ).to.throw(BSONError, 'Invalid UTF-8 string in BSON document'); + }); +}); From 4591622fd2fe4c0e45af13f030a7a336ef51f1e1 Mon Sep 17 00:00:00 2001 From: Grace Chong Date: Tue, 23 Nov 2021 14:06:27 -0500 Subject: [PATCH 02/10] chore: use bson 4.6.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1ac940b95d6..47067d70ca2 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "email": "dbx-node@mongodb.com" }, "dependencies": { - "bson": "^4.5.4", + "bson": "^4.6.0", "denque": "^2.0.1", "mongodb-connection-string-url": "^2.2.0" }, From 745bf664f49bfd10f04e367c24b8ba6d008c858c Mon Sep 17 00:00:00 2001 From: Grace Chong Date: Tue, 23 Nov 2021 14:31:55 -0500 Subject: [PATCH 03/10] refactor: fix misspelled key and add default validation setting and test, remove .only --- src/cmap/commands.ts | 2 +- test/unit/commands.test.ts | 28 +++++++++++++++------------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/cmap/commands.ts b/src/cmap/commands.ts index 3609e7867f8..9bfda2e11ea 100644 --- a/src/cmap/commands.ts +++ b/src/cmap/commands.ts @@ -838,7 +838,7 @@ export class BinMsg { const promoteValues = options.promoteValues ?? this.opts.promoteValues; const promoteBuffers = options.promoteBuffers ?? this.opts.promoteBuffers; const bsonRegExp = options.bsonRegExp ?? this.opts.bsonRegExp; - const validation = options.validation ?? { utf8: { writeError: false } }; + const validation = options.validation ?? { utf8: { writeErrors: false } }; // Set up the options const _options: BSONSerializeOptions = { diff --git a/test/unit/commands.test.ts b/test/unit/commands.test.ts index 9a18201785c..111b8d0a4a6 100644 --- a/test/unit/commands.test.ts +++ b/test/unit/commands.test.ts @@ -70,26 +70,23 @@ const invalidUtf8InOtherKeyJSON = { ok: 1 }; -describe.only('BinMsg', function () { +describe('BinMsg', function () { it('should not throw invalid utf8 error when validation disabled for writeErrors', function () { - const binMsgValidWriteError = new BinMsg( + const binMsgInvalidUtf8ErrorMsg = new BinMsg( Buffer.alloc(0), msgHeader, - msgBodyInvalidUtf8WriteErrors, - { - validation: { utf8: { writeErrors: false } } - } + msgBodyInvalidUtf8WriteErrors ); expect(() => - binMsgValidWriteError.parse({ validation: { utf8: { writeErrors: false } } }) + binMsgInvalidUtf8ErrorMsg.parse({ validation: { utf8: { writeErrors: false } } }) ).to.not.throw(); expect( BSON.deserialize(badutf8inputtodeserialize, { validation: { utf8: { writeErrors: false } } }) ).to.deep.equals(invalidUtf8InWriteErrorsJSON); }); - it('should throw invalid utf8 error when validation not specified or enabled for writeErrors', function () { - const binMsgValidWriteError = new BinMsg( + it('should by default disable validation for writeErrors if no validation specified', function () { + const binMsgInvalidUtf8ErrorMsg = new BinMsg( Buffer.alloc(0), msgHeader, msgBodyInvalidUtf8WriteErrors @@ -100,12 +97,17 @@ describe.only('BinMsg', function () { promoteLongs: true, promoteValues: true }; - expect(() => binMsgValidWriteError.parse(options)).to.throw( - BSONError, - 'Invalid UTF-8 string in BSON document' + expect(() => binMsgInvalidUtf8ErrorMsg.parse(options)).to.not.throw(); + }); + + it('should throw invalid utf8 error when validation enabled for writeErrors', function () { + const binMsgInvalidUtf8ErrorMsg = new BinMsg( + Buffer.alloc(0), + msgHeader, + msgBodyInvalidUtf8WriteErrors ); expect(() => - binMsgValidWriteError.parse({ validation: { utf8: { writeErrors: true } } }) + binMsgInvalidUtf8ErrorMsg.parse({ validation: { utf8: { writeErrors: true } } }) ).to.throw(BSONError, 'Invalid UTF-8 string in BSON document'); }); From 05de41c0d66519488f55ebdef1769b23be4d993e Mon Sep 17 00:00:00 2001 From: Grace Chong Date: Tue, 23 Nov 2021 14:47:15 -0500 Subject: [PATCH 04/10] test: add test to make sure only writeErrors key has validation settings toggled --- src/cmap/commands.ts | 3 +++ test/unit/commands.test.ts | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/src/cmap/commands.ts b/src/cmap/commands.ts index 9bfda2e11ea..35f2a523181 100644 --- a/src/cmap/commands.ts +++ b/src/cmap/commands.ts @@ -838,6 +838,9 @@ export class BinMsg { const promoteValues = options.promoteValues ?? this.opts.promoteValues; const promoteBuffers = options.promoteBuffers ?? this.opts.promoteBuffers; const bsonRegExp = options.bsonRegExp ?? this.opts.bsonRegExp; + if (options.validation && Object.keys(options.validation.utf8)[0] !== 'writeErrors') { + throw new Error('Can only toggle validation settings for writeErrors key'); + } const validation = options.validation ?? { utf8: { writeErrors: false } }; // Set up the options diff --git a/test/unit/commands.test.ts b/test/unit/commands.test.ts index 111b8d0a4a6..4dbbef9b7bc 100644 --- a/test/unit/commands.test.ts +++ b/test/unit/commands.test.ts @@ -71,6 +71,18 @@ const invalidUtf8InOtherKeyJSON = { }; describe('BinMsg', function () { + it('should throw error if trying to toggle validation settings for keys other than writeErrors', function () { + const binMsgInvalidUtf8ErrorMsg = new BinMsg( + Buffer.alloc(0), + msgHeader, + msgBodyInvalidUtf8WriteErrors + ); + expect(() => binMsgInvalidUtf8ErrorMsg.parse({ validation: { utf8: { n: false } } })).to.throw( + Error, + 'Can only toggle validation settings for writeErrors key' + ); + }); + it('should not throw invalid utf8 error when validation disabled for writeErrors', function () { const binMsgInvalidUtf8ErrorMsg = new BinMsg( Buffer.alloc(0), From 77c83f628f7275b77fdde9da5e2a83e3e7d8b23b Mon Sep 17 00:00:00 2001 From: Grace Chong Date: Tue, 23 Nov 2021 16:10:01 -0500 Subject: [PATCH 05/10] test: limit it statements to one assertion and use contexts for organization --- src/cmap/commands.ts | 4 +- test/unit/commands.test.ts | 99 +++++++++++++++++++------------------- 2 files changed, 52 insertions(+), 51 deletions(-) diff --git a/src/cmap/commands.ts b/src/cmap/commands.ts index 35f2a523181..f34b448a00e 100644 --- a/src/cmap/commands.ts +++ b/src/cmap/commands.ts @@ -839,7 +839,9 @@ export class BinMsg { const promoteBuffers = options.promoteBuffers ?? this.opts.promoteBuffers; const bsonRegExp = options.bsonRegExp ?? this.opts.bsonRegExp; if (options.validation && Object.keys(options.validation.utf8)[0] !== 'writeErrors') { - throw new Error('Can only toggle validation settings for writeErrors key'); + throw new MongoInvalidArgumentError( + 'Can only toggle validation settings for writeErrors key' + ); } const validation = options.validation ?? { utf8: { writeErrors: false } }; diff --git a/test/unit/commands.test.ts b/test/unit/commands.test.ts index 4dbbef9b7bc..86aa41fe853 100644 --- a/test/unit/commands.test.ts +++ b/test/unit/commands.test.ts @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { BinMsg, MessageHeader } from '../../src/cmap/commands'; +import { BinMsg, MessageHeader, OpResponseOptions } from '../../src/cmap/commands'; import { BSONError } from 'bson'; import * as BSON from '../../src/bson'; @@ -11,16 +11,10 @@ const msgHeader: MessageHeader = { }; // when top-level key writeErrors contains an error message that has invalid utf8 -const msgBodyInvalidUtf8WriteErrors = Buffer.from( - '0000000000ca020000106e00000000000477726974654572726f727300a50200000330009d02000010696e646578000000000010636f646500f82a0000036b65795061747465726e000f0000001074657874000100000000036b657956616c756500610100000274657874005201000064e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e298830000026572726d736700f1000000453131303030206475706c6963617465206b6579206572726f7220636f6c6c656374696f6e3a20626967646174612e7465737420696e6465783a20746578745f3120647570206b65793a207b20746578743a202264e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e2982e2e2e22207d000000016f6b00000000000000f03f00', - 'hex' -); - -const badutf8inputtodeserialize = Buffer.from( - 'ca020000106e00000000000477726974654572726f727300a50200000330009d02000010696e646578000000000010636f646500f82a0000036b65795061747465726e000f0000001074657874000100000000036b657956616c756500610100000274657874005201000064e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e298830000026572726d736700f1000000453131303030206475706c6963617465206b6579206572726f7220636f6c6c656374696f6e3a20626967646174612e7465737420696e6465783a20746578745f3120647570206b65793a207b20746578743a202264e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e2982e2e2e22207d000000016f6b00000000000000f03f00', - 'hex' -); - +const invalidUtf8ErrorMsg = + '0000000000ca020000106e00000000000477726974654572726f727300a50200000330009d02000010696e646578000000000010636f646500f82a0000036b65795061747465726e000f0000001074657874000100000000036b657956616c756500610100000274657874005201000064e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e298830000026572726d736700f1000000453131303030206475706c6963617465206b6579206572726f7220636f6c6c656374696f6e3a20626967646174612e7465737420696e6465783a20746578745f3120647570206b65793a207b20746578743a202264e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e2982e2e2e22207d000000016f6b00000000000000f03f00'; +const msgBodyInvalidUtf8WriteErrors = Buffer.from(invalidUtf8ErrorMsg, 'hex'); +const invalidUtf8ErrorMsgDeserializeInput = Buffer.from(invalidUtf8ErrorMsg.substring(10), 'hex'); const invalidUtf8InWriteErrorsJSON = { n: 0, writeErrors: [ @@ -41,17 +35,11 @@ const invalidUtf8InWriteErrorsJSON = { }; // when another top-level key besides writeErrors has invalid utf8 -const nKeyWithInvalidUtf8 = Buffer.from( - 'cc020000026e0005000000f09f98ff000477726974654572726f727300a60200000330009e02000010696e646578000000000010636f646500f82a0000036b65795061747465726e000f0000001074657874000100000000036b657956616c756500610100000274657874005201000064e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e298830000026572726d736700f2000000453131303030206475706c6963617465206b6579206572726f7220636f6c6c656374696f6e3a20626967646174612e7465737420696e6465783a20746578745f3120647570206b65793a207b20746578743a202264e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883efbfbd2e2e2e22207d000000106f6b000100000000', - 'hex' -); - -const msgBodyNKeyWithInvalidUtf8 = Buffer.from( - '0000000000cc020000026e0005000000f09f98ff000477726974654572726f727300a60200000330009e02000010696e646578000000000010636f646500f82a0000036b65795061747465726e000f0000001074657874000100000000036b657956616c756500610100000274657874005201000064e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e298830000026572726d736700f2000000453131303030206475706c6963617465206b6579206572726f7220636f6c6c656374696f6e3a20626967646174612e7465737420696e6465783a20746578745f3120647570206b65793a207b20746578743a202264e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883efbfbd2e2e2e22207d000000106f6b000100000000', - 'hex' -); - -const invalidUtf8InOtherKeyJSON = { +const nKeyWithInvalidUtf8 = + '0000000000cc020000026e0005000000f09f98ff000477726974654572726f727300a60200000330009e02000010696e646578000000000010636f646500f82a0000036b65795061747465726e000f0000001074657874000100000000036b657956616c756500610100000274657874005201000064e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e298830000026572726d736700f2000000453131303030206475706c6963617465206b6579206572726f7220636f6c6c656374696f6e3a20626967646174612e7465737420696e6465783a20746578745f3120647570206b65793a207b20746578743a202264e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883e29883efbfbd2e2e2e22207d000000106f6b000100000000'; +const nKeyWithInvalidUtf8DeserializeInput = Buffer.from(nKeyWithInvalidUtf8.substring(10), 'hex'); +const msgBodyNKeyWithInvalidUtf8 = Buffer.from(nKeyWithInvalidUtf8, 'hex'); +const invalidUtf8InNKeyJSON = { n: '��', writeErrors: [ { @@ -71,30 +59,35 @@ const invalidUtf8InOtherKeyJSON = { }; describe('BinMsg', function () { - it('should throw error if trying to toggle validation settings for keys other than writeErrors', function () { + context('when validation is disabled for writeErrors', function () { const binMsgInvalidUtf8ErrorMsg = new BinMsg( Buffer.alloc(0), msgHeader, msgBodyInvalidUtf8WriteErrors ); - expect(() => binMsgInvalidUtf8ErrorMsg.parse({ validation: { utf8: { n: false } } })).to.throw( - Error, - 'Can only toggle validation settings for writeErrors key' - ); + const options: OpResponseOptions = { validation: { utf8: { writeErrors: false } } }; + + it('contains replacement characters for invalid utf8 in writeError object', function () { + expect(BSON.deserialize(invalidUtf8ErrorMsgDeserializeInput, options)).to.deep.equals( + invalidUtf8InWriteErrorsJSON + ); + }); + + it('should not throw invalid utf8 error', function () { + expect(() => binMsgInvalidUtf8ErrorMsg.parse(options)).to.not.throw(); + }); }); - it('should not throw invalid utf8 error when validation disabled for writeErrors', function () { + it('should throw error if trying to toggle validation settings for keys other than writeErrors', function () { const binMsgInvalidUtf8ErrorMsg = new BinMsg( Buffer.alloc(0), msgHeader, msgBodyInvalidUtf8WriteErrors ); - expect(() => - binMsgInvalidUtf8ErrorMsg.parse({ validation: { utf8: { writeErrors: false } } }) - ).to.not.throw(); - expect( - BSON.deserialize(badutf8inputtodeserialize, { validation: { utf8: { writeErrors: false } } }) - ).to.deep.equals(invalidUtf8InWriteErrorsJSON); + expect(() => binMsgInvalidUtf8ErrorMsg.parse({ validation: { utf8: { n: false } } })).to.throw( + Error, + 'Can only toggle validation settings for writeErrors key' + ); }); it('should by default disable validation for writeErrors if no validation specified', function () { @@ -112,6 +105,28 @@ describe('BinMsg', function () { expect(() => binMsgInvalidUtf8ErrorMsg.parse(options)).to.not.throw(); }); + context( + 'when another key has invalid utf8 and validation is enabled for writeErrors', + function () { + const binMsgAnotherKeyWithInvalidUtf8 = new BinMsg( + Buffer.alloc(0), + msgHeader, + msgBodyNKeyWithInvalidUtf8 + ); + const option: OpResponseOptions = { validation: { utf8: { writeErrors: true } } }; + + it('should not throw invalid utf8 error', function () { + expect(() => binMsgAnotherKeyWithInvalidUtf8.parse(option)).to.not.throw(); + }); + + it('contains replacement characters for invalid utf8 key', function () { + expect(BSON.deserialize(nKeyWithInvalidUtf8DeserializeInput, option)).to.deep.equals( + invalidUtf8InNKeyJSON + ); + }); + } + ); + it('should throw invalid utf8 error when validation enabled for writeErrors', function () { const binMsgInvalidUtf8ErrorMsg = new BinMsg( Buffer.alloc(0), @@ -123,22 +138,6 @@ describe('BinMsg', function () { ).to.throw(BSONError, 'Invalid UTF-8 string in BSON document'); }); - it('should not throw when another key has invalid utf8 and writeErrors is validated', function () { - const binMsgAnotherKeyWithInvalidUtf8 = new BinMsg( - Buffer.alloc(0), - msgHeader, - msgBodyNKeyWithInvalidUtf8 - ); - expect(() => - binMsgAnotherKeyWithInvalidUtf8.parse({ validation: { utf8: { writeErrors: true } } }) - ).to.not.throw(); - expect( - BSON.deserialize(nKeyWithInvalidUtf8, { - validation: { utf8: { writeErrors: true } } - }) - ).to.deep.equals(invalidUtf8InOtherKeyJSON); - }); - it('should throw error when another key has invalid utf8 and writeErrors is not validated', function () { const binMsgAnotherKeyWithInvalidUtf8 = new BinMsg( Buffer.alloc(0), From 169ad848a43dc5920a49badc9bc6e82551517c8c Mon Sep 17 00:00:00 2001 From: Grace Chong Date: Wed, 24 Nov 2021 12:36:03 -0500 Subject: [PATCH 06/10] refactor: refactor to keep change internal by removing option from BSONSerializeOptions and options registry --- package-lock.json | 14 +++++++------- src/bson.ts | 2 +- src/cmap/commands.ts | 14 ++++++++------ src/connection_string.ts | 4 ---- test/types/bson.test-d.ts | 3 +-- 5 files changed, 17 insertions(+), 20 deletions(-) diff --git a/package-lock.json b/package-lock.json index ffaebefc09a..ae937255026 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "4.2.0", "license": "Apache-2.0", "dependencies": { - "bson": "^4.5.4", + "bson": "^4.6.0", "denque": "^2.0.1", "mongodb-connection-string-url": "^2.2.0" }, @@ -1557,9 +1557,9 @@ } }, "node_modules/bson": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/bson/-/bson-4.5.4.tgz", - "integrity": "sha512-wIt0bPACnx8Ju9r6IsS2wVtGDHBr9Dxb+U29A1YED2pu8XOhS8aKjOnLZ8sxyXkPwanoK7iWWVhS1+coxde6xA==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-4.6.0.tgz", + "integrity": "sha512-8jw1NU1hglS+Da1jDOUYuNcBJ4cNHCFIqzlwoFNnsTOg2R/ox0aTYcTiBN4dzRa9q7Cvy6XErh3L8ReTEb9AQQ==", "dependencies": { "buffer": "^5.6.0" }, @@ -8466,9 +8466,9 @@ } }, "bson": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/bson/-/bson-4.5.4.tgz", - "integrity": "sha512-wIt0bPACnx8Ju9r6IsS2wVtGDHBr9Dxb+U29A1YED2pu8XOhS8aKjOnLZ8sxyXkPwanoK7iWWVhS1+coxde6xA==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-4.6.0.tgz", + "integrity": "sha512-8jw1NU1hglS+Da1jDOUYuNcBJ4cNHCFIqzlwoFNnsTOg2R/ox0aTYcTiBN4dzRa9q7Cvy6XErh3L8ReTEb9AQQ==", "requires": { "buffer": "^5.6.0" } diff --git a/src/bson.ts b/src/bson.ts index 48705674c62..356c158c16e 100644 --- a/src/bson.ts +++ b/src/bson.ts @@ -51,10 +51,10 @@ export interface BSONSerializeOptions | 'cacheFunctionsCrc32' | 'allowObjectSmallerThanBufferSize' | 'index' + | 'validation' > { /** Return BSON filled buffers from operations */ raw?: boolean; - validation?: { utf8: boolean | Record | Record }; } export function pluckBSONSerializeOptions(options: BSONSerializeOptions): BSONSerializeOptions { diff --git a/src/cmap/commands.ts b/src/cmap/commands.ts index f34b448a00e..b08ea5bba80 100644 --- a/src/cmap/commands.ts +++ b/src/cmap/commands.ts @@ -850,8 +850,7 @@ export class BinMsg { promoteLongs, promoteValues, promoteBuffers, - bsonRegExp, - validation + bsonRegExp }; while (this.index < this.data.length) { @@ -859,8 +858,9 @@ export class BinMsg { if (payloadType === 0) { const bsonSize = this.data.readUInt32LE(this.index); const bin = this.data.slice(this.index, this.index + bsonSize); - this.documents.push(raw ? bin : BSON.deserialize(bin, _options)); - + this.documents.push( + raw ? bin : BSON.deserialize(bin, Object.assign({ validation }, _options)) + ); this.index += bsonSize; } else if (payloadType === 1) { // It was decided that no driver makes use of payload type 1 @@ -874,8 +874,10 @@ export class BinMsg { const fieldsAsRaw: Document = {}; fieldsAsRaw[documentsReturnedIn] = true; _options.fieldsAsRaw = fieldsAsRaw; - - const doc = BSON.deserialize(this.documents[0] as Buffer, _options); + const doc = BSON.deserialize( + this.documents[0] as Buffer, + Object.assign({ validation }, _options) + ); this.documents = [doc]; } diff --git a/src/connection_string.ts b/src/connection_string.ts index a0921e6de1e..ed897000dca 100644 --- a/src/connection_string.ts +++ b/src/connection_string.ts @@ -864,10 +864,6 @@ export const OPTIONS = { default: false, type: 'boolean' }, - validation: { - default: { utf8: { writeErrors: false } }, - type: 'record' - }, readConcern: { transform({ values: [value], options }) { if (value instanceof ReadConcern || isRecord(value, ['level'] as const)) { diff --git a/test/types/bson.test-d.ts b/test/types/bson.test-d.ts index f33a784ff10..a98699c15e2 100644 --- a/test/types/bson.test-d.ts +++ b/test/types/bson.test-d.ts @@ -21,8 +21,7 @@ type PermittedBSONOptionKeys = | 'promoteValues' | 'bsonRegExp' | 'fieldsAsRaw' - | 'raw' - | 'validation'; + | 'raw'; const keys = null as unknown as PermittedBSONOptionKeys; // creates an explicit allow list assertion From b692baf7152981b67c86c0d95e83bf83b8d649b0 Mon Sep 17 00:00:00 2001 From: Grace Chong Date: Mon, 29 Nov 2021 10:14:00 -0500 Subject: [PATCH 07/10] chore: fix type --- src/cmap/commands.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cmap/commands.ts b/src/cmap/commands.ts index b08ea5bba80..021535aa221 100644 --- a/src/cmap/commands.ts +++ b/src/cmap/commands.ts @@ -469,7 +469,7 @@ export interface MessageHeader { export interface OpResponseOptions extends BSONSerializeOptions { raw?: boolean; documentsReturnedIn?: string | null; - validation?: { utf8: boolean | Record | Record }; + validation?: { utf8: Record | Record }; } /** @internal */ From 1eb85842c8d21c2fe28ca41aebdebc6a7c176f7a Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Mon, 29 Nov 2021 17:56:23 -0500 Subject: [PATCH 08/10] address comments, but broke tests (wip) --- src/cmap/commands.ts | 24 ++++++++---------------- test/unit/commands.test.ts | 12 ------------ 2 files changed, 8 insertions(+), 28 deletions(-) diff --git a/src/cmap/commands.ts b/src/cmap/commands.ts index 021535aa221..6104c169906 100644 --- a/src/cmap/commands.ts +++ b/src/cmap/commands.ts @@ -469,7 +469,8 @@ export interface MessageHeader { export interface OpResponseOptions extends BSONSerializeOptions { raw?: boolean; documentsReturnedIn?: string | null; - validation?: { utf8: Record | Record }; + // For now we use this internally to only prevent writeErrors from crashing the driver + validation?: { utf8: { writeErrors: boolean } }; } /** @internal */ @@ -838,19 +839,15 @@ export class BinMsg { const promoteValues = options.promoteValues ?? this.opts.promoteValues; const promoteBuffers = options.promoteBuffers ?? this.opts.promoteBuffers; const bsonRegExp = options.bsonRegExp ?? this.opts.bsonRegExp; - if (options.validation && Object.keys(options.validation.utf8)[0] !== 'writeErrors') { - throw new MongoInvalidArgumentError( - 'Can only toggle validation settings for writeErrors key' - ); - } const validation = options.validation ?? { utf8: { writeErrors: false } }; // Set up the options - const _options: BSONSerializeOptions = { + const bsonOptions: BSONSerializeOptions & { validation: { utf8: { writeErrors: boolean } } } = { promoteLongs, promoteValues, promoteBuffers, - bsonRegExp + bsonRegExp, + validation }; while (this.index < this.data.length) { @@ -858,9 +855,7 @@ export class BinMsg { if (payloadType === 0) { const bsonSize = this.data.readUInt32LE(this.index); const bin = this.data.slice(this.index, this.index + bsonSize); - this.documents.push( - raw ? bin : BSON.deserialize(bin, Object.assign({ validation }, _options)) - ); + this.documents.push(raw ? bin : BSON.deserialize(bin, bsonOptions)); this.index += bsonSize; } else if (payloadType === 1) { // It was decided that no driver makes use of payload type 1 @@ -873,11 +868,8 @@ export class BinMsg { if (this.documents.length === 1 && documentsReturnedIn != null && raw) { const fieldsAsRaw: Document = {}; fieldsAsRaw[documentsReturnedIn] = true; - _options.fieldsAsRaw = fieldsAsRaw; - const doc = BSON.deserialize( - this.documents[0] as Buffer, - Object.assign({ validation }, _options) - ); + bsonOptions.fieldsAsRaw = fieldsAsRaw; + const doc = BSON.deserialize(this.documents[0] as Buffer, bsonOptions); this.documents = [doc]; } diff --git a/test/unit/commands.test.ts b/test/unit/commands.test.ts index 86aa41fe853..1ff2ced06fa 100644 --- a/test/unit/commands.test.ts +++ b/test/unit/commands.test.ts @@ -78,18 +78,6 @@ describe('BinMsg', function () { }); }); - it('should throw error if trying to toggle validation settings for keys other than writeErrors', function () { - const binMsgInvalidUtf8ErrorMsg = new BinMsg( - Buffer.alloc(0), - msgHeader, - msgBodyInvalidUtf8WriteErrors - ); - expect(() => binMsgInvalidUtf8ErrorMsg.parse({ validation: { utf8: { n: false } } })).to.throw( - Error, - 'Can only toggle validation settings for writeErrors key' - ); - }); - it('should by default disable validation for writeErrors if no validation specified', function () { const binMsgInvalidUtf8ErrorMsg = new BinMsg( Buffer.alloc(0), From 393b1226d7060b765043c93dc53bf6898af72da0 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Mon, 29 Nov 2021 18:11:32 -0500 Subject: [PATCH 09/10] fix: tests --- test/unit/commands.test.ts | 51 ++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/test/unit/commands.test.ts b/test/unit/commands.test.ts index 1ff2ced06fa..7f5bb2058e8 100644 --- a/test/unit/commands.test.ts +++ b/test/unit/commands.test.ts @@ -58,27 +58,27 @@ const invalidUtf8InNKeyJSON = { ok: 1 }; -describe('BinMsg', function () { - context('when validation is disabled for writeErrors', function () { +describe('BinMsg BSON utf8 validation', () => { + context('when validation is disabled for writeErrors', () => { const binMsgInvalidUtf8ErrorMsg = new BinMsg( Buffer.alloc(0), msgHeader, msgBodyInvalidUtf8WriteErrors ); - const options: OpResponseOptions = { validation: { utf8: { writeErrors: false } } }; + const options = { validation: { utf8: { writeErrors: false } as const } }; - it('contains replacement characters for invalid utf8 in writeError object', function () { + it('contains replacement characters for invalid utf8 in writeError object', () => { expect(BSON.deserialize(invalidUtf8ErrorMsgDeserializeInput, options)).to.deep.equals( invalidUtf8InWriteErrorsJSON ); }); - it('should not throw invalid utf8 error', function () { + it('should not throw invalid utf8 error', () => { expect(() => binMsgInvalidUtf8ErrorMsg.parse(options)).to.not.throw(); }); }); - it('should by default disable validation for writeErrors if no validation specified', function () { + it('should by default disable validation for writeErrors if no validation specified', () => { const binMsgInvalidUtf8ErrorMsg = new BinMsg( Buffer.alloc(0), msgHeader, @@ -93,29 +93,26 @@ describe('BinMsg', function () { expect(() => binMsgInvalidUtf8ErrorMsg.parse(options)).to.not.throw(); }); - context( - 'when another key has invalid utf8 and validation is enabled for writeErrors', - function () { - const binMsgAnotherKeyWithInvalidUtf8 = new BinMsg( - Buffer.alloc(0), - msgHeader, - msgBodyNKeyWithInvalidUtf8 - ); - const option: OpResponseOptions = { validation: { utf8: { writeErrors: true } } }; + context('when another key has invalid utf8 and validation is enabled for writeErrors', () => { + const binMsgAnotherKeyWithInvalidUtf8 = new BinMsg( + Buffer.alloc(0), + msgHeader, + msgBodyNKeyWithInvalidUtf8 + ); + const options = { validation: { utf8: { writeErrors: true } as const } }; - it('should not throw invalid utf8 error', function () { - expect(() => binMsgAnotherKeyWithInvalidUtf8.parse(option)).to.not.throw(); - }); + it('should not throw invalid utf8 error', () => { + expect(() => binMsgAnotherKeyWithInvalidUtf8.parse(options)).to.not.throw(); + }); - it('contains replacement characters for invalid utf8 key', function () { - expect(BSON.deserialize(nKeyWithInvalidUtf8DeserializeInput, option)).to.deep.equals( - invalidUtf8InNKeyJSON - ); - }); - } - ); + it('contains replacement characters for invalid utf8 key', () => { + expect(BSON.deserialize(nKeyWithInvalidUtf8DeserializeInput, options)).to.deep.equals( + invalidUtf8InNKeyJSON + ); + }); + }); - it('should throw invalid utf8 error when validation enabled for writeErrors', function () { + it('should throw invalid utf8 error when validation enabled for writeErrors', () => { const binMsgInvalidUtf8ErrorMsg = new BinMsg( Buffer.alloc(0), msgHeader, @@ -126,7 +123,7 @@ describe('BinMsg', function () { ).to.throw(BSONError, 'Invalid UTF-8 string in BSON document'); }); - it('should throw error when another key has invalid utf8 and writeErrors is not validated', function () { + it('should throw error when another key has invalid utf8 and writeErrors is not validated', () => { const binMsgAnotherKeyWithInvalidUtf8 = new BinMsg( Buffer.alloc(0), msgHeader, From 447da06f109dc44e66bd13d72c942481e4074c1f Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Tue, 30 Nov 2021 11:49:27 -0500 Subject: [PATCH 10/10] fix: lint --- src/cmap/commands.ts | 5 +++-- test/unit/commands.test.ts | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/cmap/commands.ts b/src/cmap/commands.ts index 6104c169906..322ca736a39 100644 --- a/src/cmap/commands.ts +++ b/src/cmap/commands.ts @@ -842,13 +842,14 @@ export class BinMsg { const validation = options.validation ?? { utf8: { writeErrors: false } }; // Set up the options - const bsonOptions: BSONSerializeOptions & { validation: { utf8: { writeErrors: boolean } } } = { + const bsonOptions: BSONSerializeOptions = { promoteLongs, promoteValues, promoteBuffers, bsonRegExp, validation - }; + // Due to the strictness of the BSON libraries validation option we need this cast + } as BSONSerializeOptions & { validation: { utf8: { writeErrors: boolean } } }; while (this.index < this.data.length) { const payloadType = this.data.readUInt8(this.index++); diff --git a/test/unit/commands.test.ts b/test/unit/commands.test.ts index 7f5bb2058e8..a8156abc732 100644 --- a/test/unit/commands.test.ts +++ b/test/unit/commands.test.ts @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { BinMsg, MessageHeader, OpResponseOptions } from '../../src/cmap/commands'; +import { BinMsg, MessageHeader } from '../../src/cmap/commands'; import { BSONError } from 'bson'; import * as BSON from '../../src/bson';