@cloudpss/ubjson 0.5.41 → 0.5.43

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.
Files changed (58) hide show
  1. package/dist/base/decoder.d.ts +3 -1
  2. package/dist/base/decoder.d.ts.map +1 -1
  3. package/dist/base/decoder.js +3 -1
  4. package/dist/base/decoder.js.map +1 -1
  5. package/dist/helper/decode-ae.d.ts +4 -1
  6. package/dist/helper/decode-ae.d.ts.map +1 -1
  7. package/dist/helper/decode-ae.js +16 -9
  8. package/dist/helper/decode-ae.js.map +1 -1
  9. package/dist/helper/decode.d.ts +8 -1
  10. package/dist/helper/decode.d.ts.map +1 -1
  11. package/dist/helper/decode.js +40 -3
  12. package/dist/helper/decode.js.map +1 -1
  13. package/dist/helper/string-decoder.d.ts.map +1 -1
  14. package/dist/helper/string-decoder.js +92 -114
  15. package/dist/helper/string-decoder.js.map +1 -1
  16. package/dist/helper/utils.d.ts +1 -1
  17. package/dist/helper/utils.d.ts.map +1 -1
  18. package/dist/helper/utils.js.map +1 -1
  19. package/dist/index.d.ts +4 -4
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +4 -4
  22. package/dist/index.js.map +1 -1
  23. package/dist/options.d.ts +17 -1
  24. package/dist/options.d.ts.map +1 -1
  25. package/dist/rxjs/decoder.d.ts +2 -1
  26. package/dist/rxjs/decoder.d.ts.map +1 -1
  27. package/dist/rxjs/decoder.js +82 -83
  28. package/dist/rxjs/decoder.js.map +1 -1
  29. package/dist/rxjs/index.d.ts +1 -1
  30. package/dist/rxjs/index.d.ts.map +1 -1
  31. package/dist/stream/decoder.d.ts +2 -1
  32. package/dist/stream/decoder.d.ts.map +1 -1
  33. package/dist/stream/decoder.js +2 -2
  34. package/dist/stream/decoder.js.map +1 -1
  35. package/dist/stream/index.d.ts +5 -5
  36. package/dist/stream/index.d.ts.map +1 -1
  37. package/dist/stream/index.js +6 -6
  38. package/dist/stream/index.js.map +1 -1
  39. package/package.json +2 -2
  40. package/src/base/decoder.ts +5 -1
  41. package/src/helper/decode-ae.ts +19 -9
  42. package/src/helper/decode.ts +47 -3
  43. package/src/helper/string-decoder.ts +87 -102
  44. package/src/helper/utils.ts +1 -1
  45. package/src/index.ts +6 -6
  46. package/src/options.ts +18 -1
  47. package/src/rxjs/decoder.ts +7 -6
  48. package/src/rxjs/index.ts +1 -1
  49. package/src/stream/decoder.ts +4 -3
  50. package/src/stream/index.ts +8 -8
  51. package/tests/.utils.js +9 -1
  52. package/tests/decode.js +52 -0
  53. package/tests/e2e/.data.js +11 -0
  54. package/dist/stream-helper/decoder.d.ts +0 -12
  55. package/dist/stream-helper/decoder.d.ts.map +0 -1
  56. package/dist/stream-helper/decoder.js +0 -18
  57. package/dist/stream-helper/decoder.js.map +0 -1
  58. package/src/stream-helper/decoder.ts +0 -21
@@ -1,3 +1,4 @@
1
+ import type { DecodeOptions } from '../options.js';
1
2
  import { constants } from './constants.js';
2
3
  import { UnexpectedEofError, unsupportedType } from './errors.js';
3
4
  import { decode } from './string-decoder.js';
@@ -15,10 +16,12 @@ export interface DecodeCursor {
15
16
  offset: number;
16
17
  /** 读取到末尾时调用 */
17
18
  eof(): never;
19
+ /** 选项 */
20
+ readonly options: DecodeOptions | undefined;
18
21
  }
19
22
 
20
23
  /** 创建数据包装 */
