bson 4.5.1 → 4.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/HISTORY.md +572 -0
- package/bower.json +1 -1
- package/bson.d.ts +70 -13
- package/dist/bson.browser.esm.js +314 -177
- package/dist/bson.browser.esm.js.map +1 -1
- package/dist/bson.browser.umd.js +317 -176
- package/dist/bson.browser.umd.js.map +1 -1
- package/dist/bson.bundle.js +317 -176
- package/dist/bson.bundle.js.map +1 -1
- package/dist/bson.esm.js +314 -177
- package/dist/bson.esm.js.map +1 -1
- package/lib/binary.js +11 -9
- package/lib/binary.js.map +1 -1
- package/lib/bson.js +11 -3
- package/lib/bson.js.map +1 -1
- package/lib/code.js +0 -1
- package/lib/code.js.map +1 -1
- package/lib/constants.js +5 -1
- package/lib/constants.js.map +1 -1
- package/lib/db_ref.js +0 -1
- package/lib/db_ref.js.map +1 -1
- package/lib/decimal128.js +6 -5
- package/lib/decimal128.js.map +1 -1
- package/lib/double.js +3 -1
- package/lib/double.js.map +1 -1
- package/lib/ensure_buffer.js +3 -2
- package/lib/ensure_buffer.js.map +1 -1
- package/lib/error.js +55 -0
- package/lib/error.js.map +1 -0
- package/lib/extended_json.js +11 -5
- package/lib/extended_json.js.map +1 -1
- package/lib/int_32.js +4 -2
- package/lib/int_32.js.map +1 -1
- package/lib/objectid.js +39 -35
- package/lib/objectid.js.map +1 -1
- package/lib/parser/deserializer.js +131 -53
- package/lib/parser/deserializer.js.map +1 -1
- package/lib/parser/serializer.js +13 -12
- package/lib/parser/serializer.js.map +1 -1
- package/lib/regexp.js +9 -2
- package/lib/regexp.js.map +1 -1
- package/lib/symbol.js +0 -2
- package/lib/symbol.js.map +1 -1
- package/lib/uuid.js +4 -4
- package/lib/uuid.js.map +1 -1
- package/lib/uuid_utils.js +2 -1
- package/lib/uuid_utils.js.map +1 -1
- package/package.json +16 -3
- package/src/binary.ts +12 -10
- package/src/bson.ts +7 -1
- package/src/code.ts +0 -1
- package/src/constants.ts +6 -0
- package/src/db_ref.ts +0 -1
- package/src/decimal128.ts +6 -5
- package/src/double.ts +4 -1
- package/src/ensure_buffer.ts +3 -2
- package/src/error.ts +23 -0
- package/src/extended_json.ts +13 -5
- package/src/int_32.ts +5 -2
- package/src/objectid.ts +40 -43
- package/src/parser/deserializer.ts +159 -57
- package/src/parser/serializer.ts +13 -12
- package/src/regexp.ts +14 -2
- package/src/symbol.ts +0 -2
- package/src/uuid.ts +4 -4
- package/src/uuid_utils.ts +2 -1
package/src/error.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** @public */
|
|
2
|
+
export class BSONError extends Error {
|
|
3
|
+
constructor(message: string) {
|
|
4
|
+
super(message);
|
|
5
|
+
Object.setPrototypeOf(this, BSONError.prototype);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
get name(): string {
|
|
9
|
+
return 'BSONError';
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** @public */
|
|
14
|
+
export class BSONTypeError extends TypeError {
|
|
15
|
+
constructor(message: string) {
|
|
16
|
+
super(message);
|
|
17
|
+
Object.setPrototypeOf(this, BSONTypeError.prototype);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
get name(): string {
|
|
21
|
+
return 'BSONTypeError';
|
|
22
|
+
}
|
|
23
|
+
}
|
package/src/extended_json.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { Code } from './code';
|
|
|
4
4
|
import { DBRef, isDBRefLike } from './db_ref';
|
|
5
5
|
import { Decimal128 } from './decimal128';
|
|
6
6
|
import { Double } from './double';
|
|
7
|
+
import { BSONError, BSONTypeError } from './error';
|
|
7
8
|
import { Int32 } from './int_32';
|
|
8
9
|
import { Long } from './long';
|
|
9
10
|
import { MaxKey } from './max_key';
|
|
@@ -185,7 +186,7 @@ function serializeValue(value: any, options: EJSONSerializeOptions): any {
|
|
|
185
186
|
circularPart.length + (alreadySeen.length + current.length) / 2 - 1
|
|
186
187
|
);
|
|
187
188
|
|
|
188
|
-
throw new
|
|
189
|
+
throw new BSONTypeError(
|
|
189
190
|
'Converting circular structure to EJSON:\n' +
|
|
190
191
|
` ${leadingPart}${alreadySeen}${circularPart}${current}\n` +
|
|
191
192
|
` ${leadingSpace}\\${dashes}/`
|
|
@@ -274,7 +275,7 @@ const BSON_TYPE_MAPPINGS = {
|
|
|
274
275
|
|
|
275
276
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
276
277
|
function serializeDocument(doc: any, options: EJSONSerializeOptions) {
|
|
277
|
-
if (doc == null || typeof doc !== 'object') throw new
|
|
278
|
+
if (doc == null || typeof doc !== 'object') throw new BSONError('not an object instance');
|
|
278
279
|
|
|
279
280
|
const bsontype: BSONType['_bsontype'] = doc._bsontype;
|
|
280
281
|
if (typeof bsontype === 'undefined') {
|
|
@@ -300,7 +301,7 @@ function serializeDocument(doc: any, options: EJSONSerializeOptions) {
|
|
|
300
301
|
// Copy the object into this library's version of that type.
|
|
301
302
|
const mapper = BSON_TYPE_MAPPINGS[doc._bsontype];
|
|
302
303
|
if (!mapper) {
|
|
303
|
-
throw new
|
|
304
|
+
throw new BSONTypeError('Unrecognized or invalid _bsontype: ' + doc._bsontype);
|
|
304
305
|
}
|
|
305
306
|
outDoc = mapper(outDoc);
|
|
306
307
|
}
|
|
@@ -319,7 +320,7 @@ function serializeDocument(doc: any, options: EJSONSerializeOptions) {
|
|
|
319
320
|
|
|
320
321
|
return outDoc.toExtendedJSON(options);
|
|
321
322
|
} else {
|
|
322
|
-
throw new
|
|
323
|
+
throw new BSONError('_bsontype must be a string, but was: ' + typeof bsontype);
|
|
323
324
|
}
|
|
324
325
|
}
|
|
325
326
|
|
|
@@ -366,7 +367,14 @@ export namespace EJSON {
|
|
|
366
367
|
if (typeof finalOptions.relaxed === 'boolean') finalOptions.strict = !finalOptions.relaxed;
|
|
367
368
|
if (typeof finalOptions.strict === 'boolean') finalOptions.relaxed = !finalOptions.strict;
|
|
368
369
|
|
|
369
|
-
return JSON.parse(text, (
|
|
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
|
+
});
|
|
370
378
|
}
|
|
371
379
|
|
|
372
380
|
export type JSONPrimitive = string | number | boolean | null;
|
package/src/int_32.ts
CHANGED
|
@@ -25,7 +25,7 @@ export class Int32 {
|
|
|
25
25
|
value = value.valueOf();
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
this.value = +value;
|
|
28
|
+
this.value = +value | 0;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
/**
|
|
@@ -37,7 +37,10 @@ export class Int32 {
|
|
|
37
37
|
return this.value;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
toString(radix?: number): string {
|
|
41
|
+
return this.value.toString(radix);
|
|
42
|
+
}
|
|
43
|
+
|
|
41
44
|
toJSON(): number {
|
|
42
45
|
return this.value;
|
|
43
46
|
}
|
package/src/objectid.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Buffer } from 'buffer';
|
|
2
2
|
import { ensureBuffer } from './ensure_buffer';
|
|
3
|
+
import { BSONTypeError } from './error';
|
|
3
4
|
import { deprecate, isUint8Array, randomBytes } from './parser/utils';
|
|
4
5
|
|
|
5
6
|
// Regular expression that checks for hex value
|
|
@@ -30,7 +31,7 @@ export class ObjectId {
|
|
|
30
31
|
_bsontype!: 'ObjectId';
|
|
31
32
|
|
|
32
33
|
/** @internal */
|
|
33
|
-
static index =
|
|
34
|
+
static index = Math.floor(Math.random() * 0xffffff);
|
|
34
35
|
|
|
35
36
|
static cacheHexString: boolean;
|
|
36
37
|
|
|
@@ -42,54 +43,54 @@ export class ObjectId {
|
|
|
42
43
|
/**
|
|
43
44
|
* Create an ObjectId type
|
|
44
45
|
*
|
|
45
|
-
* @param
|
|
46
|
+
* @param inputId - Can be a 24 character hex string, 12 byte binary Buffer, or a number.
|
|
46
47
|
*/
|
|
47
|
-
constructor(
|
|
48
|
-
if (!(this instanceof ObjectId)) return new ObjectId(
|
|
49
|
-
|
|
50
|
-
//
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
48
|
+
constructor(inputId?: string | Buffer | number | ObjectIdLike | ObjectId) {
|
|
49
|
+
if (!(this instanceof ObjectId)) return new ObjectId(inputId);
|
|
50
|
+
|
|
51
|
+
// workingId is set based on type of input and whether valid id exists for the input
|
|
52
|
+
let workingId;
|
|
53
|
+
if (typeof inputId === 'object' && inputId && 'id' in inputId) {
|
|
54
|
+
if (typeof inputId.id !== 'string' && !ArrayBuffer.isView(inputId.id)) {
|
|
55
|
+
throw new BSONTypeError(
|
|
56
|
+
'Argument passed in must have an id that is of type string or Buffer'
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
if ('toHexString' in inputId && typeof inputId.toHexString === 'function') {
|
|
60
|
+
workingId = Buffer.from(inputId.toHexString(), 'hex');
|
|
59
61
|
} else {
|
|
60
|
-
|
|
62
|
+
workingId = inputId.id;
|
|
61
63
|
}
|
|
64
|
+
} else {
|
|
65
|
+
workingId = inputId;
|
|
62
66
|
}
|
|
63
67
|
|
|
64
|
-
//
|
|
65
|
-
if (
|
|
68
|
+
// the following cases use workingId to construct an ObjectId
|
|
69
|
+
if (workingId == null || typeof workingId === 'number') {
|
|
70
|
+
// The most common use case (blank id, new objectId instance)
|
|
66
71
|
// Generate a new id
|
|
67
|
-
this[kId] = ObjectId.generate(typeof
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
if (ArrayBuffer.isView(id) && id.byteLength === 12) {
|
|
75
|
-
this[kId] = ensureBuffer(id);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (typeof id === 'string') {
|
|
79
|
-
if (id.length === 12) {
|
|
80
|
-
const bytes = Buffer.from(id);
|
|
72
|
+
this[kId] = ObjectId.generate(typeof workingId === 'number' ? workingId : undefined);
|
|
73
|
+
} else if (ArrayBuffer.isView(workingId) && workingId.byteLength === 12) {
|
|
74
|
+
this[kId] = ensureBuffer(workingId);
|
|
75
|
+
} else if (typeof workingId === 'string') {
|
|
76
|
+
if (workingId.length === 12) {
|
|
77
|
+
const bytes = Buffer.from(workingId);
|
|
81
78
|
if (bytes.byteLength === 12) {
|
|
82
79
|
this[kId] = bytes;
|
|
80
|
+
} else {
|
|
81
|
+
throw new BSONTypeError('Argument passed in must be a string of 12 bytes');
|
|
83
82
|
}
|
|
84
|
-
} else if (
|
|
85
|
-
this[kId] = Buffer.from(
|
|
83
|
+
} else if (workingId.length === 24 && checkForHexRegExp.test(workingId)) {
|
|
84
|
+
this[kId] = Buffer.from(workingId, 'hex');
|
|
86
85
|
} else {
|
|
87
|
-
throw new
|
|
88
|
-
'Argument passed in must be a
|
|
86
|
+
throw new BSONTypeError(
|
|
87
|
+
'Argument passed in must be a string of 12 bytes or a string of 24 hex characters'
|
|
89
88
|
);
|
|
90
89
|
}
|
|
90
|
+
} else {
|
|
91
|
+
throw new BSONTypeError('Argument passed in does not match the accepted types');
|
|
91
92
|
}
|
|
92
|
-
|
|
93
|
+
// If we are caching the hex string
|
|
93
94
|
if (ObjectId.cacheHexString) {
|
|
94
95
|
this.__id = this.id.toString('hex');
|
|
95
96
|
}
|
|
@@ -155,7 +156,7 @@ export class ObjectId {
|
|
|
155
156
|
*/
|
|
156
157
|
static generate(time?: number): Buffer {
|
|
157
158
|
if ('number' !== typeof time) {
|
|
158
|
-
time =
|
|
159
|
+
time = Math.floor(Date.now() / 1000);
|
|
159
160
|
}
|
|
160
161
|
|
|
161
162
|
const inc = ObjectId.getInc();
|
|
@@ -188,7 +189,6 @@ export class ObjectId {
|
|
|
188
189
|
* Converts the id into a 24 character hex string for printing
|
|
189
190
|
*
|
|
190
191
|
* @param format - The Buffer toString format parameter.
|
|
191
|
-
* @internal
|
|
192
192
|
*/
|
|
193
193
|
toString(format?: string): string {
|
|
194
194
|
// Is the id a buffer then use the buffer toString method to return the format
|
|
@@ -196,10 +196,7 @@ export class ObjectId {
|
|
|
196
196
|
return this.toHexString();
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
-
/**
|
|
200
|
-
* Converts to its JSON the 24 character hex string representation.
|
|
201
|
-
* @internal
|
|
202
|
-
*/
|
|
199
|
+
/** Converts to its JSON the 24 character hex string representation. */
|
|
203
200
|
toJSON(): string {
|
|
204
201
|
return this.toHexString();
|
|
205
202
|
}
|
|
@@ -280,7 +277,7 @@ export class ObjectId {
|
|
|
280
277
|
static createFromHexString(hexString: string): ObjectId {
|
|
281
278
|
// Throw an error if it's not a valid setup
|
|
282
279
|
if (typeof hexString === 'undefined' || (hexString != null && hexString.length !== 24)) {
|
|
283
|
-
throw new
|
|
280
|
+
throw new BSONTypeError(
|
|
284
281
|
'Argument passed in must be a single String of 12 bytes or a string of 24 hex characters'
|
|
285
282
|
);
|
|
286
283
|
}
|
|
@@ -6,6 +6,7 @@ import * as constants from '../constants';
|
|
|
6
6
|
import { DBRef, DBRefLike, isDBRefLike } from '../db_ref';
|
|
7
7
|
import { Decimal128 } from '../decimal128';
|
|
8
8
|
import { Double } from '../double';
|
|
9
|
+
import { BSONError } from '../error';
|
|
9
10
|
import { Int32 } from '../int_32';
|
|
10
11
|
import { Long } from '../long';
|
|
11
12
|
import { MaxKey } from '../max_key';
|
|
@@ -44,6 +45,22 @@ export interface DeserializeOptions {
|
|
|
44
45
|
index?: number;
|
|
45
46
|
|
|
46
47
|
raw?: boolean;
|
|
48
|
+
/** Allows for opt-out utf-8 validation for all keys or
|
|
49
|
+
* specified keys. Must be all true or all false.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```js
|
|
53
|
+
* // disables validation on all keys
|
|
54
|
+
* validation: { utf8: false }
|
|
55
|
+
*
|
|
56
|
+
* // enables validation only on specified keys a, b, and c
|
|
57
|
+
* validation: { utf8: { a: true, b: true, c: true } }
|
|
58
|
+
*
|
|
59
|
+
* // disables validation only on specified keys a, b
|
|
60
|
+
* validation: { utf8: { a: false, b: false } }
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
validation?: { utf8: boolean | Record<string, true> | Record<string, false> };
|
|
47
64
|
}
|
|
48
65
|
|
|
49
66
|
// Internal long versions
|
|
@@ -67,26 +84,28 @@ export function deserialize(
|
|
|
67
84
|
(buffer[index + 3] << 24);
|
|
68
85
|
|
|
69
86
|
if (size < 5) {
|
|
70
|
-
throw new
|
|
87
|
+
throw new BSONError(`bson size must be >= 5, is ${size}`);
|
|
71
88
|
}
|
|
72
89
|
|
|
73
90
|
if (options.allowObjectSmallerThanBufferSize && buffer.length < size) {
|
|
74
|
-
throw new
|
|
91
|
+
throw new BSONError(`buffer length ${buffer.length} must be >= bson size ${size}`);
|
|
75
92
|
}
|
|
76
93
|
|
|
77
94
|
if (!options.allowObjectSmallerThanBufferSize && buffer.length !== size) {
|
|
78
|
-
throw new
|
|
95
|
+
throw new BSONError(`buffer length ${buffer.length} must === bson size ${size}`);
|
|
79
96
|
}
|
|
80
97
|
|
|
81
98
|
if (size + index > buffer.byteLength) {
|
|
82
|
-
throw new
|
|
99
|
+
throw new BSONError(
|
|
83
100
|
`(bson size ${size} + options.index ${index} must be <= buffer length ${buffer.byteLength})`
|
|
84
101
|
);
|
|
85
102
|
}
|
|
86
103
|
|
|
87
104
|
// Illegal end value
|
|
88
105
|
if (buffer[index + size - 1] !== 0) {
|
|
89
|
-
throw new
|
|
106
|
+
throw new BSONError(
|
|
107
|
+
"One object, sized correctly, with a spot for an EOO, but the EOO isn't 0x00"
|
|
108
|
+
);
|
|
90
109
|
}
|
|
91
110
|
|
|
92
111
|
// Start deserializtion
|
|
@@ -117,18 +136,57 @@ function deserializeObject(
|
|
|
117
136
|
const promoteLongs = options['promoteLongs'] == null ? true : options['promoteLongs'];
|
|
118
137
|
const promoteValues = options['promoteValues'] == null ? true : options['promoteValues'];
|
|
119
138
|
|
|
139
|
+
// Ensures default validation option if none given
|
|
140
|
+
const validation = options.validation == null ? { utf8: true } : options.validation;
|
|
141
|
+
|
|
142
|
+
// Shows if global utf-8 validation is enabled or disabled
|
|
143
|
+
let globalUTFValidation = true;
|
|
144
|
+
// Reflects utf-8 validation setting regardless of global or specific key validation
|
|
145
|
+
let validationSetting: boolean;
|
|
146
|
+
// Set of keys either to enable or disable validation on
|
|
147
|
+
const utf8KeysSet = new Set();
|
|
148
|
+
|
|
149
|
+
// Check for boolean uniformity and empty validation option
|
|
150
|
+
const utf8ValidatedKeys = validation.utf8;
|
|
151
|
+
if (typeof utf8ValidatedKeys === 'boolean') {
|
|
152
|
+
validationSetting = utf8ValidatedKeys;
|
|
153
|
+
} else {
|
|
154
|
+
globalUTFValidation = false;
|
|
155
|
+
const utf8ValidationValues = Object.keys(utf8ValidatedKeys).map(function (key) {
|
|
156
|
+
return utf8ValidatedKeys[key];
|
|
157
|
+
});
|
|
158
|
+
if (utf8ValidationValues.length === 0) {
|
|
159
|
+
throw new BSONError('UTF-8 validation setting cannot be empty');
|
|
160
|
+
}
|
|
161
|
+
if (typeof utf8ValidationValues[0] !== 'boolean') {
|
|
162
|
+
throw new BSONError('Invalid UTF-8 validation option, must specify boolean values');
|
|
163
|
+
}
|
|
164
|
+
validationSetting = utf8ValidationValues[0];
|
|
165
|
+
// Ensures boolean uniformity in utf-8 validation (all true or all false)
|
|
166
|
+
if (!utf8ValidationValues.every(item => item === validationSetting)) {
|
|
167
|
+
throw new BSONError('Invalid UTF-8 validation option - keys must be all true or all false');
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Add keys to set that will either be validated or not based on validationSetting
|
|
172
|
+
if (!globalUTFValidation) {
|
|
173
|
+
for (const key of Object.keys(utf8ValidatedKeys)) {
|
|
174
|
+
utf8KeysSet.add(key);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
120
178
|
// Set the start index
|
|
121
179
|
const startIndex = index;
|
|
122
180
|
|
|
123
181
|
// Validate that we have at least 4 bytes of buffer
|
|
124
|
-
if (buffer.length < 5) throw new
|
|
182
|
+
if (buffer.length < 5) throw new BSONError('corrupt bson message < 5 bytes long');
|
|
125
183
|
|
|
126
184
|
// Read the document size
|
|
127
185
|
const size =
|
|
128
186
|
buffer[index++] | (buffer[index++] << 8) | (buffer[index++] << 16) | (buffer[index++] << 24);
|
|
129
187
|
|
|
130
188
|
// Ensure buffer is valid size
|
|
131
|
-
if (size < 5 || size > buffer.length) throw new
|
|
189
|
+
if (size < 5 || size > buffer.length) throw new BSONError('corrupt bson message');
|
|
132
190
|
|
|
133
191
|
// Create holding object
|
|
134
192
|
const object: Document = isArray ? [] : {};
|
|
@@ -154,8 +212,19 @@ function deserializeObject(
|
|
|
154
212
|
}
|
|
155
213
|
|
|
156
214
|
// If are at the end of the buffer there is a problem with the document
|
|
157
|
-
if (i >= buffer.byteLength) throw new
|
|
215
|
+
if (i >= buffer.byteLength) throw new BSONError('Bad BSON Document: illegal CString');
|
|
216
|
+
|
|
217
|
+
// Represents the key
|
|
158
218
|
const name = isArray ? arrayIndex++ : buffer.toString('utf8', index, i);
|
|
219
|
+
|
|
220
|
+
// shouldValidateKey is true if the key should be validated, false otherwise
|
|
221
|
+
let shouldValidateKey = true;
|
|
222
|
+
if (globalUTFValidation || utf8KeysSet.has(name)) {
|
|
223
|
+
shouldValidateKey = validationSetting;
|
|
224
|
+
} else {
|
|
225
|
+
shouldValidateKey = !validationSetting;
|
|
226
|
+
}
|
|
227
|
+
|
|
159
228
|
if (isPossibleDBRef !== false && (name as string)[0] === '$') {
|
|
160
229
|
isPossibleDBRef = allowedDBRefKeys.test(name as string);
|
|
161
230
|
}
|
|
@@ -173,20 +242,10 @@ function deserializeObject(
|
|
|
173
242
|
stringSize <= 0 ||
|
|
174
243
|
stringSize > buffer.length - index ||
|
|
175
244
|
buffer[index + stringSize - 1] !== 0
|
|
176
|
-
)
|
|
177
|
-
throw new
|
|
178
|
-
|
|
179
|
-
value = buffer.toString('utf8', index, index + stringSize - 1);
|
|
180
|
-
|
|
181
|
-
for (let i = 0; i < value.length; i++) {
|
|
182
|
-
if (value.charCodeAt(i) === 0xfffd) {
|
|
183
|
-
if (!validateUtf8(buffer, index, index + stringSize - 1)) {
|
|
184
|
-
throw new Error('Invalid UTF-8 string in BSON document');
|
|
185
|
-
}
|
|
186
|
-
break;
|
|
187
|
-
}
|
|
245
|
+
) {
|
|
246
|
+
throw new BSONError('bad string length in bson');
|
|
188
247
|
}
|
|
189
|
-
|
|
248
|
+
value = getValidatedString(buffer, index, index + stringSize - 1, shouldValidateKey);
|
|
190
249
|
index = index + stringSize;
|
|
191
250
|
} else if (elementType === constants.BSON_DATA_OID) {
|
|
192
251
|
const oid = Buffer.alloc(12);
|
|
@@ -222,7 +281,8 @@ function deserializeObject(
|
|
|
222
281
|
(buffer[index++] << 24);
|
|
223
282
|
value = new Date(new Long(lowBits, highBits).toNumber());
|
|
224
283
|
} else if (elementType === constants.BSON_DATA_BOOLEAN) {
|
|
225
|
-
if (buffer[index] !== 0 && buffer[index] !== 1)
|
|
284
|
+
if (buffer[index] !== 0 && buffer[index] !== 1)
|
|
285
|
+
throw new BSONError('illegal boolean type value');
|
|
226
286
|
value = buffer[index++] === 1;
|
|
227
287
|
} else if (elementType === constants.BSON_DATA_OBJECT) {
|
|
228
288
|
const _index = index;
|
|
@@ -232,13 +292,17 @@ function deserializeObject(
|
|
|
232
292
|
(buffer[index + 2] << 16) |
|
|
233
293
|
(buffer[index + 3] << 24);
|
|
234
294
|
if (objectSize <= 0 || objectSize > buffer.length - index)
|
|
235
|
-
throw new
|
|
295
|
+
throw new BSONError('bad embedded document length in bson');
|
|
236
296
|
|
|
237
297
|
// We have a raw value
|
|
238
298
|
if (raw) {
|
|
239
299
|
value = buffer.slice(index, index + objectSize);
|
|
240
300
|
} else {
|
|
241
|
-
|
|
301
|
+
let objectOptions = options;
|
|
302
|
+
if (!globalUTFValidation) {
|
|
303
|
+
objectOptions = { ...options, validation: { utf8: shouldValidateKey } };
|
|
304
|
+
}
|
|
305
|
+
value = deserializeObject(buffer, _index, objectOptions, false);
|
|
242
306
|
}
|
|
243
307
|
|
|
244
308
|
index = index + objectSize;
|
|
@@ -266,12 +330,14 @@ function deserializeObject(
|
|
|
266
330
|
}
|
|
267
331
|
arrayOptions['raw'] = true;
|
|
268
332
|
}
|
|
269
|
-
|
|
333
|
+
if (!globalUTFValidation) {
|
|
334
|
+
arrayOptions = { ...arrayOptions, validation: { utf8: shouldValidateKey } };
|
|
335
|
+
}
|
|
270
336
|
value = deserializeObject(buffer, _index, arrayOptions, true);
|
|
271
337
|
index = index + objectSize;
|
|
272
338
|
|
|
273
|
-
if (buffer[index - 1] !== 0) throw new
|
|
274
|
-
if (index !== stopIndex) throw new
|
|
339
|
+
if (buffer[index - 1] !== 0) throw new BSONError('invalid array terminator byte');
|
|
340
|
+
if (index !== stopIndex) throw new BSONError('corrupted array bson');
|
|
275
341
|
} else if (elementType === constants.BSON_DATA_UNDEFINED) {
|
|
276
342
|
value = undefined;
|
|
277
343
|
} else if (elementType === constants.BSON_DATA_NULL) {
|
|
@@ -323,11 +389,11 @@ function deserializeObject(
|
|
|
323
389
|
const subType = buffer[index++];
|
|
324
390
|
|
|
325
391
|
// Did we have a negative binary size, throw
|
|
326
|
-
if (binarySize < 0) throw new
|
|
392
|
+
if (binarySize < 0) throw new BSONError('Negative binary type element size found');
|
|
327
393
|
|
|
328
394
|
// Is the length longer than the document
|
|
329
395
|
if (binarySize > buffer.byteLength)
|
|
330
|
-
throw new
|
|
396
|
+
throw new BSONError('Binary type size larger than document size');
|
|
331
397
|
|
|
332
398
|
// Decode as raw Buffer object if options specifies it
|
|
333
399
|
if (buffer['slice'] != null) {
|
|
@@ -339,11 +405,11 @@ function deserializeObject(
|
|
|
339
405
|
(buffer[index++] << 16) |
|
|
340
406
|
(buffer[index++] << 24);
|
|
341
407
|
if (binarySize < 0)
|
|
342
|
-
throw new
|
|
408
|
+
throw new BSONError('Negative binary type element size found for subtype 0x02');
|
|
343
409
|
if (binarySize > totalBinarySize - 4)
|
|
344
|
-
throw new
|
|
410
|
+
throw new BSONError('Binary type with subtype 0x02 contains too long binary size');
|
|
345
411
|
if (binarySize < totalBinarySize - 4)
|
|
346
|
-
throw new
|
|
412
|
+
throw new BSONError('Binary type with subtype 0x02 contains too short binary size');
|
|
347
413
|
}
|
|
348
414
|
|
|
349
415
|
if (promoteBuffers && promoteValues) {
|
|
@@ -361,11 +427,11 @@ function deserializeObject(
|
|
|
361
427
|
(buffer[index++] << 16) |
|
|
362
428
|
(buffer[index++] << 24);
|
|
363
429
|
if (binarySize < 0)
|
|
364
|
-
throw new
|
|
430
|
+
throw new BSONError('Negative binary type element size found for subtype 0x02');
|
|
365
431
|
if (binarySize > totalBinarySize - 4)
|
|
366
|
-
throw new
|
|
432
|
+
throw new BSONError('Binary type with subtype 0x02 contains too long binary size');
|
|
367
433
|
if (binarySize < totalBinarySize - 4)
|
|
368
|
-
throw new
|
|
434
|
+
throw new BSONError('Binary type with subtype 0x02 contains too short binary size');
|
|
369
435
|
}
|
|
370
436
|
|
|
371
437
|
// Copy the data
|
|
@@ -390,7 +456,7 @@ function deserializeObject(
|
|
|
390
456
|
i++;
|
|
391
457
|
}
|
|
392
458
|
// If are at the end of the buffer there is a problem with the document
|
|
393
|
-
if (i >= buffer.length) throw new
|
|
459
|
+
if (i >= buffer.length) throw new BSONError('Bad BSON Document: illegal CString');
|
|
394
460
|
// Return the C string
|
|
395
461
|
const source = buffer.toString('utf8', index, i);
|
|
396
462
|
// Create the regexp
|
|
@@ -403,7 +469,7 @@ function deserializeObject(
|
|
|
403
469
|
i++;
|
|
404
470
|
}
|
|
405
471
|
// If are at the end of the buffer there is a problem with the document
|
|
406
|
-
if (i >= buffer.length) throw new
|
|
472
|
+
if (i >= buffer.length) throw new BSONError('Bad BSON Document: illegal CString');
|
|
407
473
|
// Return the C string
|
|
408
474
|
const regExpOptions = buffer.toString('utf8', index, i);
|
|
409
475
|
index = i + 1;
|
|
@@ -435,7 +501,7 @@ function deserializeObject(
|
|
|
435
501
|
i++;
|
|
436
502
|
}
|
|
437
503
|
// If are at the end of the buffer there is a problem with the document
|
|
438
|
-
if (i >= buffer.length) throw new
|
|
504
|
+
if (i >= buffer.length) throw new BSONError('Bad BSON Document: illegal CString');
|
|
439
505
|
// Return the C string
|
|
440
506
|
const source = buffer.toString('utf8', index, i);
|
|
441
507
|
index = i + 1;
|
|
@@ -447,7 +513,7 @@ function deserializeObject(
|
|
|
447
513
|
i++;
|
|
448
514
|
}
|
|
449
515
|
// If are at the end of the buffer there is a problem with the document
|
|
450
|
-
if (i >= buffer.length) throw new
|
|
516
|
+
if (i >= buffer.length) throw new BSONError('Bad BSON Document: illegal CString');
|
|
451
517
|
// Return the C string
|
|
452
518
|
const regExpOptions = buffer.toString('utf8', index, i);
|
|
453
519
|
index = i + 1;
|
|
@@ -464,9 +530,10 @@ function deserializeObject(
|
|
|
464
530
|
stringSize <= 0 ||
|
|
465
531
|
stringSize > buffer.length - index ||
|
|
466
532
|
buffer[index + stringSize - 1] !== 0
|
|
467
|
-
)
|
|
468
|
-
throw new
|
|
469
|
-
|
|
533
|
+
) {
|
|
534
|
+
throw new BSONError('bad string length in bson');
|
|
535
|
+
}
|
|
536
|
+
const symbol = getValidatedString(buffer, index, index + stringSize - 1, shouldValidateKey);
|
|
470
537
|
value = promoteValues ? symbol : new BSONSymbol(symbol);
|
|
471
538
|
index = index + stringSize;
|
|
472
539
|
} else if (elementType === constants.BSON_DATA_TIMESTAMP) {
|
|
@@ -496,9 +563,15 @@ function deserializeObject(
|
|
|
496
563
|
stringSize <= 0 ||
|
|
497
564
|
stringSize > buffer.length - index ||
|
|
498
565
|
buffer[index + stringSize - 1] !== 0
|
|
499
|
-
)
|
|
500
|
-
throw new
|
|
501
|
-
|
|
566
|
+
) {
|
|
567
|
+
throw new BSONError('bad string length in bson');
|
|
568
|
+
}
|
|
569
|
+
const functionString = getValidatedString(
|
|
570
|
+
buffer,
|
|
571
|
+
index,
|
|
572
|
+
index + stringSize - 1,
|
|
573
|
+
shouldValidateKey
|
|
574
|
+
);
|
|
502
575
|
|
|
503
576
|
// If we are evaluating the functions
|
|
504
577
|
if (evalFunctions) {
|
|
@@ -524,7 +597,7 @@ function deserializeObject(
|
|
|
524
597
|
|
|
525
598
|
// Element cannot be shorter than totalSize + stringSize + documentSize + terminator
|
|
526
599
|
if (totalSize < 4 + 4 + 4 + 1) {
|
|
527
|
-
throw new
|
|
600
|
+
throw new BSONError('code_w_scope total size shorter minimum expected length');
|
|
528
601
|
}
|
|
529
602
|
|
|
530
603
|
// Get the code string size
|
|
@@ -538,11 +611,17 @@ function deserializeObject(
|
|
|
538
611
|
stringSize <= 0 ||
|
|
539
612
|
stringSize > buffer.length - index ||
|
|
540
613
|
buffer[index + stringSize - 1] !== 0
|
|
541
|
-
)
|
|
542
|
-
throw new
|
|
614
|
+
) {
|
|
615
|
+
throw new BSONError('bad string length in bson');
|
|
616
|
+
}
|
|
543
617
|
|
|
544
618
|
// Javascript function
|
|
545
|
-
const functionString =
|
|
619
|
+
const functionString = getValidatedString(
|
|
620
|
+
buffer,
|
|
621
|
+
index,
|
|
622
|
+
index + stringSize - 1,
|
|
623
|
+
shouldValidateKey
|
|
624
|
+
);
|
|
546
625
|
// Update parse index position
|
|
547
626
|
index = index + stringSize;
|
|
548
627
|
// Parse the element
|
|
@@ -560,12 +639,12 @@ function deserializeObject(
|
|
|
560
639
|
|
|
561
640
|
// Check if field length is too short
|
|
562
641
|
if (totalSize < 4 + 4 + objectSize + stringSize) {
|
|
563
|
-
throw new
|
|
642
|
+
throw new BSONError('code_w_scope total size is too short, truncating scope');
|
|
564
643
|
}
|
|
565
644
|
|
|
566
645
|
// Check if totalSize field is too long
|
|
567
646
|
if (totalSize > 4 + 4 + objectSize + stringSize) {
|
|
568
|
-
throw new
|
|
647
|
+
throw new BSONError('code_w_scope total size is too long, clips outer document');
|
|
569
648
|
}
|
|
570
649
|
|
|
571
650
|
// If we are evaluating the functions
|
|
@@ -595,10 +674,12 @@ function deserializeObject(
|
|
|
595
674
|
stringSize > buffer.length - index ||
|
|
596
675
|
buffer[index + stringSize - 1] !== 0
|
|
597
676
|
)
|
|
598
|
-
throw new
|
|
677
|
+
throw new BSONError('bad string length in bson');
|
|
599
678
|
// Namespace
|
|
600
|
-
if (
|
|
601
|
-
|
|
679
|
+
if (validation != null && validation.utf8) {
|
|
680
|
+
if (!validateUtf8(buffer, index, index + stringSize - 1)) {
|
|
681
|
+
throw new BSONError('Invalid UTF-8 string in BSON document');
|
|
682
|
+
}
|
|
602
683
|
}
|
|
603
684
|
const namespace = buffer.toString('utf8', index, index + stringSize - 1);
|
|
604
685
|
// Update parse index position
|
|
@@ -615,7 +696,7 @@ function deserializeObject(
|
|
|
615
696
|
// Upgrade to DBRef type
|
|
616
697
|
value = new DBRef(namespace, oid);
|
|
617
698
|
} else {
|
|
618
|
-
throw new
|
|
699
|
+
throw new BSONError(
|
|
619
700
|
'Detected unknown BSON type ' + elementType.toString(16) + ' for fieldname "' + name + '"'
|
|
620
701
|
);
|
|
621
702
|
}
|
|
@@ -633,8 +714,8 @@ function deserializeObject(
|
|
|
633
714
|
|
|
634
715
|
// Check if the deserialization was against a valid array/object
|
|
635
716
|
if (size !== index - startIndex) {
|
|
636
|
-
if (isArray) throw new
|
|
637
|
-
throw new
|
|
717
|
+
if (isArray) throw new BSONError('corrupt array bson');
|
|
718
|
+
throw new BSONError('corrupt object bson');
|
|
638
719
|
}
|
|
639
720
|
|
|
640
721
|
// if we did not find "$ref", "$id", "$db", or found an extraneous $key, don't make a DBRef
|
|
@@ -670,3 +751,24 @@ function isolateEval(
|
|
|
670
751
|
// Set the object
|
|
671
752
|
return functionCache[functionString].bind(object);
|
|
672
753
|
}
|
|
754
|
+
|
|
755
|
+
function getValidatedString(
|
|
756
|
+
buffer: Buffer,
|
|
757
|
+
start: number,
|
|
758
|
+
end: number,
|
|
759
|
+
shouldValidateUtf8: boolean
|
|
760
|
+
) {
|
|
761
|
+
const value = buffer.toString('utf8', start, end);
|
|
762
|
+
// if utf8 validation is on, do the check
|
|
763
|
+
if (shouldValidateUtf8) {
|
|
764
|
+
for (let i = 0; i < value.length; i++) {
|
|
765
|
+
if (value.charCodeAt(i) === 0xfffd) {
|
|
766
|
+
if (!validateUtf8(buffer, start, end)) {
|
|
767
|
+
throw new BSONError('Invalid UTF-8 string in BSON document');
|
|
768
|
+
}
|
|
769
|
+
break;
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
return value;
|
|
774
|
+
}
|