Skip to content

Commit d75102d

Browse files
authored
fix(NODE-3629): correct corpus runner and add null checks (#464)
1 parent 80d7f03 commit d75102d

19 files changed

+859
-110
lines changed

Diff for: package-lock.json

+582
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: package.json

+2
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"@rollup/plugin-typescript": "^6.0.0",
3939
"@typescript-eslint/eslint-plugin": "^3.10.1",
4040
"@typescript-eslint/parser": "^3.10.1",
41+
"array-includes": "^3.1.3",
4142
"benchmark": "^2.1.4",
4243
"chai": "^4.2.0",
4344
"downlevel-dts": "^0.7.0",
@@ -54,6 +55,7 @@
5455
"mocha": "5.2.0",
5556
"node-fetch": "^2.6.1",
5657
"nyc": "^15.1.0",
58+
"object.entries": "^1.1.4",
5759
"prettier": "^2.1.1",
5860
"rimraf": "^3.0.2",
5961
"rollup": "^2.26.5",

Diff for: src/binary.ts

+7-6
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { ensureBuffer } from './ensure_buffer';
33
import { uuidHexStringToBuffer } from './uuid_utils';
44
import { UUID, UUIDExtended } from './uuid';
55
import type { EJSONOptions } from './extended_json';
6+
import { BSONError, BSONTypeError } from './error';
67

78
/** @public */
89
export type BinarySequence = Uint8Array | Buffer | number[];
@@ -73,7 +74,7 @@ export class Binary {
7374
!(buffer instanceof ArrayBuffer) &&
7475
!Array.isArray(buffer)
7576
) {
76-
throw new TypeError(
77+
throw new BSONTypeError(
7778
'Binary can only be constructed from string, Buffer, TypedArray, or Array<number>'
7879
);
7980
}
@@ -108,9 +109,9 @@ export class Binary {
108109
put(byteValue: string | number | Uint8Array | Buffer | number[]): void {
109110
// If it's a string and a has more than one character throw an error
110111
if (typeof byteValue === 'string' && byteValue.length !== 1) {
111-
throw new TypeError('only accepts single character String');
112+
throw new BSONTypeError('only accepts single character String');
112113
} else if (typeof byteValue !== 'number' && byteValue.length !== 1)
113-
throw new TypeError('only accepts single character Uint8Array or Array');
114+
throw new BSONTypeError('only accepts single character Uint8Array or Array');
114115

115116
// Decode the byte value once
116117
let decodedByte: number;
@@ -123,7 +124,7 @@ export class Binary {
123124
}
124125

125126
if (decodedByte < 0 || decodedByte > 255) {
126-
throw new TypeError('only accepts number in a valid unsigned byte range 0-255');
127+
throw new BSONTypeError('only accepts number in a valid unsigned byte range 0-255');
127128
}
128129

129130
if (this.buffer.length > this.position) {
@@ -238,7 +239,7 @@ export class Binary {
238239
return new UUID(this.buffer.slice(0, this.position));
239240
}
240241

241-
throw new Error(
242+
throw new BSONError(
242243
`Binary sub_type "${this.sub_type}" is not supported for converting to UUID. Only "${Binary.SUBTYPE_UUID}" is currently supported.`
243244
);
244245
}
@@ -266,7 +267,7 @@ export class Binary {
266267
data = uuidHexStringToBuffer(doc.$uuid);
267268
}
268269
if (!data) {
269-
throw new TypeError(`Unexpected Binary Extended JSON format ${JSON.stringify(doc)}`);
270+
throw new BSONTypeError(`Unexpected Binary Extended JSON format ${JSON.stringify(doc)}`);
270271
}
271272
return new Binary(data, type);
272273
}

Diff for: src/bson.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { Map } from './map';
1212
import { MaxKey } from './max_key';
1313
import { MinKey } from './min_key';
1414
import { ObjectId } from './objectid';
15+
import { BSONError, BSONTypeError } from './error';
1516
import { calculateObjectSize as internalCalculateObjectSize } from './parser/calculate_size';
1617
// Parts of the parser
1718
import { deserialize as internalDeserialize, DeserializeOptions } from './parser/deserializer';
@@ -98,6 +99,7 @@ export {
9899
// later builds we changed it back to ObjectID (capital D) to match legacy implementations.
99100
ObjectId as ObjectID
100101
};
102+
export { BSONError, BSONTypeError } from './error';
101103

102104
/** @public */
103105
export interface Document {
@@ -326,6 +328,8 @@ const BSON = {
326328
serializeWithBufferAndIndex,
327329
deserialize,
328330
calculateObjectSize,
329-
deserializeStream
331+
deserializeStream,
332+
BSONError,
333+
BSONTypeError
330334
};
331335
export default BSON;

Diff for: src/decimal128.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Buffer } from 'buffer';
2+
import { BSONTypeError } from './error';
23
import { Long } from './long';
34

45
const PARSE_STRING_REGEXP = /^(\+|-)?(\d+|(\d*\.\d*))?(E|e)?([-+])?(\d+)?$/;
@@ -105,7 +106,7 @@ function lessThan(left: Long, right: Long): boolean {
105106
}
106107

107108
function invalidErr(string: string, message: string) {
108-
throw new TypeError(`"${string}" is not a valid Decimal128 string - ${message}`);
109+
throw new BSONTypeError(`"${string}" is not a valid Decimal128 string - ${message}`);
109110
}
110111

111112
/** @public */
@@ -187,7 +188,7 @@ export class Decimal128 {
187188
// TODO: implementing a custom parsing for this, or refactoring the regex would yield
188189
// further gains.
189190
if (representation.length >= 7000) {
190-
throw new TypeError('' + representation + ' not a valid Decimal128 string');
191+
throw new BSONTypeError('' + representation + ' not a valid Decimal128 string');
191192
}
192193

193194
// Results
@@ -197,7 +198,7 @@ export class Decimal128 {
197198

198199
// Validate the string
199200
if ((!stringMatch && !infMatch && !nanMatch) || representation.length === 0) {
200-
throw new TypeError('' + representation + ' not a valid Decimal128 string');
201+
throw new BSONTypeError('' + representation + ' not a valid Decimal128 string');
201202
}
202203

203204
if (stringMatch) {
@@ -269,7 +270,7 @@ export class Decimal128 {
269270
}
270271

271272
if (sawRadix && !nDigitsRead)
272-
throw new TypeError('' + representation + ' not a valid Decimal128 string');
273+
throw new BSONTypeError('' + representation + ' not a valid Decimal128 string');
273274

274275
// Read exponent if exists
275276
if (representation[index] === 'e' || representation[index] === 'E') {

Diff for: src/ensure_buffer.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Buffer } from 'buffer';
2+
import { BSONTypeError } from './error';
23
import { isAnyArrayBuffer } from './parser/utils';
34

45
/**
@@ -22,5 +23,5 @@ export function ensureBuffer(potentialBuffer: Buffer | ArrayBufferView | ArrayBu
2223
return Buffer.from(potentialBuffer);
2324
}
2425

25-
throw new TypeError('Must use either Buffer or TypedArray');
26+
throw new BSONTypeError('Must use either Buffer or TypedArray');
2627
}

Diff for: src/error.ts

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/** @public */
2+
export class BSONError extends Error {
3+
get name(): string {
4+
return 'BSONError';
5+
}
6+
}
7+
8+
/** @public */
9+
export class BSONTypeError extends TypeError {
10+
get name(): string {
11+
return 'BSONTypeError';
12+
}
13+
}

Diff for: src/extended_json.ts

+13-5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { Code } from './code';
44
import { DBRef, isDBRefLike } from './db_ref';
55
import { Decimal128 } from './decimal128';
66
import { Double } from './double';
7+
import { BSONError, BSONTypeError } from './error';
78
import { Int32 } from './int_32';
89
import { Long } from './long';
910
import { MaxKey } from './max_key';
@@ -185,7 +186,7 @@ function serializeValue(value: any, options: EJSONSerializeOptions): any {
185186
circularPart.length + (alreadySeen.length + current.length) / 2 - 1
186187
);
187188

188-
throw new TypeError(
189+
throw new BSONTypeError(
189190
'Converting circular structure to EJSON:\n' +
190191
` ${leadingPart}${alreadySeen}${circularPart}${current}\n` +
191192
` ${leadingSpace}\\${dashes}/`
@@ -274,7 +275,7 @@ const BSON_TYPE_MAPPINGS = {
274275

275276
// eslint-disable-next-line @typescript-eslint/no-explicit-any
276277
function serializeDocument(doc: any, options: EJSONSerializeOptions) {
277-
if (doc == null || typeof doc !== 'object') throw new Error('not an object instance');
278+
if (doc == null || typeof doc !== 'object') throw new BSONError('not an object instance');
278279

279280
const bsontype: BSONType['_bsontype'] = doc._bsontype;
280281
if (typeof bsontype === 'undefined') {
@@ -300,7 +301,7 @@ function serializeDocument(doc: any, options: EJSONSerializeOptions) {
300301
// Copy the object into this library's version of that type.
301302
const mapper = BSON_TYPE_MAPPINGS[doc._bsontype];
302303
if (!mapper) {
303-
throw new TypeError('Unrecognized or invalid _bsontype: ' + doc._bsontype);
304+
throw new BSONTypeError('Unrecognized or invalid _bsontype: ' + doc._bsontype);
304305
}
305306
outDoc = mapper(outDoc);
306307
}
@@ -319,7 +320,7 @@ function serializeDocument(doc: any, options: EJSONSerializeOptions) {
319320

320321
return outDoc.toExtendedJSON(options);
321322
} else {
322-
throw new Error('_bsontype must be a string, but was: ' + typeof bsontype);
323+
throw new BSONError('_bsontype must be a string, but was: ' + typeof bsontype);
323324
}
324325
}
325326

@@ -366,7 +367,14 @@ export namespace EJSON {
366367
if (typeof finalOptions.relaxed === 'boolean') finalOptions.strict = !finalOptions.relaxed;
367368
if (typeof finalOptions.strict === 'boolean') finalOptions.relaxed = !finalOptions.strict;
368369

369-
return JSON.parse(text, (_key, value) => deserializeValue(value, finalOptions));
370+
return JSON.parse(text, (key, value) => {
371+
if (key.indexOf('\x00') !== -1) {
372+
throw new BSONError(
373+
`BSON Document field names cannot contain null bytes, found: ${JSON.stringify(key)}`
374+
);
375+
}
376+
return deserializeValue(value, finalOptions);
377+
});
370378
}
371379

372380
export type JSONPrimitive = string | number | boolean | null;

Diff for: src/objectid.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Buffer } from 'buffer';
22
import { ensureBuffer } from './ensure_buffer';
3+
import { BSONTypeError } from './error';
34
import { deprecate, isUint8Array, randomBytes } from './parser/utils';
45

56
// Regular expression that checks for hex value
@@ -84,7 +85,7 @@ export class ObjectId {
8485
} else if (id.length === 24 && checkForHexRegExp.test(id)) {
8586
this[kId] = Buffer.from(id, 'hex');
8687
} else {
87-
throw new TypeError(
88+
throw new BSONTypeError(
8889
'Argument passed in must be a Buffer or string of 12 bytes or a string of 24 hex characters'
8990
);
9091
}
@@ -276,7 +277,7 @@ export class ObjectId {
276277
static createFromHexString(hexString: string): ObjectId {
277278
// Throw an error if it's not a valid setup
278279
if (typeof hexString === 'undefined' || (hexString != null && hexString.length !== 24)) {
279-
throw new TypeError(
280+
throw new BSONTypeError(
280281
'Argument passed in must be a single String of 12 bytes or a string of 24 hex characters'
281282
);
282283
}

0 commit comments

Comments
 (0)