21
- export function DecodeCursor(data: BinaryData): DecodeCursor {
24
+ export function DecodeCursor(data: BufferSource, options?: DecodeOptions): DecodeCursor {
22
25
  const d = toUint8Array(data);
23
26
  const v = new DataView(d.buffer, d.byteOffset, d.byteLength);
24
27
  return {
@@ -28,6 +31,7 @@ export function DecodeCursor(data: BinaryData): DecodeCursor {
28
31
  eof() {
29
32
  throw new UnexpectedEofError();
30
33
  },
34
+ options,
31
35
  };
32
36
  }
33
37
 
@@ -160,6 +164,35 @@ export function read(cursor: DecodeCursor): unknown {
160
164
  return readData(cursor, marker);
161
165
  }
162
166
 
167
+ /** 处理 `__proto__` */
168
+ export function protoAction(cursor: Pick<DecodeCursor, 'options'>, obj: Record<string, unknown>, value: unknown): void {
169
+ if (cursor.options?.protoAction === 'error') {
170
+ throw new Error('Unexpected "__proto__"');
171
+ } else if (cursor.options?.protoAction === 'allow') {
172
+ defineProperty(obj, '__proto__', {
173
+ value,
174
+ enumerable: true,
175
+ configurable: true,
176
+ writable: true,
177
+ });
178
+ }
179
+ }
180
+
181
+ /** 处理 `constructor` */
182
+ export function constructorAction(
183
+ cursor: Pick<DecodeCursor, 'options'>,
184
+ obj: Record<string, unknown>,
185
+ value: unknown,
186
+ ): void {
187
+ if (cursor.options?.constructorAction === 'error') {
188
+ throw new Error('Unexpected "constructor"');
189
+ } else if (cursor.options?.constructorAction === 'remove') {
190
+ return;
191
+ }
192
+ // eslint-disable-next-line @typescript-eslint/dot-notation
193
+ obj['constructor'] = value;
194
+ }
195
+
163
196
  /** 读取优化对象数据 */
164
197
  function readObjectOptimizedData(cursor: DecodeCursor, marker: OptimizedFormatMarkers): unknown {
165
198
  const { count, type } = marker;
@@ -168,7 +201,12 @@ function readObjectOptimizedData(cursor: DecodeCursor, marker: OptimizedFormatMa
168
201
  const key = readKey(cursor);
169
202
  const value = readData(cursor, type ?? readMarker(cursor));
170
203
  if (key === '__proto__') {
171
- defineProperty(object, key, { value, enumerable: true, configurable: true, writable: true });
204
+ protoAction(cursor, object, value);
205
+ continue;
206
+ }
207
+ if (key === 'constructor') {
208
+ constructorAction(cursor, object, value);
209
+ continue;
172
210
  }
173
211
  object[key] = value;
174
212
  }
@@ -198,7 +236,12 @@ export function readData(cursor: DecodeCursor, marker: number): unknown {
198
236
  const key = readKey(cursor);
199
237
  const value = read(cursor);
200
238
  if (key === '__proto__') {
201
- defineProperty(object, key, { value, enumerable: true, configurable: true, writable: true });
239
+ protoAction(cursor, object, value);
240
+ continue;
241
+ }
242
+ if (key === 'constructor') {
243
+ constructorAction(cursor, object, value);
244
+ continue;
202
245
  }
203
246
  object[key] = value;
204
247
  }
@@ -286,6 +329,7 @@ export function readData(cursor: DecodeCursor, marker: number): unknown {
286
329
  return Array.from({ length: count }).fill(true);
287
330
  case constants.FALSE:
288
331
  return Array.from({ length: count }).fill(false);
332
+ case undefined:
289
333
  default:
290
334
  break;
291
335
  }
@@ -69,118 +69,103 @@ export function shortStringInJS(buf: Uint8Array, begin: number, length: number):
69
69
  if (length < 4) {
70
70
  if (length < 2) {
71
71
  if (length === 0) return '';
72
- else {
73
- const a = buf[begin++]!;
74
- if ((a & 0x80) > 1) {
75
- return;
76
- }
77
- return fromCharCode(a);
78
- }
79
- } else {
80
- const a = buf[begin++]!;
81
- const b = buf[begin++]!;
82
- if ((a & 0x80) > 0 || (b & 0x80) > 0) {
72
+ const a = buf[begin]!;
73
+ if ((a & 0x80) > 0) {
83
74
  return;
84
75
  }
85
- if (length < 3) return fromCharCode(a, b);
86
- const c = buf[begin++]!;
87
- if ((c & 0x80) > 0) {
88
- return;
89
- }
90
- return fromCharCode(a, b, c);
76
+ return fromCharCode(a);
91
77
  }
92
- } else {
93
78
  const a = buf[begin++]!;
94
79
  const b = buf[begin++]!;
80
+ if ((a & 0x80) > 0 || (b & 0x80) > 0) {
81
+ return;
82
+ }
83
+ if (length < 3) return fromCharCode(a, b);
95
84
  const c = buf[begin++]!;
96
- const d = buf[begin++]!;
97
- if ((a & 0x80) > 0 || (b & 0x80) > 0 || (c & 0x80) > 0 || (d & 0x80) > 0) {
85
+ if ((c & 0x80) > 0) {
98
86
  return;
99
87
  }
100
- if (length < 6) {
101
- if (length === 4) return fromCharCode(a, b, c, d);
102
- else {
103
- const e = buf[begin++]!;
104
- if ((e & 0x80) > 0) {
105
- return;
106
- }
107
- return fromCharCode(a, b, c, d, e);
108
- }
109
- } else if (length < 8) {
110
- const e = buf[begin++]!;
111
- const f = buf[begin++]!;
112
- if ((e & 0x80) > 0 || (f & 0x80) > 0) {
113
- return;
114
- }
115
- if (length < 7) return fromCharCode(a, b, c, d, e, f);
116
- const g = buf[begin++]!;
117
- if ((g & 0x80) > 0) {
118
- return;
119
- }
120
- return fromCharCode(a, b, c, d, e, f, g);
121
- } else {
122
- const e = buf[begin++]!;
123
- const f = buf[begin++]!;
124
- const g = buf[begin++]!;
125
- const h = buf[begin++]!;
126
- if ((e & 0x80) > 0 || (f & 0x80) > 0 || (g & 0x80) > 0 || (h & 0x80) > 0) {
127
- return;
128
- }
129
- if (length < 10) {
130
- if (length === 8) return fromCharCode(a, b, c, d, e, f, g, h);
131
- else {
132
- const i = buf[begin++]!;
133
- if ((i & 0x80) > 0) {
134
- return;
135
- }
136
- return fromCharCode(a, b, c, d, e, f, g, h, i);
137
- }
138
- } else if (length < 12) {
139
- const i = buf[begin++]!;
140
- const j = buf[begin++]!;
141
- if ((i & 0x80) > 0 || (j & 0x80) > 0) {
142
- return;
143
- }
144
- if (length < 11) return fromCharCode(a, b, c, d, e, f, g, h, i, j);
145
- const k = buf[begin++]!;
146
- if ((k & 0x80) > 0) {
147
- return;
148
- }
149
- return fromCharCode(a, b, c, d, e, f, g, h, i, j, k);
150
- } else {
151
- const i = buf[begin++]!;
152
- const j = buf[begin++]!;
153
- const k = buf[begin++]!;
154
- const l = buf[begin++]!;
155
- if ((i & 0x80) > 0 || (j & 0x80) > 0 || (k & 0x80) > 0 || (l & 0x80) > 0) {
156
- return;
157
- }
158
- if (length < 14) {
159
- if (length === 12) return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l);
160
- else {
161
- const m = buf[begin++]!;
162
- if ((m & 0x80) > 0) {
163
- begin -= 13;
164
- return;
165
- }
166
- return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m);
167
- }
168
- } else {
169
- const m = buf[begin++]!;
170
- const n = buf[begin++]!;
171
- if ((m & 0x80) > 0 || (n & 0x80) > 0) {
172
- return;
173
- }
174
- if (length < 15) return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m, n);
175
- const o = buf[begin++]!;
176
- if ((o & 0x80) > 0) {
177
- return;
178
- }
179
- return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o);
180
- }
181
- }
88
+ return fromCharCode(a, b, c);
89
+ }
90
+ const a = buf[begin++]!;
91
+ const b = buf[begin++]!;
92
+ const c = buf[begin++]!;
93
+ const d = buf[begin++]!;
94
+ if ((a & 0x80) > 0 || (b & 0x80) > 0 || (c & 0x80) > 0 || (d & 0x80) > 0) {
95
+ return;
96
+ }
97
+ if (length < 6) {
98
+ if (length === 4) return fromCharCode(a, b, c, d);
99
+ const e = buf[begin++]!;
100
+ if ((e & 0x80) > 0) {
101
+ return;
182
102
  }
103
+ return fromCharCode(a, b, c, d, e);
104
+ }
105
+ const e = buf[begin++]!;
106
+ const f = buf[begin++]!;
107
+ if ((e & 0x80) > 0 || (f & 0x80) > 0) {
108
+ return;
109
+ }
110
+ if (length < 8) {
111
+ if (length < 7) return fromCharCode(a, b, c, d, e, f);
112
+ const g = buf[begin++]!;
113
+ if ((g & 0x80) > 0) {
114
+ return;
115
+ }
116
+ return fromCharCode(a, b, c, d, e, f, g);
117
+ }
118
+ const g = buf[begin++]!;
119
+ const h = buf[begin++]!;
120
+ if ((g & 0x80) > 0 || (h & 0x80) > 0) {
121
+ return;
122
+ }
123
+ if (length < 10) {
124
+ if (length === 8) return fromCharCode(a, b, c, d, e, f, g, h);
125
+ const i = buf[begin++]!;
126
+ if ((i & 0x80) > 0) {
127
+ return;
128
+ }
129
+ return fromCharCode(a, b, c, d, e, f, g, h, i);
130
+ }
131
+ const i = buf[begin++]!;
132
+ const j = buf[begin++]!;
133
+ if ((i & 0x80) > 0 || (j & 0x80) > 0) {
134
+ return;
135
+ }
136
+ if (length < 12) {
137
+ if (length < 11) return fromCharCode(a, b, c, d, e, f, g, h, i, j);
138
+ const k = buf[begin++]!;
139
+ if ((k & 0x80) > 0) {
140
+ return;
141
+ }
142
+ return fromCharCode(a, b, c, d, e, f, g, h, i, j, k);
143
+ }
144
+ const k = buf[begin++]!;
145
+ const l = buf[begin++]!;
146
+ if ((k & 0x80) > 0 || (l & 0x80) > 0) {
147
+ return;
148
+ }
149
+ if (length < 14) {
150
+ if (length === 12) return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l);
151
+ const m = buf[begin++]!;
152
+ if ((m & 0x80) > 0) {
153
+ begin -= 13;
154
+ return;
155
+ }
156
+ return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m);
157
+ }
158
+ const m = buf[begin++]!;
159
+ const n = buf[begin++]!;
160
+ if ((m & 0x80) > 0 || (n & 0x80) > 0) {
161
+ return;
162
+ }
163
+ if (length < 15) return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m, n);
164
+ const o = buf[begin++]!;
165
+ if ((o & 0x80) > 0) {
166
+ return;
183
167
  }
