From 44e64d49cd972d04d859ad22347e0e10c74d4161 Mon Sep 17 00:00:00 2001 From: mohammed arib Date: Mon, 13 Jul 2026 13:25:14 +0530 Subject: [PATCH] reject negative enum and union index in python binary decoder --- lang/py/avro/io.py | 6 +++--- lang/py/avro/test/test_io.py | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/lang/py/avro/io.py b/lang/py/avro/io.py index 4be3d8e0574..5b012a9066a 100644 --- a/lang/py/avro/io.py +++ b/lang/py/avro/io.py @@ -765,7 +765,7 @@ def read_enum(self, writers_schema: avro.schema.EnumSchema, readers_schema: avro """ # read data index_of_symbol = decoder.read_int() - if index_of_symbol >= len(writers_schema.symbols): + if not 0 <= index_of_symbol < len(writers_schema.symbols): raise avro.errors.SchemaResolutionException( f"Can't access enum index {index_of_symbol} for enum with {len(writers_schema.symbols)} symbols", writers_schema, readers_schema ) @@ -864,7 +864,7 @@ def read_union(self, writers_schema: avro.schema.UnionSchema, readers_schema: av """ # schema resolution index_of_schema = int(decoder.read_long()) - if index_of_schema >= len(writers_schema.schemas): + if not 0 <= index_of_schema < len(writers_schema.schemas): raise avro.errors.SchemaResolutionException( f"Can't access branch index {index_of_schema} for union with {len(writers_schema.schemas)} branches", writers_schema, readers_schema ) @@ -875,7 +875,7 @@ def read_union(self, writers_schema: avro.schema.UnionSchema, readers_schema: av def skip_union(self, writers_schema: avro.schema.UnionSchema, decoder: BinaryDecoder) -> None: index_of_schema = int(decoder.read_long()) - if index_of_schema >= len(writers_schema.schemas): + if not 0 <= index_of_schema < len(writers_schema.schemas): raise avro.errors.SchemaResolutionException( f"Can't access branch index {index_of_schema} for union with {len(writers_schema.schemas)} branches", writers_schema ) diff --git a/lang/py/avro/test/test_io.py b/lang/py/avro/test/test_io.py index 41a0e366bad..cd3f710bfed 100644 --- a/lang/py/avro/test/test_io.py +++ b/lang/py/avro/test/test_io.py @@ -598,6 +598,22 @@ def test_unknown_symbol(self) -> None: datum_reader = avro.io.DatumReader(writers_schema, readers_schema) self.assertRaises(avro.errors.SchemaResolutionException, datum_reader.read, decoder) + def test_negative_enum_index(self) -> None: + # A negative zigzag-encoded index (0x01 -> -1) must be rejected rather than + # silently wrapping to symbols[-1] via Python negative indexing. + writers_schema = avro.schema.parse(json.dumps({"type": "enum", "name": "Test", "symbols": ["FOO", "BAR"]})) + decoder = avro.io.BinaryDecoder(io.BytesIO(b"\x01")) + datum_reader = avro.io.DatumReader(writers_schema, writers_schema) + self.assertRaises(avro.errors.SchemaResolutionException, datum_reader.read, decoder) + + def test_negative_union_index(self) -> None: + # A negative branch index must be rejected rather than selecting schemas[-1] + # and decoding the payload with the wrong branch schema. + writers_schema = avro.schema.parse(json.dumps(["null", "string", "long"])) + decoder = avro.io.BinaryDecoder(io.BytesIO(b"\x01\x08")) + datum_reader = avro.io.DatumReader(writers_schema, writers_schema) + self.assertRaises(avro.errors.SchemaResolutionException, datum_reader.read, decoder) + def test_no_default_value(self) -> None: writers_schema = LONG_RECORD_SCHEMA datum_to_write = LONG_RECORD_DATUM