File protobuf-CVE-2026-0994.patch of Package protobuf.42475
--- a/python/google/protobuf/json_format.py
+++ b/python/google/protobuf/json_format.py
@@ -391,7 +391,13 @@ def _CreateMessageFromTypeUrl(type_url,
return message_class()
-def Parse(text, message, ignore_unknown_fields=False, descriptor_pool=None):
+def Parse(
+ text,
+ message,
+ ignore_unknown_fields=False,
+ descriptor_pool=None,
+ max_recursion_depth=100,
+):
"""Parses a JSON representation of a protocol message into a message.
Args:
@@ -400,6 +406,9 @@ def Parse(text, message, ignore_unknown_
ignore_unknown_fields: If True, do not raise errors for unknown fields.
descriptor_pool: A Descriptor Pool for resolving types. If None use the
default.
+ max_recursion_depth: max recursion depth of JSON message to be deserialized.
+ JSON messages over this depth will fail to be deserialized. Default value
+ is 100.
Returns:
The same message passed as argument.
@@ -412,13 +421,25 @@ def Parse(text, message, ignore_unknown_
js = json.loads(text, object_pairs_hook=_DuplicateChecker)
except ValueError as e:
raise ParseError('Failed to load JSON: {0}.'.format(str(e)))
- return ParseDict(js, message, ignore_unknown_fields, descriptor_pool)
-
-def ParseDict(js_dict,
- message,
- ignore_unknown_fields=False,
- descriptor_pool=None):
+ try:
+ return ParseDict(
+ js, message, ignore_unknown_fields, descriptor_pool, max_recursion_depth
+ )
+ except ParseError as e:
+ raise e
+ except Exception as e:
+ raise ParseError(
+ 'Failed to parse JSON: {0}: {1}.'.format(type(e).__name__, str(e))
+ ) from e
+
+def ParseDict(
+ js_dict,
+ message,
+ ignore_unknown_fields=False,
+ descriptor_pool=None,
+ max_recursion_depth=100,
+):
"""Parses a JSON dictionary representation into a message.
Args:
@@ -427,11 +448,14 @@ def ParseDict(js_dict,
ignore_unknown_fields: If True, do not raise errors for unknown fields.
descriptor_pool: A Descriptor Pool for resolving types. If None use the
default.
+ max_recursion_depth: max recursion depth of JSON message to be deserialized.
+ JSON messages over this depth will fail to be deserialized. Default value
+ is 100.
Returns:
The same message passed as argument.
"""
- parser = _Parser(ignore_unknown_fields, descriptor_pool)
+ parser = _Parser(ignore_unknown_fields, descriptor_pool, max_recursion_depth)
parser.ConvertMessage(js_dict, message)
return message
@@ -442,9 +466,13 @@ _INT_OR_FLOAT = six.integer_types + (flo
class _Parser(object):
"""JSON format parser for protocol message."""
- def __init__(self, ignore_unknown_fields, descriptor_pool):
+ def __init__(
+ self, ignore_unknown_fields, descriptor_pool, max_recursion_depth
+ ):
self.ignore_unknown_fields = ignore_unknown_fields
self.descriptor_pool = descriptor_pool
+ self.max_recursion_depth = max_recursion_depth
+ self.recursion_depth = 0
def ConvertMessage(self, value, message):
"""Convert a JSON object into a message.
@@ -456,6 +484,17 @@ class _Parser(object):
Raises:
ParseError: In case of convert problems.
"""
+ # Increment recursion depth at message entry. The max_recursion_depth limit
+ # is exclusive: a depth value equal to max_recursion_depth will trigger an
+ # error. For example, with max_recursion_depth=5, nesting up to depth 4 is
+ # allowed, but attempting depth 5 raises ParseError.
+ self.recursion_depth += 1
+ if self.recursion_depth > self.max_recursion_depth:
+ raise ParseError(
+ 'Message too deep. Max recursion depth is {0}'.format(
+ self.max_recursion_depth
+ )
+ )
message_descriptor = message.DESCRIPTOR
full_name = message_descriptor.full_name
if _IsWrapperMessage(message_descriptor):
@@ -464,6 +503,7 @@ class _Parser(object):
methodcaller(_WKTJSONMETHODS[full_name][1], value, message)(self)
else:
self._ConvertFieldValuePair(value, message)
+ self.recursion_depth -= 1
def _ConvertFieldValuePair(self, js, message):
"""Convert field value pairs into regular message.
@@ -595,8 +635,9 @@ class _Parser(object):
if _IsWrapperMessage(message_descriptor):
self._ConvertWrapperMessage(value['value'], sub_message)
elif full_name in _WKTJSONMETHODS:
- methodcaller(
- _WKTJSONMETHODS[full_name][1], value['value'], sub_message)(self)
+ # For well-known types (including nested Any), use ConvertMessage
+ # to ensure recursion depth is properly tracked
+ self.ConvertMessage(value['value'], sub_message)
else:
del value['@type']
self._ConvertFieldValuePair(value, sub_message)