@cloudpss/ubjson 0.5.40 → 0.5.42
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/dist/base/decoder.d.ts +3 -1
- package/dist/base/decoder.d.ts.map +1 -1
- package/dist/base/decoder.js +3 -1
- package/dist/base/decoder.js.map +1 -1
- package/dist/base/encoder.d.ts +2 -0
- package/dist/base/encoder.d.ts.map +1 -1
- package/dist/base/encoder.js +6 -1
- package/dist/base/encoder.js.map +1 -1
- package/dist/encoder.d.ts +2 -1
- package/dist/encoder.d.ts.map +1 -1
- package/dist/encoder.js +2 -1
- package/dist/encoder.js.map +1 -1
- package/dist/helper/decode-ae.d.ts +53 -0
- package/dist/helper/decode-ae.d.ts.map +1 -0
- package/dist/helper/decode-ae.js +590 -0
- package/dist/helper/decode-ae.js.map +1 -0
- package/dist/helper/decode.d.ts +8 -1
- package/dist/helper/decode.d.ts.map +1 -1
- package/dist/helper/decode.js +46 -8
- package/dist/helper/decode.js.map +1 -1
- package/dist/helper/string-decoder.d.ts.map +1 -1
- package/dist/helper/string-decoder.js +92 -114
- package/dist/helper/string-decoder.js.map +1 -1
- package/dist/index.d.ts +6 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -8
- package/dist/index.js.map +1 -1
- package/dist/options.d.ts +22 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +2 -0
- package/dist/options.js.map +1 -0
- package/dist/rxjs/decoder.d.ts +2 -1
- package/dist/rxjs/decoder.d.ts.map +1 -1
- package/dist/rxjs/decoder.js +84 -59
- package/dist/rxjs/decoder.js.map +1 -1
- package/dist/rxjs/encoder.d.ts +2 -1
- package/dist/rxjs/encoder.d.ts.map +1 -1
- package/dist/rxjs/encoder.js +2 -2
- package/dist/rxjs/encoder.js.map +1 -1
- package/dist/rxjs/index.d.ts +1 -0
- package/dist/rxjs/index.d.ts.map +1 -1
- package/dist/stream/decoder.d.ts +2 -1
- package/dist/stream/decoder.d.ts.map +1 -1
- package/dist/stream/decoder.js +2 -2
- package/dist/stream/decoder.js.map +1 -1
- package/dist/stream/encoder.d.ts +3 -2
- package/dist/stream/encoder.d.ts.map +1 -1
- package/dist/stream/encoder.js +2 -2
- package/dist/stream/encoder.js.map +1 -1
- package/dist/stream/index.d.ts +8 -6
- package/dist/stream/index.d.ts.map +1 -1
- package/dist/stream/index.js +12 -12
- package/dist/stream/index.js.map +1 -1
- package/dist/stream-helper/encoder.d.ts +2 -1
- package/dist/stream-helper/encoder.d.ts.map +1 -1
- package/dist/stream-helper/encoder.js +2 -1
- package/dist/stream-helper/encoder.js.map +1 -1
- package/package.json +3 -3
- package/src/base/decoder.ts +5 -1
- package/src/base/encoder.ts +6 -1
- package/src/encoder.ts +3 -1
- package/src/helper/decode-ae.ts +630 -0
- package/src/helper/decode.ts +52 -8
- package/src/helper/string-decoder.ts +87 -102
- package/src/index.ts +11 -8
- package/src/options.ts +22 -0
- package/src/rxjs/decoder.ts +72 -45
- package/src/rxjs/encoder.ts +3 -2
- package/src/rxjs/index.ts +1 -0
- package/src/stream/decoder.ts +3 -2
- package/src/stream/encoder.ts +4 -3
- package/src/stream/index.ts +14 -12
- package/src/stream-helper/encoder.ts +6 -1
- package/tests/.utils.js +10 -1
- package/tests/decode.js +52 -0
- package/tests/e2e/.data.js +11 -0
- package/tests/e2e/stream.js +13 -1
- package/tests/encode.js +1 -1
- package/dist/stream-helper/decoder.d.ts +0 -12
- package/dist/stream-helper/decoder.d.ts.map +0 -1
- package/dist/stream-helper/decoder.js +0 -18
- package/dist/stream-helper/decoder.js.map +0 -1
- package/src/stream-helper/decoder.ts +0 -21
|
@@ -2,6 +2,7 @@ import { constants } from '../helper/constants.js';
|
|
|
2
2
|
import { encode, stringByteLength } from '../helper/string-encoder.js';
|
|
3
3
|
import { EncoderBase } from '../base/encoder.js';
|
|
4
4
|
import type { TypedArrayType } from '../helper/encode.js';
|
|
5
|
+
import type { EncodeOptions } from '../options.js';
|
|
5
6
|
|
|
6
7
|
const BLOCK_SIZE = 1024 * 64; // 64 KiB
|
|
7
8
|
const MAX_SIZE = 1024 * 1024 * 32; // 32 MiB
|
|
@@ -30,8 +31,12 @@ function free(buf: Uint8Array): boolean {
|
|
|
30
31
|
|
|
31
32
|
/** 流式编码 UBJSON */
|
|
32
33
|
export class StreamEncoderHelper extends EncoderBase {
|
|
33
|
-
constructor(
|
|
34
|
+
constructor(
|
|
35
|
+
options: EncodeOptions | null | undefined,
|
|
36
|
+
protected readonly onChunk: (chunk: Uint8Array) => void,
|
|
37
|
+
) {
|
|
34
38
|
super();
|
|
39
|
+
this.sortObjectKeys = options?.sortObjectKeys ?? false;
|
|
35
40
|
this.data = alloc(BLOCK_SIZE);
|
|
36
41
|
this.view = new DataView(this.data.buffer);
|
|
37
42
|
}
|
package/tests/.utils.js
CHANGED
|
@@ -2,6 +2,7 @@ import { resetEnv as resetDecoderEnv } from '../dist/helper/string-decoder.js';
|
|
|
2
2
|
import { resetEnv as resetEncoderEnv } from '../dist/helper/string-encoder.js';
|
|
3
3
|
import { resetEncoder } from '../dist/encoder.js';
|
|
4
4
|
import '../dist/helper/constants.js';
|
|
5
|
+
import '../dist/options.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* 重设所有环境
|
|
@@ -33,5 +34,13 @@ export function toArray(...args) {
|
|
|
33
34
|
* @returns {Uint8Array} Uint8Array
|
|
34
35
|
*/
|
|
35
36
|
export function toBuffer(...args) {
|
|
36
|
-
|
|
37
|
+
const data = [];
|
|
38
|
+
for (const x of args) {
|
|
39
|
+
if (typeof x == 'number') {
|
|
40
|
+
data.push(x);
|
|
41
|
+
} else {
|
|
42
|
+
data.push(...Buffer.from(x, 'ascii'));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return Uint8Array.from(data);
|
|
37
46
|
}
|
package/tests/decode.js
CHANGED
|
@@ -504,3 +504,55 @@ test('decode (eof at marker)', () => {
|
|
|
504
504
|
test('decode (eof at key)', () => {
|
|
505
505
|
expect(() => decode(toBuffer('{', 'i', 2, 'a'))).toThrow(UnexpectedEof);
|
|
506
506
|
});
|
|
507
|
+
|
|
508
|
+
describe('proto poisoning attack', () => {
|
|
509
|
+
it('should remove __proto__ key', () => {
|
|
510
|
+
const obj = /** @type {Record<string, unknown>} */ (
|
|
511
|
+
decode(toBuffer('{', 'i', 9, '__proto__', '{', 'i', 1, 'a', 'S', 'i', 3, 'abc', '}', '}'))
|
|
512
|
+
);
|
|
513
|
+
expect(Object.hasOwn(obj, '__proto__')).toBe(false);
|
|
514
|
+
expect(obj['__proto__']).toBe(Object.prototype);
|
|
515
|
+
});
|
|
516
|
+
it('should allow __proto__ key', () => {
|
|
517
|
+
const obj = /** @type {Record<string, unknown>} */ (
|
|
518
|
+
decode(toBuffer('{', 'i', 9, '__proto__', '{', 'i', 1, 'a', 'S', 'i', 3, 'abc', '}', '}'), {
|
|
519
|
+
protoAction: 'allow',
|
|
520
|
+
})
|
|
521
|
+
);
|
|
522
|
+
expect(Object.hasOwn(obj, '__proto__')).toBe(true);
|
|
523
|
+
expect(obj['__proto__']).toEqual({ a: 'abc' });
|
|
524
|
+
});
|
|
525
|
+
it('should throw on __proto__ key', () => {
|
|
526
|
+
expect(() =>
|
|
527
|
+
decode(toBuffer('{', 'i', 9, '__proto__', '{', 'i', 1, 'a', 'S', 'i', 3, 'abc', '}', '}'), {
|
|
528
|
+
protoAction: 'error',
|
|
529
|
+
}),
|
|
530
|
+
).toThrow(`Unexpected "__proto__"`);
|
|
531
|
+
});
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
describe('constructor poisoning attack', () => {
|
|
535
|
+
it('should remove constructor key', () => {
|
|
536
|
+
const obj = /** @type {Record<string, unknown>} */ (
|
|
537
|
+
decode(toBuffer('{', 'i', 11, 'constructor', '{', 'i', 1, 'a', 'S', 'i', 3, 'abc', '}', '}'), {
|
|
538
|
+
constructorAction: 'remove',
|
|
539
|
+
})
|
|
540
|
+
);
|
|
541
|
+
expect(Object.hasOwn(obj, 'constructor')).toBe(false);
|
|
542
|
+
expect(obj.constructor).toBe(Object);
|
|
543
|
+
});
|
|
544
|
+
it('should allow constructor key', () => {
|
|
545
|
+
const obj = /** @type {Record<string, unknown>} */ (
|
|
546
|
+
decode(toBuffer('{', 'i', 11, 'constructor', '{', 'i', 1, 'a', 'S', 'i', 3, 'abc', '}', '}'))
|
|
547
|
+
);
|
|
548
|
+
expect(Object.hasOwn(obj, 'constructor')).toBe(true);
|
|
549
|
+
expect(obj.constructor).toEqual({ a: 'abc' });
|
|
550
|
+
});
|
|
551
|
+
it('should throw on constructor key', () => {
|
|
552
|
+
expect(() =>
|
|
553
|
+
decode(toBuffer('{', 'i', 11, 'constructor', '{', 'i', 1, 'a', 'S', 'i', 3, 'abc', '}', '}'), {
|
|
554
|
+
constructorAction: 'error',
|
|
555
|
+
}),
|
|
556
|
+
).toThrow(`Unexpected "constructor"`);
|
|
557
|
+
});
|
|
558
|
+
});
|
package/tests/e2e/.data.js
CHANGED
|
@@ -465,6 +465,17 @@ EXPECTED['object with undefined values'] = { a: 1, c: { d: 2, f: null } };
|
|
|
465
465
|
INPUTS['array with undefined values'] = [1, undefined, 2, undefined, 3];
|
|
466
466
|
EXPECTED['array with undefined values'] = [1, null, 2, null, 3];
|
|
467
467
|
|
|
468
|
+
INPUTS['inject __proto__'] = JSON.parse('{"__proto__": {"a":1}}');
|
|
469
|
+
EXPECTED['inject __proto__'] = {};
|
|
470
|
+
|
|
468
471
|
INPUTS['invalid __proto__'] = JSON.parse('{"__proto__":"xxx"}');
|
|
472
|
+
EXPECTED['invalid __proto__'] = {};
|
|
469
473
|
|
|
470
474
|
INPUTS['null __proto__'] = JSON.parse('{"__proto__":null}');
|
|
475
|
+
EXPECTED['null __proto__'] = {};
|
|
476
|
+
|
|
477
|
+
INPUTS['inject constructor'] = { constructor: { prototype: { a: 1 } } };
|
|
478
|
+
INPUTS['invalid constructor prototype'] = { constructor: { prototype: 'xxx' } };
|
|
479
|
+
INPUTS['null constructor prototype'] = { constructor: { prototype: null } };
|
|
480
|
+
INPUTS['invalid constructor'] = { constructor: 'xxx' };
|
|
481
|
+
INPUTS['null constructor'] = { constructor: null };
|
package/tests/e2e/stream.js
CHANGED
|
@@ -13,7 +13,19 @@ describe('stream', () => {
|
|
|
13
13
|
expect(decoded).toEqual(expected);
|
|
14
14
|
}).rejects.toThrow(expected);
|
|
15
15
|
} else {
|
|
16
|
-
const
|
|
16
|
+
const encoded = await encodeStream(input).toArray();
|
|
17
|
+
const data = encoded.flatMap((/** @type {Buffer} */ chunk) => {
|
|
18
|
+
// split to random chunks
|
|
19
|
+
const chunks = [];
|
|
20
|
+
let offset = 0;
|
|
21
|
+
while (offset < chunk.length) {
|
|
22
|
+
const size = Math.floor(Math.random() * chunk.length);
|
|
23
|
+
chunks.push(chunk.subarray(offset, offset + size));
|
|
24
|
+
offset += size;
|
|
25
|
+
}
|
|
26
|
+
return chunks;
|
|
27
|
+
});
|
|
28
|
+
const decoded = await decodeStream(Readable.from(data));
|
|
17
29
|
expect(decoded).toEqual(expected);
|
|
18
30
|
}
|
|
19
31
|
});
|
package/tests/encode.js
CHANGED
|
@@ -408,7 +408,7 @@ test('encode object', () => {
|
|
|
408
408
|
});
|
|
409
409
|
|
|
410
410
|
test('encode object (keep order)', () => {
|
|
411
|
-
expect(toArray(encode({ b: 2, a: 1, c: 3 }))).toEqual(
|
|
411
|
+
expect(toArray(encode({ b: 2, a: 1, c: 3 }, { sortObjectKeys: true }))).toEqual(
|
|
412
412
|
toArray('{', 'i', 1, 'a', 'U', 1, 'i', 1, 'b', 'U', 2, 'i', 1, 'c', 'U', 3, '}'),
|
|
413
413
|
);
|
|
414
414
|
// @ts-expect-error Access private property
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { DecoderBase } from '../base/decoder.js';
|
|
2
|
-
/** 未结束的流 */
|
|
3
|
-
export declare const kEof: unique symbol;
|
|
4
|
-
/** 流式解码 UBJSON */
|
|
5
|
-
export declare class StreamDecoderHelper extends DecoderBase {
|
|
6
|
-
/** @inheritdoc */
|
|
7
|
-
protected eof(): never;
|
|
8
|
-
/** 读取的字节数 */
|
|
9
|
-
get readLength(): number;
|
|
10
|
-
}
|
|
11
|
-
export { UnexpectedEofError as UnexpectedEof } from '../helper/errors.js';
|
|
12
|
-
//# sourceMappingURL=decoder.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"decoder.d.ts","sourceRoot":"","sources":["../../src/stream-helper/decoder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,YAAY;AACZ,eAAO,MAAM,IAAI,eAAgB,CAAC;AAElC,kBAAkB;AAClB,qBAAa,mBAAoB,SAAQ,WAAW;IAChD,kBAAkB;cACC,GAAG,IAAI,KAAK;IAM/B,aAAa;IACb,IAAI,UAAU,IAAI,MAAM,CAEvB;CACJ;AAED,OAAO,EAAE,kBAAkB,IAAI,aAAa,EAAE,MAAM,qBAAqB,CAAC"}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { DecoderBase } from '../base/decoder.js';
|
|
2
|
-
/** 未结束的流 */
|
|
3
|
-
export const kEof = Symbol('EOF');
|
|
4
|
-
/** 流式解码 UBJSON */
|
|
5
|
-
export class StreamDecoderHelper extends DecoderBase {
|
|
6
|
-
/** @inheritdoc */
|
|
7
|
-
eof() {
|
|
8
|
-
// 性能优化,避免 new Error 的开销
|
|
9
|
-
// eslint-disable-next-line @typescript-eslint/only-throw-error
|
|
10
|
-
throw kEof;
|
|
11
|
-
}
|
|
12
|
-
/** 读取的字节数 */
|
|
13
|
-
get readLength() {
|
|
14
|
-
return this.offset;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
export { UnexpectedEofError as UnexpectedEof } from '../helper/errors.js';
|
|
18
|
-
//# sourceMappingURL=decoder.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"decoder.js","sourceRoot":"","sources":["../../src/stream-helper/decoder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,YAAY;AACZ,MAAM,CAAC,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;AAElC,kBAAkB;AAClB,MAAM,OAAO,mBAAoB,SAAQ,WAAW;IAChD,kBAAkB;IACC,GAAG;QAClB,wBAAwB;QACxB,+DAA+D;QAC/D,MAAM,IAAI,CAAC;IACf,CAAC;IAED,aAAa;IACb,IAAI,UAAU;QACV,OAAO,IAAI,CAAC,MAAM,CAAC;IACvB,CAAC;CACJ;AAED,OAAO,EAAE,kBAAkB,IAAI,aAAa,EAAE,MAAM,qBAAqB,CAAC"}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { DecoderBase } from '../base/decoder.js';
|
|
2
|
-
|
|
3
|
-
/** 未结束的流 */
|
|
4
|
-
export const kEof = Symbol('EOF');
|
|
5
|
-
|
|
6
|
-
/** 流式解码 UBJSON */
|
|
7
|
-
export class StreamDecoderHelper extends DecoderBase {
|
|
8
|
-
/** @inheritdoc */
|
|
9
|
-
protected override eof(): never {
|
|
10
|
-
// 性能优化,避免 new Error 的开销
|
|
11
|
-
// eslint-disable-next-line @typescript-eslint/only-throw-error
|
|
12
|
-
throw kEof;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/** 读取的字节数 */
|
|
16
|
-
get readLength(): number {
|
|
17
|
-
return this.offset;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export { UnexpectedEofError as UnexpectedEof } from '../helper/errors.js';
|