@marcuspuchalla/nachos 0.1.1 → 0.1.4

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 (62) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/dist/{chunk-ZRPJUEIZ.js → chunk-5IWW5H47.js} +546 -227
  3. package/dist/chunk-5IWW5H47.js.map +1 -0
  4. package/dist/{chunk-2HBCILJS.cjs → chunk-RVG2BY32.cjs} +545 -226
  5. package/dist/chunk-RVG2BY32.cjs.map +1 -0
  6. package/dist/{chunk-2FUTHZQQ.cjs → chunk-S4RXO6IB.cjs} +244 -166
  7. package/dist/chunk-S4RXO6IB.cjs.map +1 -0
  8. package/dist/{chunk-7CFYWHS6.js → chunk-UMAX5MX5.js} +244 -166
  9. package/dist/chunk-UMAX5MX5.js.map +1 -0
  10. package/dist/encoder/index.cjs +13 -13
  11. package/dist/encoder/index.d.cts +2 -2
  12. package/dist/encoder/index.d.ts +2 -2
  13. package/dist/encoder/index.js +1 -1
  14. package/dist/index.cjs +32 -32
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.cts +28 -19
  17. package/dist/index.d.ts +28 -19
  18. package/dist/index.js +16 -16
  19. package/dist/index.js.map +1 -1
  20. package/dist/metafile-cjs.json +1 -1
  21. package/dist/metafile-esm.json +1 -1
  22. package/dist/parser/index.cjs +14 -14
  23. package/dist/parser/index.d.cts +3 -1
  24. package/dist/parser/index.d.ts +3 -1
  25. package/dist/parser/index.js +1 -1
  26. package/dist/{useCborSimpleEncoder-TVxzNJ_9.d.ts → useCborSimpleEncoder-BoKEmjP9.d.ts} +0 -2
  27. package/dist/{useCborSimpleEncoder-ButVU988.d.cts → useCborSimpleEncoder-C_OHxoB8.d.cts} +0 -2
  28. package/dist/{useCborTag-B_iaShG6.d.ts → useCborTag-BD6Sqp7p.d.ts} +11 -6
  29. package/dist/{useCborTag-BfTIV8HM.d.cts → useCborTag-QpZR-Er2.d.cts} +11 -6
  30. package/package.json +1 -1
  31. package/src/__tests__/public-api.test.ts +153 -0
  32. package/src/__tests__/roundtrip.test.ts +701 -0
  33. package/src/encoder/__tests__/cbor-collection-encoder.test.ts +129 -5
  34. package/src/encoder/__tests__/cbor-encoder-errors.test.ts +847 -0
  35. package/src/encoder/__tests__/cbor-simple-encoder.test.ts +126 -0
  36. package/src/encoder/__tests__/cbor-string-encoder.test.ts +14 -0
  37. package/src/encoder/composables/useCborCollectionEncoder.ts +56 -23
  38. package/src/encoder/composables/useCborEncoder.ts +27 -1
  39. package/src/encoder/composables/useCborSimpleEncoder.ts +40 -8
  40. package/src/encoder/composables/useCborStringEncoder.ts +23 -10
  41. package/src/encoder/types.ts +0 -2
  42. package/src/index.ts +29 -20
  43. package/src/parser/__tests__/buffer-native-parsing.test.ts +338 -0
  44. package/src/parser/__tests__/cbor-float-errors.test.ts +41 -0
  45. package/src/parser/__tests__/cbor-map-duplicate-keys.test.ts +97 -7
  46. package/src/parser/__tests__/cbor-security-dos-protection.test.ts +166 -33
  47. package/src/parser/__tests__/cbor-standard-tags.test.ts +104 -7
  48. package/src/parser/__tests__/cbor-string-errors.test.ts +4 -4
  49. package/src/parser/__tests__/cbor-tag-errors.test.ts +1 -1
  50. package/src/parser/__tests__/cbor-tag-reparse-fix.test.ts +268 -0
  51. package/src/parser/composables/useCborCollection.ts +45 -42
  52. package/src/parser/composables/useCborFloat.ts +95 -9
  53. package/src/parser/composables/useCborInteger.ts +24 -10
  54. package/src/parser/composables/useCborParser.ts +387 -216
  55. package/src/parser/composables/useCborString.ts +22 -4
  56. package/src/parser/composables/useCborTag.ts +149 -53
  57. package/src/parser/utils.ts +11 -0
  58. package/dist/chunk-2FUTHZQQ.cjs.map +0 -1
  59. package/dist/chunk-2HBCILJS.cjs.map +0 -1
  60. package/dist/chunk-7CFYWHS6.js.map +0 -1
  61. package/dist/chunk-ZRPJUEIZ.js.map +0 -1
  62. package/src/encoder/composables/#useCborTagEncoder.ts# +0 -158
@@ -4,6 +4,15 @@ var chunkPD72MVTX_cjs = require('./chunk-PD72MVTX.cjs');
4
4
 
5
5
  // src/parser/utils.ts
