From 6f4916b9dce7a9f53eb1741592318de60f2a7ad2 Mon Sep 17 00:00:00 2001 From: Hayleigh Thompson Date: Sun, 19 Jul 2026 00:24:35 +0200 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Panic=20on=20cbor=20ints?= =?UTF-8?q?=20>=3D=2053=20bits=20on=20the=20javascript=20target.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/at/cbor.gleam | 227 +++++++++++++++++++++------------------------- 1 file changed, 103 insertions(+), 124 deletions(-) diff --git a/src/at/cbor.gleam b/src/at/cbor.gleam index 084df23..6949c2e 100644 --- a/src/at/cbor.gleam +++ b/src/at/cbor.gleam @@ -64,123 +64,57 @@ fn parse_dynamic(bits: BitArray) -> Result(#(Dynamic, BitArray), DecodeError) { <<>> -> Error(UnexpectedEndOfInput) // UNSIGNED INTS - <<0:3, value:5, remaining:bits>> if value <= 23 -> - Ok(#(dynamic.int(value), remaining)) - - <<0:3, 24:5, value:8, remaining:bits>> - | <<0:3, 25:5, value:16, remaining:bits>> - | <<0:3, 26:5, value:32, remaining:bits>> - | <<0:3, 27:5, value:64, remaining:bits>> -> - Ok(#(dynamic.int(value), remaining)) + <<0:3, size_indicator:5, remaining:bits>> -> + case parse_int(size_indicator, remaining) { + Ok(#(value, remaining)) -> Ok(#(dynamic.int(value), remaining)) + Error(reason) -> Error(reason) + } // SIGNED INTS - <<1:3, value:5, remaining:bits>> if value <= 23 -> - Ok(#(dynamic.int(-1 - value), remaining)) - - <<1:3, 24:5, value:8, remaining:bits>> - | <<1:3, 25:5, value:16, remaining:bits>> - | <<1:3, 26:5, value:32, remaining:bits>> - | <<1:3, 27:5, value:64, remaining:bits>> -> - Ok(#(dynamic.int(-1 - value), remaining)) + <<1:3, size_indicator:5, remaining:bits>> -> + case parse_int(size_indicator, remaining) { + Ok(#(value, remaining)) -> Ok(#(dynamic.int(-1 - value), remaining)) + Error(reason) -> Error(reason) + } // BYTES - <<2:3, size:5, bits:bytes-size(size), remaining:bits>> if size <= 23 -> - Ok(#(dynamic.bit_array(bits), remaining)) - - <<2:3, 24:5, size:8, bits:bytes-size(size), remaining:bits>> - | <<2:3, 25:5, size:16, bits:bytes-size(size), remaining:bits>> - | <<2:3, 26:5, size:32, bits:bytes-size(size), remaining:bits>> - | <<2:3, 27:5, size:64, bits:bytes-size(size), remaining:bits>> -> - Ok(#(dynamic.bit_array(bits), remaining)) - - // STRINGS - <<3:3, size:5, bits:bytes-size(size), remaining:bits>> if size <= 23 -> - case bit_array.to_string(bits) { - Ok(string) -> Ok(#(dynamic.string(string), remaining)) - Error(_) -> Error(MalformedInput) + <<2:3, size_indicator:5, remaining:bits>> -> + case parse_bytes(size_indicator, remaining) { + Ok(#(bytes, remaining)) -> Ok(#(dynamic.bit_array(bytes), remaining)) + Error(reason) -> Error(reason) } - <<3:3, 24:5, size:8, bits:bytes-size(size), remaining:bits>> - | <<3:3, 25:5, size:16, bits:bytes-size(size), remaining:bits>> - | <<3:3, 26:5, size:32, bits:bytes-size(size), remaining:bits>> - | <<3:3, 27:5, size:64, bits:bytes-size(size), remaining:bits>> -> - case bit_array.to_string(bits) { - Ok(string) -> Ok(#(dynamic.string(string), remaining)) - Error(_) -> Error(MalformedInput) + // STRINGS + <<3:3, size_indicator:5, remaining:bits>> -> + case parse_string(size_indicator, remaining) { + Ok(#(string, remaining)) -> Ok(#(dynamic.string(string), remaining)) + Error(reason) -> Error(reason) } // ARRAYS - <<4:3, size:5, remaining:bits>> if size <= 23 -> - do_parse_dynamic_array(remaining, size, []) - - <<4:3, 24:5, size:8, remaining:bits>> - | <<4:3, 25:5, size:16, remaining:bits>> - | <<4:3, 26:5, size:32, remaining:bits>> - | <<4:3, 27:5, size:64, remaining:bits>> -> - do_parse_dynamic_array(remaining, size, []) + <<4:3, size_indicator:5, remaining:bits>> -> + case parse_int(size_indicator, remaining) { + Ok(#(count, remaining)) -> do_parse_dynamic_array(remaining, count, []) + Error(reason) -> Error(reason) + } // MAPS - <<5:3, size:5, remaining:bits>> if size <= 23 -> - do_parse_dynamic_map(remaining, size, []) - - <<5:3, 24:5, size:8, remaining:bits>> - | <<5:3, 25:5, size:16, remaining:bits>> - | <<5:3, 26:5, size:32, remaining:bits>> - | <<5:3, 27:5, size:64, remaining:bits>> -> - do_parse_dynamic_map(remaining, size, []) + <<5:3, size_indicator:5, remaining:bits>> -> + case parse_int(size_indicator, remaining) { + Ok(#(count, remaining)) -> do_parse_dynamic_map(remaining, count, []) + Error(reason) -> Error(reason) + } // CIDS - <<6:3, 24:5, 42:8, 2:3, size:5, bits:bytes-size(size), remaining:bits>> - if size <= 23 - -> - case bits { - <<0:8, cid:bits>> -> Ok(#(dynamic.bit_array(cid), remaining)) - _ -> Error(MalformedInput) - } + <<6:3, 24:5, 42:8, 2:3, size_indicator:5, remaining:bits>> -> + case parse_bytes(size_indicator, remaining) { + Ok(#(bytes, remaining)) -> + case bytes { + <<0:8, cid:bits>> -> Ok(#(dynamic.bit_array(cid), remaining)) + _ -> Error(MalformedInput) + } - << - 6:3, - 24:5, - 42:8, - 2:3, - 24:5, - size:8, - bits:bytes-size(size), - remaining:bits, - >> - | << - 6:3, - 24:5, - 42:8, - 2:3, - 25:5, - size:16, - bits:bytes-size(size), - remaining:bits, - >> - | << - 6:3, - 24:5, - 42:8, - 2:3, - 26:5, - size:32, - bits:bytes-size(size), - remaining:bits, - >> - | << - 6:3, - 24:5, - 42:8, - 2:3, - 27:5, - size:64, - bits:bytes-size(size), - remaining:bits, - >> -> - case bits { - <<0:8, cid:bits>> -> Ok(#(dynamic.bit_array(cid), remaining)) - _ -> Error(MalformedInput) + Error(reason) -> Error(reason) } // BOOLS @@ -197,6 +131,68 @@ fn parse_dynamic(bits: BitArray) -> Result(#(Dynamic, BitArray), DecodeError) { } } +fn parse_int( + size_indicator: Int, + bits: BitArray, +) -> Result(#(Int, BitArray), DecodeError) { + case size_indicator, bits { + n, _ if n <= 23 -> Ok(#(n, bits)) + 24, <> -> Ok(#(value, remaining)) + 25, <> -> Ok(#(value, remaining)) + 26, <> -> Ok(#(value, remaining)) + 27, bits -> parse_int64(bits) + _, _ -> Error(MalformedInput) + } +} + +@target(erlang) +fn parse_int64(bits: BitArray) -> Result(#(Int, BitArray), DecodeError) { + case bits { + <> -> Ok(#(value, remaining)) + _ -> Error(MalformedInput) + } +} + +@target(javascript) +fn parse_int64(bits: BitArray) -> Result(#(Int, BitArray), DecodeError) { + case bits { + <> if value <= 9_007_199_254_740_991 -> + Ok(#(value, remaining)) + + _ -> Error(MalformedInput) + } +} + +fn parse_bytes( + size_indicator: Int, + bits: BitArray, +) -> Result(#(BitArray, BitArray), DecodeError) { + case parse_int(size_indicator, bits) { + Ok(#(size, remaining)) -> + case remaining { + <> -> Ok(#(bytes, remaining)) + _ -> Error(MalformedInput) + } + + Error(reason) -> Error(reason) + } +} + +fn parse_string( + size: Int, + bits: BitArray, +) -> Result(#(String, BitArray), DecodeError) { + case parse_bytes(size, bits) { + Ok(#(bytes, remaining)) -> + case bit_array.to_string(bytes) { + Ok(value) -> Ok(#(value, remaining)) + Error(_) -> Error(MalformedInput) + } + + Error(reason) -> Error(reason) + } +} + fn do_parse_dynamic_array( bits: BitArray, count: Int, @@ -221,9 +217,9 @@ fn do_parse_dynamic_map( case count, bits { 0, remaining -> Ok(#(dynamic.properties(entries), remaining)) - _, <<3:3, size:5, bits:bytes-size(size), remaining:bits>> if size <= 23 -> - case bit_array.to_string(bits) { - Ok(key) -> + _, <<3:3, size:5, remaining:bits>> -> + case parse_string(size, remaining) { + Ok(#(key, remaining)) -> case parse_dynamic(remaining) { Ok(#(value, remaining)) -> do_parse_dynamic_map(remaining, count - 1, [ @@ -233,25 +229,8 @@ fn do_parse_dynamic_map( Error(reason) -> Error(reason) } - Error(_) -> Error(MalformedInput) - } - _, <<3:3, 24:5, size:8, bits:bytes-size(size), remaining:bits>> - | _, <<3:3, 25:5, size:16, bits:bytes-size(size), remaining:bits>> - | _, <<3:3, 26:5, size:32, bits:bytes-size(size), remaining:bits>> - | _, <<3:3, 27:5, size:64, bits:bytes-size(size), remaining:bits>> - -> - case bit_array.to_string(bits) { - Ok(key) -> - case parse_dynamic(remaining) { - Ok(#(value, remaining)) -> - do_parse_dynamic_map(remaining, count - 1, [ - #(dynamic.string(key), value), - ..entries - ]) - Error(reason) -> Error(reason) - } - Error(_) -> Error(MalformedInput) + Error(reason) -> Error(reason) } _, _ -> Error(MalformedInput) -- 2.51.2