168
+ return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o);
184
169
  }
185
170
 
186
171
  let TEXT_DECODER: TextDecoder | null;
@@ -1,5 +1,5 @@
1
1
  /** 支持的数据转为 Uint8Array */
2
- export function toUint8Array(data: BinaryData): Uint8Array {
2
+ export function toUint8Array(data: BufferSource): Uint8Array {
3
3
  if (data == null || typeof data != 'object' || typeof data.byteLength != 'number') {
4
4
  throw new TypeError('Invalid data');
5
5
  }
package/src/index.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { getEncoder } from './encoder.js';
2
2
  import { Decoder } from './decoder.js';
3
- import type { EncodeOptions } from './options.js';
3
+ import type { EncodeOptions, DecodeOptions } from './options.js';
4
4
 
5
5
  export { UnexpectedEofError as UnexpectedEof } from './helper/errors.js';
6
- export type { EncodeOptions };
6
+ export type { EncodeOptions, DecodeOptions };
7
7
 
8
8
  /** 编码为 UBJSON */
9
9
  export function encode(value: unknown, options?: EncodeOptions): Uint8Array {
@@ -16,14 +16,14 @@ export function encodeMany(value: Iterable<unknown>, options?: EncodeOptions): U
16
16
  }
17
17
 
18
18
  /** 解码 UBJSON */
19
- export function decode(value: BinaryData): unknown {
20
- const decoder = new Decoder(value);
19
+ export function decode(value: BufferSource, options?: DecodeOptions): unknown {
20
+ const decoder = new Decoder(value, options);
21
21
  return decoder.decode();
22
22
  }
23
23
 
24
24
  /** 解码 UBJSON */
25
- export function* decodeMany(value: BinaryData): Iterable<unknown> {
26
- const decoder = new Decoder(value);
25
+ export function* decodeMany(value: BufferSource, options?: DecodeOptions): Iterable<unknown> {
26
+ const decoder = new Decoder(value, options);
27
27
  while (!decoder.ended) {
28
28
  yield decoder.decode();
29
29
  }
package/src/options.ts CHANGED
@@ -1,5 +1,22 @@
1
1
  /** 序列化选项 */
2
2
  export interface EncodeOptions {
3
- /** 序列化对象时排序属性 */
3
+ /**
4
+ * 序列化对象时排序属性
5
+ * @default false
6
+ */
4
7
  sortObjectKeys?: boolean;
5
8
  }
9
+
10
+ /** 反序列化选项 */
11
+ export interface DecodeOptions {
12
+ /**
13
+ * 解析对象时对 `__proto__` 属性的处理方式
14
+ * @default 'remove'
15
+ */
16
+ protoAction?: 'error' | 'allow' | 'remove';
17
+ /**
18
+ * 解析对象时对 `constructor` 属性的处理方式
19
+ * @default 'allow'
20
+ */
21
+ constructorAction?: 'error' | 'allow' | 'remove';
22
+ }
@@ -1,15 +1,16 @@
1
1
  import { Observable, type OperatorFunction } from 'rxjs';
2
- import { UnexpectedEof } from '../stream-helper/decoder.js';
3
2
  import { toUint8Array } from '../helper/utils.js';
4
3
  import { read } from '../helper/decode-ae.js';
4
+ import type { DecodeOptions } from '../options.js';
5
+ import { UnexpectedEofError } from '../helper/errors.js';
5
6
 
6
7
  const EMPTY_BUFFER = new Uint8Array(0);
7
8
  const EMPTY_VIEW = new DataView(EMPTY_BUFFER.buffer);
8
9
 
9
10
  /** 流式解码 UBJSON */
10
- export function decode(): OperatorFunction<BinaryData, unknown> {
11
- return (observable) => {
12
- return new Observable<unknown>((subscriber) => {
11
+ export function decode(options?: DecodeOptions): OperatorFunction<BufferSource, unknown> {
12
+ return (observable) =>
13
+ new Observable<unknown>((subscriber) => {
13
14
  const data = EMPTY_BUFFER;
14
15
  const cursor = {
15
16
  view: EMPTY_VIEW,
@@ -17,6 +18,7 @@ export function decode(): OperatorFunction<BinaryData, unknown> {
17
18
  capacity: 0,
18
19
  size: 0,
19
20
  offset: 0,
21
+ options,
20
22
  };
21
23
  /** reader 返回的还需接收的字节数 */
22
24
  let required = 1;
@@ -84,7 +86,7 @@ export function decode(): OperatorFunction<BinaryData, unknown> {
84
86
  },
85
87
  complete() {
86
88
  if (cursor.size > cursor.offset) {
87
- subscriber.error(new UnexpectedEof());
89
+ subscriber.error(new UnexpectedEofError());
88
90
  } else {
89
91
  subscriber.complete();
90
92
  }
@@ -93,5 +95,4 @@ export function decode(): OperatorFunction<BinaryData, unknown> {
93
95
  },
94
96
  });
95
97
  });
96
- };
97
98
  }
package/src/rxjs/index.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export { encode } from './encoder.js';
2
2
  export { decode } from './decoder.js';
3
3
  export { UnexpectedEofError as UnexpectedEof } from '../helper/errors.js';
4
- export type { EncodeOptions } from '../options.js';
4
+ export type { EncodeOptions, DecodeOptions } from '../options.js';
@@ -1,16 +1,17 @@
1
1
  import { Transform, type TransformCallback } from 'node:stream';
2
2
  import { Subject } from 'rxjs';
3
3
  import { decode } from '../rxjs/decoder.js';
4
+ import type { DecodeOptions } from '../options.js';
4
5
 
5
6
  /** 流式解码 UBJSON */
6
7
  export class StreamDecoder extends Transform {
7
- constructor() {
8
+ constructor(options?: DecodeOptions) {
8
9
  super({
9
10
  readableObjectMode: true,
10
11
  writableObjectMode: false,
11
12
  });
12
- this.buffer = new Subject<BinaryData>();
13
- this.buffer.pipe(decode()).subscribe({
13
+ this.buffer = new Subject<BufferSource>();
14
+ this.buffer.pipe(decode(options)).subscribe({
14
15
  next: (value) => {
15
16
  // null is not allowed in a stream
16
17
  if (value == null) return;
@@ -1,10 +1,10 @@
1
1
  import { Readable, type Transform } from 'node:stream';
2
2
  import { StreamEncoder } from './encoder.js';
3
3
  import { StreamDecoder } from './decoder.js';
4
- import type { EncodeOptions } from '../options.js';
4
+ import type { EncodeOptions, DecodeOptions } from '../options.js';
5
5
 
6
6
  export { UnexpectedEofError as UnexpectedEof } from '../helper/errors.js';
7
- export type { EncodeOptions };
7
+ export type { EncodeOptions, DecodeOptions };
8
8
 
9
9
  /** 编码为 UBJSON */
10
10
  export function encode(value: unknown, options?: EncodeOptions): Readable {
@@ -39,9 +39,9 @@ export function encoder(options?: EncodeOptions): Transform {
39
39
  }
40
40
 
41
41
  /** 解码 UBJSON */
42
- export async function decode(stream: NodeJS.ReadableStream): Promise<unknown> {
42
+ export async function decode(stream: NodeJS.ReadableStream, options?: DecodeOptions): Promise<unknown> {
43
43
  return new Promise((resolve, reject) => {
44
- const decoder = new StreamDecoder();
44
+ const decoder = new StreamDecoder(options);
45
45
  decoder.on('error', reject);
46
46
  decoder.on('end', resolve);
47
47
  decoder.on('data', resolve);
@@ -50,12 +50,12 @@ export async function decode(stream: NodeJS.ReadableStream): Promise<unknown> {
50
50
  }
51
51
 
52
52
  /** 解码 UBJSON */
53
- export function decodeMany(stream: NodeJS.ReadableStream): AsyncIterable<unknown> {
54
- const decoder = new StreamDecoder();
53
+ export function decodeMany(stream: NodeJS.ReadableStream, options?: DecodeOptions): AsyncIterable<unknown> {
54
+ const decoder = new StreamDecoder(options);
55
55
  return stream.pipe(decoder);
56
56
  }
57
57
 
58
58
  /** 解码 UBJSON */
59
- export function decoder(): Transform {
60
- return new StreamDecoder();
59
+ export function decoder(options?: DecodeOptions): Transform {
60
+ return new StreamDecoder(options);
61
61
  }
package/tests/.utils.js CHANGED
@@ -34,5 +34,13 @@ export function toArray(...args) {
34
34
  * @returns {Uint8Array} Uint8Array
35
35
  */
36
36
  export function toBuffer(...args) {
37
- return Uint8Array.from(args, (x) => (typeof x == 'number' ? x : x.charCodeAt(0)));
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);
38
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
+ });
@@ -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 };
@@ -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';