6
6
  var hexToBytes = (hex) => {
7
+ if (hex.length === 0) {
8
+ return new Uint8Array(0);
9
+ }
10
+ if (hex.length % 2 !== 0) {
11
+ throw new Error("Hex string must have even length");
12
+ }
13
+ if (!/^[0-9a-fA-F]+$/.test(hex)) {
14
+ throw new Error(`Invalid hex character in: ${hex}`);
15
+ }
7
16
  const bytes = hex.match(/.{1,2}/g);
8
17
  if (!bytes) return new Uint8Array(0);
9
18
  return new Uint8Array(bytes.map((byte) => parseInt(byte, 16)));
@@ -235,9 +244,8 @@ function hasDuplicates(items) {
235
244
 
236
245
  // src/parser/composables/useCborInteger.ts
237
246
  function useCborInteger() {
238
- const parseInteger = (hexString, options) => {
239
- const buffer = hexToBytes(hexString);
240
- const initialByte = readByte(buffer, 0);
247
+ const parseIntegerFromBuffer = (buffer, offset, options) => {
248
+ const initialByte = readByte(buffer, offset);
241
249
  const { majorType, additionalInfo } = extractCborHeader(initialByte);
242
250
  let rawValue;
243
251
  let bytesRead;
@@ -245,16 +253,16 @@ function useCborInteger() {
245
253
  rawValue = additionalInfo;
246
254
  bytesRead = 1;
247
255
  } else if (additionalInfo === 24) {
248
- rawValue = readByte(buffer, 1);
256
+ rawValue = readByte(buffer, offset + 1);
249
257
  bytesRead = 2;
250
258
  } else if (additionalInfo === 25) {
251
- rawValue = readUint(buffer, 1, 2);
259
+ rawValue = readUint(buffer, offset + 1, 2);
252
260
  bytesRead = 3;
253
261
  } else if (additionalInfo === 26) {
254
- rawValue = readUint(buffer, 1, 4);
262
+ rawValue = readUint(buffer, offset + 1, 4);
255
263
  bytesRead = 5;
256
264
  } else if (additionalInfo === 27) {
257
- const bigValue = readBigUint(buffer, 1, 8);
265
+ const bigValue = readBigUint(buffer, offset + 1, 8);
258
266
  if (bigValue <= BigInt(Number.MAX_SAFE_INTEGER)) {
259
267
  rawValue = Number(bigValue);
260
268
  } else {
@@ -294,8 +302,13 @@ function useCborInteger() {
294
302
  bytesRead
295
303
  };
296
304
  };
305
+ const parseInteger = (hexString, options) => {
306
+ const buffer = hexToBytes(hexString);
307
+ return parseIntegerFromBuffer(buffer, 0, options);
308
+ };
297
309
  return {
298
- parseInteger
310
+ parseInteger,
311
+ parseIntegerFromBuffer
299
312
  };
300
313
  }
301
314
 
@@ -363,6 +376,14 @@ function useCborString() {
363
376
  currentOffset++;
364
377
  break;
365
378
  }
379
+ const chunkInitialByte = readByte(buffer, currentOffset);
380
+ const chunkHeader = extractCborHeader(chunkInitialByte);
381
+ if (chunkHeader.majorType !== 2) {
382
+ throw new Error(`Indefinite byte string chunks must be byte strings (major type 2), got ${chunkHeader.majorType}`);
383
+ }
384
+ if (chunkHeader.additionalInfo === 31) {
385
+ throw new Error("Indefinite byte string chunks must be definite-length (RFC 8949 Section 3.2.3)");
386
+ }
366
387
  let chunkResult;
367
388
  try {
368
389
  chunkResult = parseByteString(buffer, currentOffset, options);
@@ -426,6 +447,14 @@ function useCborString() {
426
447
  currentOffset++;
427
448
  break;
428
449
  }
450
+ const chunkInitialByte = readByte(buffer, currentOffset);
451
+ const chunkHeader = extractCborHeader(chunkInitialByte);
452
+ if (chunkHeader.majorType !== 3) {
453
+ throw new Error(`Indefinite text string chunks must be text strings (major type 3), got ${chunkHeader.majorType}`);
454
+ }
455
+ if (chunkHeader.additionalInfo === 31) {
456
+ throw new Error("Indefinite text string chunks must be definite-length (RFC 8949 Section 3.2.3)");
457
+ }
429
458
  let chunkResult;
430
459
  try {
431
460
  chunkResult = parseTextString(buffer, currentOffset, options);
@@ -508,6 +537,45 @@ function useCborFloat() {
508
537
  }
509
538
  return (sign === 0 ? 1 : -1) * Math.pow(2, exponent - 15) * (1 + fraction / 1024);
510
539
  };
540
+ const fitsInFloat16 = (value) => {
541
+ if (Number.isNaN(value) || value === Infinity || value === -Infinity) return true;
542
+ if (Object.is(value, 0) || Object.is(value, -0)) return true;
543
+ const abs = Math.abs(value);
544
+ if (abs > 65504) return false;
545
+ if (abs < 5960464477539063e-23) return false;
546
+ const sign = value < 0 ? 1 : 0;
547
+ const buf = new ArrayBuffer(8);
548
+ const view = new DataView(buf);
549
+ view.setFloat64(0, abs, false);
550
+ const bits64 = view.getBigUint64(0, false);
551
+ const exp64 = Number(bits64 >> 52n & 0x7ffn) - 1023;
552
+ const mant64 = Number(bits64 & 0xfffffffffffffn);
553
+ let exp16;
554
+ let mant16;
555
+ if (exp64 < -14) {
556
+ exp16 = 0;
557
+ const shift = -14 - exp64;
558
+ mant16 = (1 << 10 | mant64 >> 42) >> shift;
559
+ } else if (exp64 > 15) {
560
+ return false;
561
+ } else {
562
+ exp16 = exp64 + 15;
563
+ mant16 = mant64 >> 42;
564
+ }
565
+ const float16Bits = sign << 15 | exp16 << 10 | mant16;
566
+ const s = (float16Bits & 32768) >> 15;
567
+ const e = (float16Bits & 31744) >> 10;
568
+ const f = float16Bits & 1023;
569
+ let reconstructed;
570
+ if (e === 0) {
571
+ reconstructed = (s === 0 ? 1 : -1) * Math.pow(2, -14) * (f / 1024);
572
+ } else if (e === 31) {
573
+ reconstructed = f === 0 ? s === 0 ? Infinity : -Infinity : NaN;
574
+ } else {
575
+ reconstructed = (s === 0 ? 1 : -1) * Math.pow(2, e - 15) * (1 + f / 1024);
576
+ }
577
+ return reconstructed === value;
578
+ };
511
579
  const parseSimpleFromBuffer = (buffer, offset) => {
512
580
  const initialByte = readByte(buffer, offset);
513
581
  const { majorType, additionalInfo } = extractCborHeader(initialByte);
@@ -558,7 +626,7 @@ function useCborFloat() {
558
626
  throw new Error(`Invalid additional info: ${additionalInfo}`);
559
627
  }
560
628
  };
561
- const parseFloatFromBuffer = (buffer, offset) => {
629
+ const parseFloatFromBuffer = (buffer, offset, options) => {
562
630
  const initialByte = readByte(buffer, offset);
563
631
  const { majorType, additionalInfo } = extractCborHeader(initialByte);
564
632
  if (majorType !== 7) {
@@ -569,6 +637,16 @@ function useCborFloat() {
569
637
  if (offset + 2 >= buffer.length) {
570
638
  throw new Error("Unexpected end of buffer while reading Float16");
571
639
  }
640
+ if (options?.validateCanonical) {
641
+ const byte1 = readByte(buffer, offset + 1);
642
+ const byte2 = readByte(buffer, offset + 2);
643
+ const bits = byte1 << 8 | byte2;
644
+ const exp = bits >> 10 & 31;
645
+ const mant = bits & 1023;
646
+ if (exp === 31 && mant !== 0 && bits !== 32256) {
647
+ throw new Error("Non-canonical NaN encoding: use 0xf97e00");
648
+ }
649
+ }
572
650
  const value = float16ToFloat64(buffer, offset + 1);
573
651
  return { value, bytesRead: 3 };
574
652
  }
@@ -578,6 +656,14 @@ function useCborFloat() {
578
656
  }
579
657
  const dataView = new DataView(buffer.buffer, buffer.byteOffset + offset + 1, 4);
580
658
  const value = dataView.getFloat32(0, false);
659
+ if (options?.validateCanonical) {
660
+ if (Number.isNaN(value)) {
661
+ throw new Error("Non-canonical NaN encoding: NaN must use float16 0xf97e00");
662
+ }
663
+ if (fitsInFloat16(value)) {
664
+ throw new Error("Non-canonical float32: value fits in float16, use shortest encoding");
665
+ }
666
+ }
581
667
  return { value, bytesRead: 5 };
582
668
  }
583
669
  case 27: {
@@ -586,20 +672,32 @@ function useCborFloat() {
586
672
  }
587
673
  const dataView = new DataView(buffer.buffer, buffer.byteOffset + offset + 1, 8);
588
674
  const value = dataView.getFloat64(0, false);
675
+ if (options?.validateCanonical) {
676
+ if (Number.isNaN(value)) {
677
+ throw new Error("Non-canonical NaN encoding: NaN must use float16 0xf97e00");
678
+ }
679
+ if (fitsInFloat16(value)) {
680
+ throw new Error("Non-canonical float64: value fits in float16/float32, use shortest encoding");
681
+ }
682
+ const f32 = Math.fround(value);
683
+ if (f32 === value || Object.is(value, -0) && Object.is(f32, -0)) {
684
+ throw new Error("Non-canonical float64: value fits in float16/float32, use shortest encoding");
685
+ }
686
+ }
589
687
  return { value, bytesRead: 9 };
590
688
  }
591
689
  default:
592
690
  throw new Error(`Additional info ${additionalInfo} is not a float type`);
593
691
  }
594
692
  };
595
- const parseFromBuffer = (buffer, offset) => {
693
+ const parseFromBuffer = (buffer, offset, options) => {
596
694
  const initialByte = readByte(buffer, offset);
597
695
  const { majorType, additionalInfo } = extractCborHeader(initialByte);
598
696
  if (majorType !== 7) {
599
697
  throw new Error(`Expected major type 7 (simple/float), got ${majorType}`);
600
698
  }
601
699
  if (additionalInfo === 25 || additionalInfo === 26 || additionalInfo === 27) {
602
- return parseFloatFromBuffer(buffer, offset);
700
+ return parseFloatFromBuffer(buffer, offset, options);
603
701
  } else {
604
702
  return parseSimpleFromBuffer(buffer, offset);
605
703
  }
@@ -608,70 +706,75 @@ function useCborFloat() {
608
706
  const buffer = hexToBytes(hexString);
609
707
  return parseSimpleFromBuffer(buffer, 0);
610
708
  };
611
- const parseFloat = (hexString, _options) => {
709
+ const parseFloat = (hexString, options) => {
612
710
  const buffer = hexToBytes(hexString);
613
- return parseFloatFromBuffer(buffer, 0);
711
+ return parseFloatFromBuffer(buffer, 0, options);
614
712
  };
615
- const parse = (hexString, _options) => {
713
+ const parse = (hexString, options) => {
616
714
  const buffer = hexToBytes(hexString);
617
- return parseFromBuffer(buffer, 0);
715
+ return parseFromBuffer(buffer, 0, options);
618
716
  };
619
717
  return {
620
718
  parse,
621
719
  parseFloat,
622
- parseSimple
720
+ parseSimple,
721
+ parseFromBuffer
623
722
  };
624
723
  }
625
724
 
626
725
  // src/parser/composables/useCborTag.ts
627
726
  function useCborTag() {
628
- const { parseInteger } = useCborInteger();
727
+ const { parseIntegerFromBuffer } = useCborInteger();
629
728
  const { parseByteString, parseTextString } = useCborString();
630
- const { parseFloat, parseSimple } = useCborFloat();
631
- const parseItem = (buffer, offset, options, tagDepth = 0) => {
729
+ const { parseFromBuffer: parseFloatOrSimpleFromBuffer } = useCborFloat();
730
+ let parseStartTime = 0;
731
+ const parseItem = (buffer, offset, options, tagDepth = 0, collectionDepth = 0) => {
732
+ if (parseStartTime > 0 && options?.limits?.maxParseTime) {
733
+ const elapsed = Date.now() - parseStartTime;
734
+ if (elapsed > options.limits.maxParseTime) {
735
+ throw new Error(`Parse timeout: exceeded ${options.limits.maxParseTime}ms limit`);
736
+ }
737
+ }
632
738
  if (offset >= buffer.length) {
633
739
  throw new Error(`Unexpected end of buffer at offset ${offset}`);
634
740
  }
635
741
  const initialByte = readByte(buffer, offset);
636
- const { majorType, additionalInfo } = extractCborHeader(initialByte);
742
+ const { majorType } = extractCborHeader(initialByte);
637
743
  switch (majorType) {
638
744
  case 0:
639
745
  // Unsigned integer
640
- case 1: {
641
- const intHex = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
642
- const result = parseInteger(intHex, options);
643
- return { value: result.value, bytesRead: result.bytesRead };
644
- }
746
+ case 1:
747
+ return parseIntegerFromBuffer(buffer, offset, options);
645
748
  case 2:
646
749
  return parseByteString(buffer, offset, options);
647
750
  case 3:
648
751
  return parseTextString(buffer, offset, options);
649
752
  case 4:
650
- return parseArrayInternal(buffer, offset, options);
753
+ return parseArrayInternal(buffer, offset, options, collectionDepth);
651
754
  case 5:
652
- return parseMapInternal(buffer, offset, options);
755
+ return parseMapInternal(buffer, offset, options, collectionDepth);
653
756
  case 6:
654
757
  return parseTagFromBuffer(buffer, offset, options, tagDepth);
655
- case 7: {
656
- const simpleHex = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
657
- if (additionalInfo >= 25 && additionalInfo <= 27) {
658
- const result = parseFloat(simpleHex, options);
659
- return { value: result.value, bytesRead: result.bytesRead };
660
- } else {
661
- const result = parseSimple(simpleHex, options);
662
- return { value: result.value, bytesRead: result.bytesRead };
663
- }
664
- }
758
+ case 7:
759
+ return parseFloatOrSimpleFromBuffer(buffer, offset, options);
665
760
  default:
666
761
  throw new Error(`Unknown major type: ${majorType}`);
667
762
  }
668
763
  };
669
- const parseArrayInternal = (buffer, offset, options) => {
764
+ const parseArrayInternal = (buffer, offset, options, depth = 0) => {
670
765
  const initialByte = readByte(buffer, offset);
671
766
  const { majorType, additionalInfo } = extractCborHeader(initialByte);
672
767
  if (majorType !== 4) {
673
768
  throw new Error(`Expected major type 4 (array), got ${majorType}`);
674
769
  }
770
+ const isIndefiniteAllowed = options?.allowIndefinite ?? !(options?.validateCanonical || options?.strict);
771
+ if (additionalInfo === 31 && !isIndefiniteAllowed) {
772
+ throw new Error("Indefinite-length encoding is not allowed (strict/canonical mode)");
773
+ }
774
+ const maxDepth = options?.limits?.maxDepth ?? chunkPD72MVTX_cjs.DEFAULT_LIMITS.maxDepth;
775
+ if (depth >= maxDepth) {
776
+ throw new Error(`Maximum nesting depth ${maxDepth} exceeded`);
777
+ }
675
778
  let length;
676
779
  let bytesConsumed;
677
780
  if (additionalInfo < 24) {
@@ -699,23 +802,39 @@ function useCborTag() {
699
802
  } else {
700
803
  throw new Error(`Invalid additional info for array: ${additionalInfo}`);
701
804
  }
805
+ if (length !== null && options?.limits?.maxArrayLength && length > options.limits.maxArrayLength) {
806
+ throw new Error(`Array length ${length} exceeds limit of ${options.limits.maxArrayLength}`);
807
+ }
702
808
  let currentOffset = offset + 1 + bytesConsumed;
703
809
  const items = [];
704
810
  if (length === null) {
811
+ let index = 0;
812
+ let foundBreak = false;
705
813
  while (currentOffset < buffer.length) {
706
814
  const nextByte = readByte(buffer, currentOffset);
707
815
  if (nextByte === 255) {
708
816
  currentOffset++;
817
+ foundBreak = true;
709
818
  break;
710
819
  }
711
- const itemResult = parseItem(buffer, currentOffset, options);
820
+ if (options?.limits?.maxArrayLength && index >= options.limits.maxArrayLength) {
821
+ throw new Error(`Array length exceeds limit of ${options.limits.maxArrayLength}`);
822
+ }
823
+ const itemResult = parseItem(buffer, currentOffset, options, 0, depth + 1);
712
824
  items.push(itemResult.value);
713
825
  currentOffset += itemResult.bytesRead;
826
+ index++;
827
+ }
828
+ if (!foundBreak) {
829
+ throw new Error("Indefinite-length array missing break code (0xFF)");
714
830
  }
715
831
  items[chunkPD72MVTX_cjs.INDEFINITE_SYMBOL] = true;
716
832
  } else {
717
833
  for (let i = 0; i < length; i++) {
718
- const itemResult = parseItem(buffer, currentOffset, options);
834
+ if (currentOffset >= buffer.length) {
835
+ throw new Error(`Unexpected end of buffer while parsing array element ${i}/${length}`);
836
+ }
837
+ const itemResult = parseItem(buffer, currentOffset, options, 0, depth + 1);
719
838
  items.push(itemResult.value);
720
839
  currentOffset += itemResult.bytesRead;
721
840
  }
@@ -725,12 +844,20 @@ function useCborTag() {
725
844
  bytesRead: currentOffset - offset
726
845
  };
727
846
  };
728
- const parseMapInternal = (buffer, offset, options) => {
847
+ const parseMapInternal = (buffer, offset, options, depth = 0) => {
729
848
  const initialByte = readByte(buffer, offset);
730
849
  const { majorType, additionalInfo } = extractCborHeader(initialByte);
731
850
  if (majorType !== 5) {
732
851
  throw new Error(`Expected major type 5 (map), got ${majorType}`);
733
852
  }
853
+ const isIndefiniteAllowed = options?.allowIndefinite ?? !(options?.validateCanonical || options?.strict);
854
+ if (additionalInfo === 31 && !isIndefiniteAllowed) {
855
+ throw new Error("Indefinite-length encoding is not allowed (strict/canonical mode)");
856
+ }
857
+ const maxDepth = options?.limits?.maxDepth ?? chunkPD72MVTX_cjs.DEFAULT_LIMITS.maxDepth;
858
+ if (depth >= maxDepth) {
859
+ throw new Error(`Maximum nesting depth ${maxDepth} exceeded`);
860
+ }
734
861
  let length;
735
862
  let bytesConsumed;
736
863
  if (additionalInfo < 24) {
@@ -758,27 +885,43 @@ function useCborTag() {
758
885
  } else {
759
886
  throw new Error(`Invalid additional info for map: ${additionalInfo}`);
760
887
  }
888
+ if (length !== null && options?.limits?.maxMapSize && length > options.limits.maxMapSize) {
889
+ throw new Error(`Map size ${length} exceeds limit of ${options.limits.maxMapSize}`);
890
+ }
761
891
  let currentOffset = offset + 1 + bytesConsumed;
762
892
  const map = /* @__PURE__ */ new Map();
763
893
  if (length === null) {
894
+ let index = 0;
895
+ let foundBreak = false;
764
896
  while (currentOffset < buffer.length) {
765
897
  const nextByte = readByte(buffer, currentOffset);
766
898
  if (nextByte === 255) {
767
899
  currentOffset++;
900
+ foundBreak = true;
768
901
  break;
769
902
  }
770
- const keyResult = parseItem(buffer, currentOffset, options);
903
+ if (options?.limits?.maxMapSize && index >= options.limits.maxMapSize) {
904
+ throw new Error(`Map size exceeds limit of ${options.limits.maxMapSize}`);
905
+ }
906
+ const keyResult = parseItem(buffer, currentOffset, options, 0, depth + 1);
771
907
  currentOffset += keyResult.bytesRead;
772
- const valueResult = parseItem(buffer, currentOffset, options);
908
+ const valueResult = parseItem(buffer, currentOffset, options, 0, depth + 1);
773
909
  currentOffset += valueResult.bytesRead;
774
910
  map.set(keyResult.value, valueResult.value);
911
+ index++;
912
+ }
913
+ if (!foundBreak) {
914
+ throw new Error("Indefinite-length map missing break code (0xFF)");
775
915
  }
776
916
  map[chunkPD72MVTX_cjs.INDEFINITE_SYMBOL] = true;
777
917
  } else {
778
918
  for (let i = 0; i < length; i++) {
779
- const keyResult = parseItem(buffer, currentOffset, options);
919
+ if (currentOffset >= buffer.length) {
920
+ throw new Error(`Unexpected end of buffer while parsing map entry ${i}/${length}`);
921
+ }
922
+ const keyResult = parseItem(buffer, currentOffset, options, 0, depth + 1);
780
923
  currentOffset += keyResult.bytesRead;
781
- const valueResult = parseItem(buffer, currentOffset, options);
924
+ const valueResult = parseItem(buffer, currentOffset, options, 0, depth + 1);
782
925
  currentOffset += valueResult.bytesRead;
783
926
  map.set(keyResult.value, valueResult.value);
784
927
  }
@@ -828,6 +971,13 @@ function useCborTag() {
828
971
  }
829
972
  return false;
830
973
  };
974
+ const isByteString = (value) => {
975
+ if (value instanceof Uint8Array) return true;
976
+ if (value && typeof value === "object" && "type" in value && value.type === "cbor-byte-string") {
977
+ return true;
978
+ }
979
+ return false;
980
+ };
831
981
  const getTextStringValue = (value) => {
832
982
  if (typeof value === "string") return value;
833
983
  if (value && typeof value === "object" && "text" in value) {
@@ -854,6 +1004,14 @@ function useCborTag() {
854
1004
  throw new Error(`Tag 1 (epoch time) must contain a number (integer or float), got ${typeof value}`);
855
1005
  }
856
1006
  break;
1007
+ case 2:
1008
+ // Positive bignum
1009
+ case 3:
1010
+ if (!shouldValidateStandard) break;
1011
+ if (!isByteString(value)) {
1012
+ throw new Error(`Tag ${tagNumber} (bignum) must contain a byte string, got ${typeof value}`);
1013
+ }
1014
+ break;
857
1015
  case 4:
858
1016
  if (!shouldValidateStandard) break;
859
1017
  if (!Array.isArray(value)) {
@@ -862,11 +1020,11 @@ function useCborTag() {
862
1020
  if (value.length !== 2) {
863
1021
  throw new Error(`Tag 4 (decimal fraction) array must have exactly 2 elements [exponent, mantissa], got ${value.length}`);
864
1022
  }
865
- if (typeof value[0] !== "number" && typeof value[0] !== "bigint") {
866
- throw new Error(`Tag 4 (decimal fraction) exponent must be an integer, got ${typeof value[0]}`);
1023
+ if (typeof value[0] !== "number" && typeof value[0] !== "bigint" || typeof value[0] === "number" && !Number.isInteger(value[0])) {
1024
+ throw new Error(`Tag 4 (decimal fraction) exponent must be an integer, got ${value[0]}`);
867
1025
  }
868
- if (typeof value[1] !== "number" && typeof value[1] !== "bigint") {
869
- throw new Error(`Tag 4 (decimal fraction) mantissa must be an integer, got ${typeof value[1]}`);
1026
+ if (typeof value[1] !== "number" && typeof value[1] !== "bigint" || typeof value[1] === "number" && !Number.isInteger(value[1])) {
1027
+ throw new Error(`Tag 4 (decimal fraction) mantissa must be an integer, got ${value[1]}`);
870
1028
  }
871
1029
  break;
872
1030
  case 5:
@@ -877,11 +1035,11 @@ function useCborTag() {
877
1035
  if (value.length !== 2) {
878
1036
  throw new Error(`Tag 5 (bigfloat) array must have exactly 2 elements [exponent, mantissa], got ${value.length}`);
879
1037
  }
880
- if (typeof value[0] !== "number" && typeof value[0] !== "bigint") {
881
- throw new Error(`Tag 5 (bigfloat) exponent must be an integer, got ${typeof value[0]}`);
1038
+ if (typeof value[0] !== "number" && typeof value[0] !== "bigint" || typeof value[0] === "number" && !Number.isInteger(value[0])) {
1039
+ throw new Error(`Tag 5 (bigfloat) exponent must be an integer, got ${value[0]}`);
882
1040
  }
883
- if (typeof value[1] !== "number" && typeof value[1] !== "bigint") {
884
- throw new Error(`Tag 5 (bigfloat) mantissa must be an integer, got ${typeof value[1]}`);
1041
+ if (typeof value[1] !== "number" && typeof value[1] !== "bigint" || typeof value[1] === "number" && !Number.isInteger(value[1])) {
1042
+ throw new Error(`Tag 5 (bigfloat) mantissa must be an integer, got ${value[1]}`);
885
1043
  }
886
1044
  break;
887
1045
  case 32:
@@ -1041,7 +1199,7 @@ function useCborTag() {
1041
1199
  if (majorType !== 6) {
1042
1200
  throw new Error(`Expected major type 6 (tag), got ${majorType}`);
1043
1201
  }
1044
- const maxTagDepth = options?.limits?.maxTagDepth ?? 64;
1202
+ const maxTagDepth = options?.limits?.maxTagDepth ?? chunkPD72MVTX_cjs.DEFAULT_LIMITS.maxTagDepth;
1045
1203
  if (tagDepth >= maxTagDepth) {
1046
1204
  throw new Error(`Tag nesting depth ${tagDepth} exceeds limit of ${maxTagDepth}`);
1047
1205
  }
@@ -1085,12 +1243,25 @@ function useCborTag() {
1085
1243
  const parseTag = (hexString, options) => {
1086
1244
  const cleanHex = hexString.replace(/\s+/g, "");
1087
1245
  const buffer = hexToBytes(cleanHex);
1088
- return parseTagFromBuffer(buffer, 0, options);
1246
+ const isTopLevel = parseStartTime === 0;
1247
+ if (isTopLevel && options?.limits?.maxParseTime) {
1248
+ parseStartTime = Date.now();
1249
+ }
1250
+ try {
1251
+ return parseTagFromBuffer(buffer, 0, options);
1252
+ } finally {
1253
+ if (isTopLevel) {
1254
+ parseStartTime = 0;
1255
+ }
1256
+ }
1089
1257
  };
1090
1258
  const parse = parseTag;
1091
1259
  return {
1092
1260
  parseTag,
1093
- parse
1261
+ parse,
1262
+ parseTagFromBuffer,
1263
+ validateTagSemantics,
1264
+ decodePlutusConstructor
1094
1265
  };
1095
1266
  }
1096
1267
 
@@ -1149,17 +1320,18 @@ var logger = {
1149
1320
 
1150
1321
  // src/parser/composables/useCborCollection.ts
1151
1322
  function useCborCollection() {
1152
- const { parseInteger } = useCborInteger();
1323
+ const { parseIntegerFromBuffer } = useCborInteger();
1153
1324
  const { parseByteString, parseTextString } = useCborString();
1154
- const { parse: parseFloatOrSimple } = useCborFloat();
1155
- const { parseTag } = useCborTag();
1156
- const convertKeyToString = (key) => {
1157
- if (key instanceof Uint8Array) {
1158
- return bytesToHex(key);
1159
- }
1160
- return String(key);
1161
- };
1325
+ const { parseFromBuffer: parseFloatOrSimpleFromBuffer } = useCborFloat();
1326
+ const { parseTagFromBuffer } = useCborTag();
1327
+ let parseStartTime = 0;
1162
1328
  const parseItem = (buffer, offset, options, depth = 0) => {
1329
+ if (parseStartTime > 0 && options?.limits?.maxParseTime) {
1330
+ const elapsed = Date.now() - parseStartTime;
1331
+ if (elapsed > options.limits.maxParseTime) {
1332
+ throw new Error(`Parse timeout: exceeded ${options.limits.maxParseTime}ms limit`);
1333
+ }
1334
+ }
1163
1335
  if (offset >= buffer.length) {
1164
1336
  throw new Error(`Unexpected end of buffer at offset ${offset}`);
1165
1337
  }
@@ -1168,11 +1340,8 @@ function useCborCollection() {
1168
1340
  switch (majorType) {
1169
1341
  case 0:
1170
1342
  // Unsigned integer
1171
- case 1: {
1172
- const intHex = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
1173
- const result = parseInteger(intHex, options);
1174
- return { value: result.value, bytesRead: result.bytesRead };
1175
- }
1343
+ case 1:
1344
+ return parseIntegerFromBuffer(buffer, offset, options);
1176
1345
  case 2:
1177
1346
  return parseByteString(buffer, offset, options);
1178
1347
  case 3:
@@ -1181,16 +1350,10 @@ function useCborCollection() {
1181
1350
  return parseArrayFromBuffer(buffer, offset, options, depth);
1182
1351
  case 5:
1183
1352
  return parseMapFromBuffer(buffer, offset, options, depth);
1184
- case 6: {
1185
- const tagHex = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
1186
- const result = parseTag(tagHex, options);
1187
- return { value: result.value, bytesRead: result.bytesRead };
1188
- }
1189
- case 7: {
1190
- const floatHex = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
1191
- const result = parseFloatOrSimple(floatHex, options);
1192
- return { value: result.value, bytesRead: result.bytesRead };
1193
- }
1353
+ case 6:
1354
+ return parseTagFromBuffer(buffer, offset, options);
1355
+ case 7:
1356
+ return parseFloatOrSimpleFromBuffer(buffer, offset, options);
1194
1357
  default:
1195
1358
  throw new Error(`Unknown major type: ${majorType}`);
1196
1359
  }
@@ -1332,7 +1495,7 @@ function useCborCollection() {
1332
1495
  }
1333
1496
  const valueResult = parseItem(buffer, currentOffset, options, depth + 1);
1334
1497
  currentOffset += valueResult.bytesRead;
1335
- const keyString = convertKeyToString(keyResult.value);
1498
+ const keyString = serializeValueForComparison(keyResult.value);
1336
1499
  if (seenKeys.has(keyString)) {
1337
1500
  const mode = options?.dupMapKeyMode || "allow";
1338
1501
  if (mode === "reject") {
@@ -1370,7 +1533,7 @@ function useCborCollection() {
1370
1533
  }
1371
1534
  const valueResult = parseItem(buffer, currentOffset, options, depth + 1);
1372
1535
  currentOffset += valueResult.bytesRead;
1373
- const keyString = convertKeyToString(keyResult.value);
1536
+ const keyString = serializeValueForComparison(keyResult.value);
1374
1537
  if (seenKeys.has(keyString)) {
1375
1538
  const mode = options?.dupMapKeyMode || "allow";
1376
1539
  if (mode === "reject") {
@@ -1410,12 +1573,26 @@ function useCborCollection() {
1410
1573
  const parseArray = (hexString, options) => {
1411
1574
  const cleanHex = hexString.replace(/\s+/g, "");
1412
1575
  const buffer = hexToBytes(cleanHex);
1413
- return parseArrayFromBuffer(buffer, 0, options, 0);
1576
+ if (options?.limits?.maxParseTime) {
1577
+ parseStartTime = Date.now();
1578
+ }
1579
+ try {
1580
+ return parseArrayFromBuffer(buffer, 0, options, 0);
1581
+ } finally {
1582
+ parseStartTime = 0;
1583
+ }
1414
1584
  };
1415
1585
  const parseMap = (hexString, options) => {
1416
1586
  const cleanHex = hexString.replace(/\s+/g, "");
1417
1587
  const buffer = hexToBytes(cleanHex);
1418
- return parseMapFromBuffer(buffer, 0, options, 0);
1588
+ if (options?.limits?.maxParseTime) {
1589
+ parseStartTime = Date.now();
1590
+ }
1591
+ try {
1592
+ return parseMapFromBuffer(buffer, 0, options, 0);
1593
+ } finally {
1594
+ parseStartTime = 0;
1595
+ }
1419
1596
  };
1420
1597
  return {
1421
1598
  parseArray,
@@ -1459,13 +1636,23 @@ function useCborParser() {
1459
1636
  throw new Error(`Parse timeout: exceeded ${ctx.options.limits.maxParseTime}ms limit (elapsed: ${elapsed}ms)`);
1460
1637
  }
1461
1638
  };
1462
- const { parseInteger } = useCborInteger();
1463
- const { parseString } = useCborString();
1639
+ const { parseInteger, parseIntegerFromBuffer: integerFromBuffer } = useCborInteger();
1640
+ const { parseString, parseByteString: byteStringFromBuffer, parseTextString: textStringFromBuffer } = useCborString();
1464
1641
  const { parseArray, parseMap } = useCborCollection();
1465
- const { parseTag } = useCborTag();
1466
- const { parse: parseFloatOrSimple } = useCborFloat();
1467
- const parse = (hexString, options) => {
1468
- const cleanHex = hexString.replace(/\s+/g, "");
1642
+ const { parseTag, validateTagSemantics, decodePlutusConstructor } = useCborTag();
1643
+ const { parse: parseFloatOrSimple, parseFromBuffer: floatOrSimpleFromBuffer } = useCborFloat();
1644
+ const parse = (input, options) => {
1645
+ const mergedOptions = mergeOptions(options);
1646
+ if (input instanceof Uint8Array) {
1647
+ if (input.length === 0) {
1648
+ throw new Error("Empty input");
1649
+ }
1650
+ if (mergedOptions.limits?.maxInputSize && input.length > mergedOptions.limits.maxInputSize) {
1651
+ throw new Error(`Input size ${input.length} bytes exceeds limit of ${mergedOptions.limits.maxInputSize} bytes`);
1652
+ }
1653
+ return dispatchFromBuffer(input, 0, mergedOptions);
1654
+ }
1655
+ const cleanHex = input.replace(/\s+/g, "");
1469
1656
  if (!cleanHex || cleanHex.length === 0) {
1470
1657
  throw new Error("Empty hex string");
1471
1658
  }
@@ -1475,7 +1662,6 @@ function useCborParser() {
1475
1662
  if (!/^[0-9a-fA-F]+$/.test(cleanHex)) {
1476
1663
  throw new Error(`Invalid hex character in: ${cleanHex}`);
1477
1664
  }
1478
- const mergedOptions = mergeOptions(options);
1479
1665
  const inputSize = cleanHex.length / 2;
1480
1666
  if (mergedOptions.limits?.maxInputSize && inputSize > mergedOptions.limits.maxInputSize) {
1481
1667
  throw new Error(`Input size ${inputSize} bytes exceeds limit of ${mergedOptions.limits.maxInputSize} bytes`);
@@ -1504,26 +1690,40 @@ function useCborParser() {
1504
1690
  throw new Error(`Unknown major type: ${majorType}`);
1505
1691
  }
1506
1692
  };
1507
- const parseWithSourceMap = (hexString, options) => {
1508
- const cleanHex = hexString.replace(/\s+/g, "");
1509
- if (!cleanHex || cleanHex.length === 0) {
1510
- throw new Error("Empty hex string");
1511
- }
1512
- if (cleanHex.length % 2 !== 0) {
1513
- throw new Error("Hex string must have even length");
1514
- }
1515
- if (!/^[0-9a-fA-F]+$/.test(cleanHex)) {
1516
- throw new Error(`Invalid hex character in: ${cleanHex}`);
1517
- }
1693
+ const parseWithSourceMap = (input, options) => {
1518
1694
  const mergedOptions = mergeOptions(options);
1519
- const inputSize = cleanHex.length / 2;
1520
- if (mergedOptions.limits?.maxInputSize && inputSize > mergedOptions.limits.maxInputSize) {
1521
- throw new Error(`Input size ${inputSize} bytes exceeds limit of ${mergedOptions.limits.maxInputSize} bytes`);
1695
+ let buffer;
1696
+ if (input instanceof Uint8Array) {
1697
+ if (input.length === 0) {
1698
+ throw new Error("Empty input");
1699
+ }
1700
+ if (mergedOptions.limits?.maxInputSize && input.length > mergedOptions.limits.maxInputSize) {
1701
+ throw new Error(`Input size ${input.length} bytes exceeds limit of ${mergedOptions.limits.maxInputSize} bytes`);
1702
+ }
1703
+ buffer = input;
1704
+ } else {
1705
+ const cleanHex = input.replace(/\s+/g, "");
1706
+ if (!cleanHex || cleanHex.length === 0) {
1707
+ throw new Error("Empty hex string");
1708
+ }
1709
+ if (cleanHex.length % 2 !== 0) {
1710
+ throw new Error("Hex string must have even length");
1711
+ }
1712
+ if (!/^[0-9a-fA-F]+$/.test(cleanHex)) {
1713
+ throw new Error(`Invalid hex character in: ${cleanHex}`);
1714
+ }
1715
+ const inputSize = cleanHex.length / 2;
1716
+ if (mergedOptions.limits?.maxInputSize && inputSize > mergedOptions.limits.maxInputSize) {
1717
+ throw new Error(`Input size ${inputSize} bytes exceeds limit of ${mergedOptions.limits.maxInputSize} bytes`);
1718
+ }
1719
+ buffer = hexToBytes(cleanHex);
1522
1720
  }
1523
- const buffer = hexToBytes(cleanHex);
1524
1721
  const sourceMap = [];
1525
1722
  const ctx = {
1526
1723
  buffer,
1724
+ offset: 0,
1725
+ sourceMap,
1726
+ currentDepth: 0,
1527
1727
  startTime: Date.now(),
1528
1728
  bytesAllocated: 0,
1529
1729
  options: mergedOptions
@@ -1666,19 +1866,57 @@ function useCborParser() {
1666
1866
  }
1667
1867
  return result;
1668
1868
  };
1869
+ const dispatchFromBuffer = (buffer, offset, options) => {
1870
+ const initialByte = readByte(buffer, offset);
1871
+ const { majorType } = extractCborHeader(initialByte);
1872
+ switch (majorType) {
1873
+ case 0:
1874
+ // Unsigned integer
1875
+ case 1:
1876
+ return integerFromBuffer(buffer, offset, options);
1877
+ case 2:
1878
+ return byteStringFromBuffer(buffer, offset, options);
1879
+ case 3:
1880
+ return textStringFromBuffer(buffer, offset, options);
1881
+ case 4: {
1882
+ const hexString = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
1883
+ return parseArray(hexString, options);
1884
+ }
1885
+ case 5: {
1886
+ const hexString = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
1887
+ return parseMap(hexString, options);
1888
+ }
1889
+ case 6: {
1890
+ const hexString = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
1891
+ return parseTag(hexString, options);
1892
+ }
1893
+ case 7:
1894
+ return floatOrSimpleFromBuffer(buffer, offset, options);
1895
+ default:
1896
+ throw new Error(`Unknown major type: ${majorType}`);
1897
+ }
1898
+ };
1669
1899
  const parseIntegerFromBuffer = (buffer, offset, options) => {
1670
- const hexString = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
1671
- return parseInteger(hexString, options);
1900
+ return integerFromBuffer(buffer, offset, options);
1672
1901
  };
1673
1902
  const parseStringFromBuffer = (buffer, offset, options) => {
1674
- const hexString = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
1675
- return parseString(hexString, options);
1903
+ const initialByte = readByte(buffer, offset);
1904
+ const { majorType } = extractCborHeader(initialByte);
1905
+ if (majorType === 2) {
1906
+ return byteStringFromBuffer(buffer, offset, options);
1907
+ }
1908
+ return textStringFromBuffer(buffer, offset, options);
1676
1909
  };
1677
1910
  const parseFloatFromBuffer = (buffer, offset, options) => {
1678
- const hexString = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
1679
- return parseFloatOrSimple(hexString, options);
1911
+ return floatOrSimpleFromBuffer(buffer, offset, options);
1680
1912
  };
1681
1913
  const parseArrayWithMap = (ctx, offset, path, sourceMap) => {
1914
+ const previousDepth = ctx.currentDepth ?? 0;
1915
+ const maxDepth = ctx.options?.limits?.maxDepth;
1916
+ if (maxDepth !== void 0 && previousDepth >= maxDepth) {
1917
+ throw new Error(`Maximum nesting depth ${maxDepth} exceeded`);
1918
+ }
1919
+ ctx.currentDepth = previousDepth + 1;
1682
1920
  const startOffset = offset;
1683
1921
  const initialByte = readByte(ctx.buffer, offset);
1684
1922
  const { additionalInfo } = extractCborHeader(initialByte);
@@ -1705,6 +1943,10 @@ function useCborParser() {
1705
1943
  length = Number(bigLength);
1706
1944
  currentOffset += 8;
1707
1945
  } else if (additionalInfo === 31) {
1946
+ const isIndefiniteAllowed = ctx.options?.allowIndefinite ?? !(ctx.options?.validateCanonical || ctx.options?.strict);
1947
+ if (!isIndefiniteAllowed) {
1948
+ throw new Error("Indefinite-length encoding is not allowed (strict/canonical mode)");
1949
+ }
1708
1950
  isIndefinite = true;
1709
1951
  length = 0;
1710
1952
  } else {
@@ -1721,49 +1963,70 @@ function useCborParser() {
1721
1963
  isHeader: true,
1722
1964
  headerEnd
1723
1965
  });
1724
- const childPaths = [];
1725
- if (isIndefinite) {
1726
- let index = 0;
1727
- while (currentOffset < ctx.buffer.length) {
1728
- const nextByte = readByte(ctx.buffer, currentOffset);
1729
- if (nextByte === 255) {
1730
- currentOffset++;
1731
- break;
1966
+ try {
1967
+ const childPaths = [];
1968
+ if (isIndefinite) {
1969
+ let index = 0;
1970
+ let foundBreak = false;
1971
+ while (currentOffset < ctx.buffer.length) {
1972
+ const nextByte = readByte(ctx.buffer, currentOffset);
1973
+ if (nextByte === 255) {
1974
+ currentOffset++;
1975
+ foundBreak = true;
1976
+ break;
1977
+ }
1978
+ if (ctx.options?.limits?.maxArrayLength && index >= ctx.options.limits.maxArrayLength) {
1979
+ throw new Error(`Array length exceeds limit of ${ctx.options.limits.maxArrayLength}`);
1980
+ }
1981
+ const elementPath = `${path}[${index}]`;
1982
+ childPaths.push(elementPath);
1983
+ const elementResult = parseValueWithMap(ctx, currentOffset, elementPath, sourceMap);
1984
+ items.push(elementResult.value);
1985
+ currentOffset += elementResult.bytesRead;
1986
+ const elementEntry = sourceMap.find((e) => e.path === elementPath);
1987
+ if (elementEntry) {
1988
+ elementEntry.parent = path;
1989
+ }
1990
+ index++;
1732
1991
  }
1733
- const elementPath = `${path}[${index}]`;
1734
- childPaths.push(elementPath);
1735
- const elementResult = parseValueWithMap(ctx, currentOffset, elementPath, sourceMap);
1736
- items.push(elementResult.value);
1737
- currentOffset += elementResult.bytesRead;
1738
- const elementEntry = sourceMap.find((e) => e.path === elementPath);
1739
- if (elementEntry) {
1740
- elementEntry.parent = path;
1992
+ if (!foundBreak) {
1993
+ throw new Error("Indefinite-length array missing break code (0xFF)");
1741
1994
  }
1742
- index++;
1743
- }
1744
- } else {
1745
- for (let i = 0; i < length; i++) {
1746
- const elementPath = `${path}[${i}]`;
1747
- childPaths.push(elementPath);
1748
- const elementResult = parseValueWithMap(ctx, currentOffset, elementPath, sourceMap);
1749
- items.push(elementResult.value);
1750
- currentOffset += elementResult.bytesRead;
1751
- const elementEntry = sourceMap.find((e) => e.path === elementPath);
1752
- if (elementEntry) {
1753
- elementEntry.parent = path;
1995
+ } else {
1996
+ if (ctx.options?.limits?.maxArrayLength && length > ctx.options.limits.maxArrayLength) {
1997
+ throw new Error(`Array length ${length} exceeds limit of ${ctx.options.limits.maxArrayLength}`);
1998
+ }
1999
+ for (let i = 0; i < length; i++) {
2000
+ const elementPath = `${path}[${i}]`;
2001
+ childPaths.push(elementPath);
2002
+ const elementResult = parseValueWithMap(ctx, currentOffset, elementPath, sourceMap);
2003
+ items.push(elementResult.value);
2004
+ currentOffset += elementResult.bytesRead;
2005
+ const elementEntry = sourceMap.find((e) => e.path === elementPath);
2006
+ if (elementEntry) {
2007
+ elementEntry.parent = path;
2008
+ }
1754
2009
  }
1755
2010
  }
2011
+ const bytesRead = currentOffset - offset;
2012
+ if (childPaths.length > 0 && sourceMap[arrayEntryIndex]) {
2013
+ sourceMap[arrayEntryIndex].children = childPaths;
2014
+ }
2015
+ return {
2016
+ value: items,
2017
+ bytesRead
2018
+ };
2019
+ } finally {
2020
+ ctx.currentDepth = previousDepth;
1756
2021
  }
1757
- const bytesRead = currentOffset - offset;
1758
- if (childPaths.length > 0 && sourceMap[arrayEntryIndex]) {
1759
- sourceMap[arrayEntryIndex].children = childPaths;
1760
- }
1761
- return {
1762
- value: items,
1763
- bytesRead
1764
- };
1765
2022
  };
1766
2023
  const parseMapWithMap = (ctx, offset, path, sourceMap) => {
2024
+ const previousDepth = ctx.currentDepth ?? 0;
2025
+ const maxDepth = ctx.options?.limits?.maxDepth;
2026
+ if (maxDepth !== void 0 && previousDepth >= maxDepth) {
2027
+ throw new Error(`Maximum nesting depth ${maxDepth} exceeded`);
2028
+ }
2029
+ ctx.currentDepth = previousDepth + 1;
1767
2030
  const startOffset = offset;
1768
2031
  const initialByte = readByte(ctx.buffer, offset);
1769
2032
  const { additionalInfo } = extractCborHeader(initialByte);
@@ -1790,6 +2053,10 @@ function useCborParser() {
1790
2053
  length = Number(bigLength);
1791
2054
  currentOffset += 8;
1792
2055
  } else if (additionalInfo === 31) {
2056
+ const isIndefiniteAllowed = ctx.options?.allowIndefinite ?? !(ctx.options?.validateCanonical || ctx.options?.strict);
2057
+ if (!isIndefiniteAllowed) {
2058
+ throw new Error("Indefinite-length encoding is not allowed (strict/canonical mode)");
2059
+ }
1793
2060
  isIndefinite = true;
1794
2061
  length = 0;
1795
2062
  } else {
@@ -1806,72 +2073,91 @@ function useCborParser() {
1806
2073
  isHeader: true,
1807
2074
  headerEnd
1808
2075
  });
1809
- const childPaths = [];
1810
- const seenKeys = /* @__PURE__ */ new Set();
1811
- if (isIndefinite) {
1812
- while (currentOffset < ctx.buffer.length) {
1813
- const nextByte = readByte(ctx.buffer, currentOffset);
1814
- if (nextByte === 255) {
1815
- currentOffset++;
1816
- break;
1817
- }
1818
- const keyPath = `${path}${path ? "." : ""}#key`;
1819
- const keyResult = parseValueWithMap(ctx, currentOffset, keyPath, sourceMap);
1820
- currentOffset += keyResult.bytesRead;
1821
- const keyString = keyResult.value instanceof Uint8Array ? Array.from(keyResult.value).map((b) => b.toString(16).padStart(2, "0")).join("") : String(keyResult.value);
1822
- if (seenKeys.has(keyString)) {
1823
- const mode = ctx.options?.dupMapKeyMode || "allow";
1824
- if (mode === "reject") {
1825
- throw new Error(`Duplicate map key detected: ${keyString} at offset ${currentOffset}`);
1826
- } else if (mode === "warn") {
1827
- logger.warn(`Duplicate map key detected: ${keyString} at offset ${currentOffset}`);
2076
+ try {
2077
+ const childPaths = [];
2078
+ const seenKeys = /* @__PURE__ */ new Set();
2079
+ if (isIndefinite) {
2080
+ let count = 0;
2081
+ let foundBreak = false;
2082
+ while (currentOffset < ctx.buffer.length) {
2083
+ const nextByte = readByte(ctx.buffer, currentOffset);
2084
+ if (nextByte === 255) {
2085
+ currentOffset++;
2086
+ foundBreak = true;
2087
+ break;
2088
+ }
2089
+ if (ctx.options?.limits?.maxMapSize && count >= ctx.options.limits.maxMapSize) {
2090
+ throw new Error(`Map size exceeds limit of ${ctx.options.limits.maxMapSize}`);
2091
+ }
2092
+ const keyPath = `${path}${path ? "." : ""}#key`;
2093
+ const keyResult = parseValueWithMap(ctx, currentOffset, keyPath, sourceMap);
2094
+ currentOffset += keyResult.bytesRead;
2095
+ const keyForDupCheck = serializeValueForComparison(keyResult.value);
2096
+ const keyString = keyResult.value instanceof Uint8Array ? Array.from(keyResult.value).map((b) => b.toString(16).padStart(2, "0")).join("") : String(keyResult.value);
2097
+ if (seenKeys.has(keyForDupCheck)) {
2098
+ const mode = ctx.options?.dupMapKeyMode || "allow";
2099
+ if (mode === "reject") {
2100
+ throw new Error(`Duplicate map key detected: ${keyString} at offset ${currentOffset}`);
2101
+ } else if (mode === "warn") {
2102
+ logger.warn(`Duplicate map key detected: ${keyString} at offset ${currentOffset}`);
2103
+ }
2104
+ }
2105
+ seenKeys.add(keyForDupCheck);
2106
+ const valuePath = path ? `${path}.${keyString}` : `.${keyString}`;
2107
+ childPaths.push(valuePath);
2108
+ const valueResult = parseValueWithMap(ctx, currentOffset, valuePath, sourceMap);
2109
+ map.set(keyResult.value, valueResult.value);
2110
+ currentOffset += valueResult.bytesRead;
2111
+ const valueEntry = sourceMap.find((e) => e.path === valuePath);
2112
+ if (valueEntry) {
2113
+ valueEntry.parent = path;
1828
2114
  }
2115
+ count++;
1829
2116
  }
1830
- seenKeys.add(keyString);
1831
- const valuePath = path ? `${path}.${keyString}` : `.${keyString}`;
1832
- childPaths.push(valuePath);
1833
- const valueResult = parseValueWithMap(ctx, currentOffset, valuePath, sourceMap);
1834
- map.set(keyResult.value, valueResult.value);
1835
- currentOffset += valueResult.bytesRead;
1836
- const valueEntry = sourceMap.find((e) => e.path === valuePath);
1837
- if (valueEntry) {
1838
- valueEntry.parent = path;
2117
+ if (!foundBreak) {
2118
+ throw new Error("Indefinite-length map missing break code (0xFF)");
1839
2119
  }
1840
- }
1841
- } else {
1842
- for (let i = 0; i < length; i++) {
1843
- const keyPath = `${path}${path ? "." : ""}#key${i}`;
1844
- const keyResult = parseValueWithMap(ctx, currentOffset, keyPath, sourceMap);
1845
- currentOffset += keyResult.bytesRead;
1846
- const keyString = keyResult.value instanceof Uint8Array ? Array.from(keyResult.value).map((b) => b.toString(16).padStart(2, "0")).join("") : String(keyResult.value);
1847
- if (seenKeys.has(keyString)) {
1848
- const mode = ctx.options?.dupMapKeyMode || "allow";
1849
- if (mode === "reject") {
1850
- throw new Error(`Duplicate map key detected: ${keyString} at offset ${currentOffset}`);
1851
- } else if (mode === "warn") {
1852
- logger.warn(`Duplicate map key detected: ${keyString} at offset ${currentOffset}`);
1853
- }
2120
+ } else {
2121
+ if (ctx.options?.limits?.maxMapSize && length > ctx.options.limits.maxMapSize) {
2122
+ throw new Error(`Map size ${length} exceeds limit of ${ctx.options.limits.maxMapSize}`);
1854
2123
  }
1855
- seenKeys.add(keyString);
1856
- const valuePath = path ? `${path}.${keyString}` : `.${keyString}`;
1857
- childPaths.push(valuePath);
1858
- const valueResult = parseValueWithMap(ctx, currentOffset, valuePath, sourceMap);
1859
- map.set(keyResult.value, valueResult.value);
1860
- currentOffset += valueResult.bytesRead;
1861
- const valueEntry = sourceMap.find((e) => e.path === valuePath);
1862
- if (valueEntry) {
1863
- valueEntry.parent = path;
2124
+ for (let i = 0; i < length; i++) {
2125
+ const keyPath = `${path}${path ? "." : ""}#key${i}`;
2126
+ const keyResult = parseValueWithMap(ctx, currentOffset, keyPath, sourceMap);
2127
+ currentOffset += keyResult.bytesRead;
2128
+ const keyForDupCheck = serializeValueForComparison(keyResult.value);
2129
+ const keyString = keyResult.value instanceof Uint8Array ? Array.from(keyResult.value).map((b) => b.toString(16).padStart(2, "0")).join("") : String(keyResult.value);
2130
+ if (seenKeys.has(keyForDupCheck)) {
2131
+ const mode = ctx.options?.dupMapKeyMode || "allow";
2132
+ if (mode === "reject") {
2133
+ throw new Error(`Duplicate map key detected: ${keyString} at offset ${currentOffset}`);
2134
+ } else if (mode === "warn") {
2135
+ logger.warn(`Duplicate map key detected: ${keyString} at offset ${currentOffset}`);
2136
+ }
2137
+ }
2138
+ seenKeys.add(keyForDupCheck);
2139
+ const valuePath = path ? `${path}.${keyString}` : `.${keyString}`;
2140
+ childPaths.push(valuePath);
2141
+ const valueResult = parseValueWithMap(ctx, currentOffset, valuePath, sourceMap);
2142
+ map.set(keyResult.value, valueResult.value);
2143
+ currentOffset += valueResult.bytesRead;
2144
+ const valueEntry = sourceMap.find((e) => e.path === valuePath);
2145
+ if (valueEntry) {
2146
+ valueEntry.parent = path;
2147
+ }
1864
2148
  }
1865
2149
  }
2150
+ const bytesRead = currentOffset - offset;
2151
+ if (sourceMap[mapEntryIndex]) {
2152
+ sourceMap[mapEntryIndex].children = childPaths;
2153
+ }
2154
+ return {
2155
+ value: map,
2156
+ bytesRead
2157
+ };
2158
+ } finally {
2159
+ ctx.currentDepth = previousDepth;
1866
2160
  }
1867
- const bytesRead = currentOffset - offset;
1868
- if (sourceMap[mapEntryIndex]) {
1869
- sourceMap[mapEntryIndex].children = childPaths;
1870
- }
1871
- return {
1872
- value: map,
1873
- bytesRead
1874
- };
1875
2161
  };
1876
2162
  const parseTagNumberHelper = (buffer, offset, ai) => {
1877
2163
  if (ai < 24) {
@@ -1930,10 +2216,29 @@ function useCborParser() {
1930
2216
  if (valueEntry) {
1931
2217
  valueEntry.parent = path;
1932
2218
  }
1933
- const hexString = Array.from(ctx.buffer.slice(startOffset, currentOffset)).map((b) => b.toString(16).padStart(2, "0")).join("");
1934
- const tagResult = parseTag(hexString, ctx.options);
2219
+ let finalValue = valueResult.value;
2220
+ if ((tagNumber === 2 || tagNumber === 3) && finalValue instanceof Uint8Array) {
2221
+ const maxBignumBytes = ctx.options?.limits?.maxBignumBytes ?? chunkPD72MVTX_cjs.DEFAULT_LIMITS.maxBignumBytes;
2222
+ if (finalValue.length > maxBignumBytes) {
2223
+ throw new Error(
2224
+ `Bignum (tag ${tagNumber}) size ${finalValue.length} bytes exceeds limit of ${maxBignumBytes} bytes`
2225
+ );
2226
+ }
2227
+ let bigintValue = 0n;
2228
+ for (let i = 0; i < finalValue.length; i++) {
2229
+ bigintValue = bigintValue << 8n | BigInt(finalValue[i]);
2230
+ }
2231
+ finalValue = tagNumber === 2 ? bigintValue : -1n - bigintValue;
2232
+ }
2233
+ validateTagSemantics(tagNumber, finalValue, ctx.options);
2234
+ const plutusConstr = decodePlutusConstructor(tagNumber, finalValue);
2235
+ const taggedValue = {
2236
+ tag: tagNumber,
2237
+ value: finalValue,
2238
+ ...plutusConstr && { plutus: plutusConstr }
2239
+ };
1935
2240
  return {
1936
- value: tagResult.value,
2241
+ value: taggedValue,
1937
2242
  bytesRead: currentOffset - startOffset
1938
2243
  };
1939
2244
  };
@@ -1948,28 +2253,42 @@ function useCborParser() {
1948
2253
  if (ai < 20) return `Simple Value ${ai}`;
1949
2254
  return "Simple Value";
1950
2255
  };
1951
- const parseSequence = (hexString, options) => {
1952
- const cleanHex = hexString.replace(/\s+/g, "");
1953
- if (!cleanHex || cleanHex.length === 0) {
1954
- return [];
1955
- }
1956
- if (cleanHex.length % 2 !== 0) {
1957
- throw new Error("Hex string must have even length");
1958
- }
1959
- if (!/^[0-9a-fA-F]+$/.test(cleanHex)) {
1960
- throw new Error(`Invalid hex character in: ${cleanHex}`);
1961
- }
2256
+ const parseSequence = (input, options) => {
1962
2257
  const mergedOptions = mergeOptions(options);
1963
- const buffer = hexToBytes(cleanHex);
2258
+ let buffer;
2259
+ if (input instanceof Uint8Array) {
2260
+ if (input.length === 0) {
2261
+ return [];
2262
+ }
2263
+ buffer = input;
2264
+ } else {
2265
+ const cleanHex = input.replace(/\s+/g, "");
2266
+ if (!cleanHex || cleanHex.length === 0) {
2267
+ return [];
2268
+ }
2269
+ if (cleanHex.length % 2 !== 0) {
2270
+ throw new Error("Hex string must have even length");
2271
+ }
2272
+ if (!/^[0-9a-fA-F]+$/.test(cleanHex)) {
2273
+ throw new Error(`Invalid hex character in: ${cleanHex}`);
2274
+ }
2275
+ buffer = hexToBytes(cleanHex);
2276
+ }
1964
2277
  const results = [];
1965
2278
  let offset = 0;
2279
+ const sequenceStartTime = mergedOptions.limits?.maxParseTime ? Date.now() : 0;
1966
2280
  while (offset < buffer.length) {
2281
+ if (sequenceStartTime > 0 && mergedOptions.limits?.maxParseTime) {
2282
+ const elapsed = Date.now() - sequenceStartTime;
2283
+ if (elapsed > mergedOptions.limits.maxParseTime) {
2284
+ throw new Error(`Parse timeout: exceeded ${mergedOptions.limits.maxParseTime}ms limit`);
2285
+ }
2286
+ }
1967
2287
  const byte = readByte(buffer, offset);
1968
2288
  if (byte === 255) {
1969
2289
  throw new Error(`Unexpected break code (0xff) at offset ${offset} - not inside indefinite-length item`);
1970
2290
  }
1971
- const remainingHex = Array.from(buffer.slice(offset)).map((b) => b.toString(16).padStart(2, "0")).join("");
1972
- const result = parse(remainingHex, mergedOptions);
2291
+ const result = dispatchFromBuffer(buffer, offset, mergedOptions);
1973
2292
  results.push(result.value);
1974
2293
  offset += result.bytesRead;
1975
2294
  }
@@ -2030,5 +2349,5 @@ exports.useCborParser = useCborParser;
2030
2349
  exports.useCborString = useCborString;
2031
2350
  exports.useCborTag = useCborTag;
2032
2351
  exports.validateUtf8Strict = validateUtf8Strict;
2033
- //# sourceMappingURL=chunk-2HBCILJS.cjs.map
2034
- //# sourceMappingURL=chunk-2HBCILJS.cjs.map
2352
+ //# sourceMappingURL=chunk-RVG2BY32.cjs.map
2353
+ //# sourceMappingURL=chunk-RVG2BY32.cjs.map