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