diff --git a/src/nte_history_exporter/decoder/arc.py b/src/nte_history_exporter/decoder/arc.py index 8ae6984..c724a14 100644 --- a/src/nte_history_exporter/decoder/arc.py +++ b/src/nte_history_exporter/decoder/arc.py @@ -50,9 +50,14 @@ def decode_arc_key(raw: bytes) -> str | None: def decode_arc_timestamp(raw8: bytes) -> tuple[int, float, str]: + if len(raw8) != 8: + raise ValueError("arc timestamps must be exactly 8 bytes") ticks = struct.unpack(" list[dict[str, Any]]: pos += 8 arc_id = decode_arc_key(name_raw) or name_raw.hex() meta = ARC_META.get(arc_id, {}) - ticks, unix_seconds, timestamp_decoded = decode_arc_timestamp(timestamp_raw) + try: + ticks, unix_seconds, timestamp_decoded = decode_arc_timestamp(timestamp_raw) + except ValueError: + return [] records.append( { "record_start": start, diff --git a/tests/test_boundary_export.py b/tests/test_boundary_export.py index 40b8372..84dfe9e 100644 --- a/tests/test_boundary_export.py +++ b/tests/test_boundary_export.py @@ -356,6 +356,16 @@ class BoundaryExportTests(unittest.TestCase): self.assertEqual(decoded[0]["reward_type"], "arc") self.assertEqual(decoded[0]["reward_key_hex"], reference_rows[0]["arc_key_hex"]) + def test_arc_response_parser_rejects_invalid_timestamp_noise(self): + response = bytearray(0x4C) + response += (10).to_bytes(4, "little") + response += bytes.fromhex("ccdee4d6be") + response += (8).to_bytes(4, "little") + response += b"garb" + response += (0xFFFFFFFFFFFFFFFF).to_bytes(8, "little") + + self.assertEqual(parse_arc_response(bytes(response)), []) + def test_arc_partial_timestamp_group_is_exported_without_warning(self): rows = load_arc_csv("arc_pages_1_to_5_v2.csv") pairs = []