@bsv/sdk 1.9.16 → 1.9.17
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/cjs/package.json +1 -1
- package/dist/cjs/src/primitives/utils.js +22 -15
- package/dist/cjs/src/primitives/utils.js.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/src/primitives/utils.js +22 -15
- package/dist/esm/src/primitives/utils.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/src/primitives/utils.d.ts.map +1 -1
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/dist/umd/bundle.js +1 -1
- package/dist/umd/bundle.js.map +1 -1
- package/docs/reference/primitives.md +19 -12
- package/package.json +1 -1
- package/src/primitives/__tests/utils.test.ts +43 -0
- package/src/primitives/utils.ts +31 -16
|
@@ -6099,25 +6099,31 @@ toUTF8 = (arr: number[]): string => {
|
|
|
6099
6099
|
}
|
|
6100
6100
|
if (byte <= 127) {
|
|
6101
6101
|
result += String.fromCharCode(byte);
|
|
6102
|
+
continue;
|
|
6102
6103
|
}
|
|
6103
|
-
|
|
6104
|
-
const
|
|
6105
|
-
|
|
6104
|
+
if (byte >= 192 && byte <= 223) {
|
|
6105
|
+
const avail = arr.length - (i + 1);
|
|
6106
|
+
const byte2 = avail >= 1 ? arr[i + 1] : 0;
|
|
6107
|
+
skip = Math.min(1, avail);
|
|
6106
6108
|
const codePoint = ((byte & 31) << 6) | (byte2 & 63);
|
|
6107
6109
|
result += String.fromCharCode(codePoint);
|
|
6110
|
+
continue;
|
|
6108
6111
|
}
|
|
6109
|
-
|
|
6110
|
-
const
|
|
6111
|
-
const
|
|
6112
|
-
|
|
6112
|
+
if (byte >= 224 && byte <= 239) {
|
|
6113
|
+
const avail = arr.length - (i + 1);
|
|
6114
|
+
const byte2 = avail >= 1 ? arr[i + 1] : 0;
|
|
6115
|
+
const byte3 = avail >= 2 ? arr[i + 2] : 0;
|
|
6116
|
+
skip = Math.min(2, avail);
|
|
6113
6117
|
const codePoint = ((byte & 15) << 12) | ((byte2 & 63) << 6) | (byte3 & 63);
|
|
6114
6118
|
result += String.fromCharCode(codePoint);
|
|
6119
|
+
continue;
|
|
6115
6120
|
}
|
|
6116
|
-
|
|
6117
|
-
const
|
|
6118
|
-
const
|
|
6119
|
-
const
|
|
6120
|
-
|
|
6121
|
+
if (byte >= 240 && byte <= 247) {
|
|
6122
|
+
const avail = arr.length - (i + 1);
|
|
6123
|
+
const byte2 = avail >= 1 ? arr[i + 1] : 0;
|
|
6124
|
+
const byte3 = avail >= 2 ? arr[i + 2] : 0;
|
|
6125
|
+
const byte4 = avail >= 3 ? arr[i + 3] : 0;
|
|
6126
|
+
skip = Math.min(3, avail);
|
|
6121
6127
|
const codePoint = ((byte & 7) << 18) |
|
|
6122
6128
|
((byte2 & 63) << 12) |
|
|
6123
6129
|
((byte3 & 63) << 6) |
|
|
@@ -6125,6 +6131,7 @@ toUTF8 = (arr: number[]): string => {
|
|
|
6125
6131
|
const surrogate1 = 55296 + ((codePoint - 65536) >> 10);
|
|
6126
6132
|
const surrogate2 = 56320 + ((codePoint - 65536) & 1023);
|
|
6127
6133
|
result += String.fromCharCode(surrogate1, surrogate2);
|
|
6134
|
+
continue;
|
|
6128
6135
|
}
|
|
6129
6136
|
}
|
|
6130
6137
|
return result;
|
package/package.json
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
zero2,
|
|
5
5
|
toHex,
|
|
6
6
|
encode,
|
|
7
|
+
toUTF8,
|
|
7
8
|
fromBase58,
|
|
8
9
|
toBase58,
|
|
9
10
|
fromBase58Check,
|
|
@@ -209,6 +210,48 @@ describe('utils', () => {
|
|
|
209
210
|
})
|
|
210
211
|
})
|
|
211
212
|
|
|
213
|
+
describe('toUTF8 bounds checks', () => {
|
|
214
|
+
const guarded = (arr: number[]): number[] => {
|
|
215
|
+
const target = arr.slice()
|
|
216
|
+
const handler: ProxyHandler<number[]> = {
|
|
217
|
+
get (t, prop, receiver) {
|
|
218
|
+
if (prop === 'length' || typeof prop !== 'string') {
|
|
219
|
+
return Reflect.get(t, prop as any, receiver)
|
|
220
|
+
}
|
|
221
|
+
const idx = Number(prop)
|
|
222
|
+
if (Number.isInteger(idx)) {
|
|
223
|
+
if (idx < 0 || idx >= t.length) {
|
|
224
|
+
throw new Error(`out-of-bounds read at index ${idx} (length ${t.length})`)
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return Reflect.get(t, prop as any, receiver)
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return new Proxy(target, handler) as unknown as number[]
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
it('does not access out-of-bounds on truncated 2-byte sequence', () => {
|
|
234
|
+
const input = guarded([0xC3])
|
|
235
|
+
expect(() => toUTF8(input)).not.toThrow()
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
it('does not access out-of-bounds on truncated 3-byte sequences', () => {
|
|
239
|
+
const input1 = guarded([0xE2])
|
|
240
|
+
const input2 = guarded([0xE2, 0x82])
|
|
241
|
+
expect(() => toUTF8(input1)).not.toThrow()
|
|
242
|
+
expect(() => toUTF8(input2)).not.toThrow()
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
it('does not access out-of-bounds on truncated 4-byte sequences', () => {
|
|
246
|
+
const input1 = guarded([0xF0])
|
|
247
|
+
const input2 = guarded([0xF0, 0x9F])
|
|
248
|
+
const input3 = guarded([0xF0, 0x9F, 0x98])
|
|
249
|
+
expect(() => toUTF8(input1)).not.toThrow()
|
|
250
|
+
expect(() => toUTF8(input2)).not.toThrow()
|
|
251
|
+
expect(() => toUTF8(input3)).not.toThrow()
|
|
252
|
+
})
|
|
253
|
+
})
|
|
254
|
+
|
|
212
255
|
describe('toArray base64', () => {
|
|
213
256
|
it('decodes empty string to empty array', () => {
|
|
214
257
|
expect(toArray('', 'base64')).toEqual([])
|
package/src/primitives/utils.ts
CHANGED
|
@@ -237,7 +237,6 @@ export const toUTF8 = (arr: number[]): string => {
|
|
|
237
237
|
|
|
238
238
|
for (let i = 0; i < arr.length; i++) {
|
|
239
239
|
const byte = arr[i]
|
|
240
|
-
|
|
241
240
|
// this byte is part of a multi-byte sequence, skip it
|
|
242
241
|
// added to avoid modifying i within the loop which is considered unsafe.
|
|
243
242
|
if (skip > 0) {
|
|
@@ -248,26 +247,41 @@ export const toUTF8 = (arr: number[]): string => {
|
|
|
248
247
|
// 1-byte sequence (0xxxxxxx)
|
|
249
248
|
if (byte <= 0x7f) {
|
|
250
249
|
result += String.fromCharCode(byte)
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
250
|
+
continue
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// 2-byte sequence (110xxxxx 10xxxxxx)
|
|
254
|
+
if (byte >= 0xc0 && byte <= 0xdf) {
|
|
255
|
+
const avail = arr.length - (i + 1)
|
|
256
|
+
const byte2 = avail >= 1 ? arr[i + 1] : 0
|
|
257
|
+
skip = Math.min(1, avail)
|
|
258
|
+
|
|
255
259
|
const codePoint = ((byte & 0x1f) << 6) | (byte2 & 0x3f)
|
|
256
260
|
result += String.fromCharCode(codePoint)
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
261
|
+
continue
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// 3-byte sequence (1110xxxx 10xxxxxx 10xxxxxx)
|
|
265
|
+
if (byte >= 0xe0 && byte <= 0xef) {
|
|
266
|
+
const avail = arr.length - (i + 1)
|
|
267
|
+
const byte2 = avail >= 1 ? arr[i + 1] : 0
|
|
268
|
+
const byte3 = avail >= 2 ? arr[i + 2] : 0
|
|
269
|
+
skip = Math.min(2, avail)
|
|
270
|
+
|
|
262
271
|
const codePoint =
|
|
263
272
|
((byte & 0x0f) << 12) | ((byte2 & 0x3f) << 6) | (byte3 & 0x3f)
|
|
264
273
|
result += String.fromCharCode(codePoint)
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
274
|
+
continue
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// 4-byte sequence (11110xxx 10xxxxxx 10xxxxxx 10xxxxxx)
|
|
278
|
+
if (byte >= 0xf0 && byte <= 0xf7) {
|
|
279
|
+
const avail = arr.length - (i + 1)
|
|
280
|
+
const byte2 = avail >= 1 ? arr[i + 1] : 0
|
|
281
|
+
const byte3 = avail >= 2 ? arr[i + 2] : 0
|
|
282
|
+
const byte4 = avail >= 3 ? arr[i + 3] : 0
|
|
283
|
+
skip = Math.min(3, avail)
|
|
284
|
+
|
|
271
285
|
const codePoint =
|
|
272
286
|
((byte & 0x07) << 18) |
|
|
273
287
|
((byte2 & 0x3f) << 12) |
|
|
@@ -278,6 +292,7 @@ export const toUTF8 = (arr: number[]): string => {
|
|
|
278
292
|
const surrogate1 = 0xd800 + ((codePoint - 0x10000) >> 10)
|
|
279
293
|
const surrogate2 = 0xdc00 + ((codePoint - 0x10000) & 0x3ff)
|
|
280
294
|
result += String.fromCharCode(surrogate1, surrogate2)
|
|
295
|
+
continue
|
|
281
296
|
}
|
|
282
297
|
}
|
|
283
298
|
|