summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBob Ippolito <bob@redivi.com>2023-04-04 10:59:13 -0700
committerBob Ippolito <bob@redivi.com>2023-04-04 10:59:13 -0700
commit59dac4e82cd6766fc31a9389d573d732580eaab5 (patch)
tree2cba0cbde6419fa11c6111d82f64b989c46455ec
parent1e495c1199f56c2e8e7931f20bda7481842e18ff (diff)
downloadsimplejson-59dac4e82cd6766fc31a9389d573d732580eaab5.tar.gz
SJ-PT-23-03: Backport integer string length limitation to limit quadratic parsing
-rw-r--r--CHANGES.txt2
-rw-r--r--index.rst14
-rw-r--r--simplejson/__init__.py4
-rw-r--r--simplejson/decoder.py15
-rw-r--r--simplejson/tests/test_decode.py8
5 files changed, 40 insertions, 3 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index ddb2bd8..b70200f 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -4,6 +4,8 @@ Version 3.19.0 released 2023-04-XX
implementation of the decoder (SJ-PT-23-01)
* Fix missing reference count decrease if PyOS_string_to_double raises
an exception in Python 2.x; was probably unreachable (SJ-PT-23-02)
+* Backport the integer string length limitation from Python 3.11 to
+ limit quadratic number parsing (SJ-PT-23-03)
Version 3.18.4 released 2023-03-14
diff --git a/index.rst b/index.rst
index fbb52b5..a10ccb7 100644
--- a/index.rst
+++ b/index.rst
@@ -412,6 +412,13 @@ Basic Usage
be used to use another datatype or parser for JSON integers
(e.g. :class:`float`).
+ .. versionchanged:: 3.19.0
+ The integer to string conversion length limitation introduced in
+ Python 3.11 has been backported. An attempt to parse an integer
+ with more than 4300 digits will result in an exception unless a
+ suitable alternative parser is specified
+ (e.g. :class:`decimal.Decimal`)
+
*parse_constant*, if specified, will be called with one of the following
strings: ``'-Infinity'``, ``'Infinity'``, ``'NaN'``. This can be used to
raise an exception if invalid JSON numbers are encountered.
@@ -502,6 +509,13 @@ Encoders and decoders
be used to use another datatype or parser for JSON integers
(e.g. :class:`float`).
+ .. versionchanged:: 3.19.0
+ The integer to string conversion length limitation introduced in
+ Python 3.11 has been backported. An attempt to parse an integer
+ with more than 4300 digits will result in an exception unless a
+ suitable alternative parser is specified
+ (e.g. :class:`decimal.Decimal`)
+
*parse_constant*, if specified, will be called with one of the following
strings: ``'-Infinity'``, ``'Infinity'``, ``'NaN'``. This can be used to
raise an exception if invalid JSON numbers are encountered.
diff --git a/simplejson/__init__.py b/simplejson/__init__.py
index 2f0bebf..92b6009 100644
--- a/simplejson/__init__.py
+++ b/simplejson/__init__.py
@@ -442,12 +442,12 @@ def load(fp, encoding=None, cls=None, object_hook=None, parse_float=None,
takes priority.
*parse_float*, if specified, will be called with the string of every
- JSON float to be decoded. By default, this is equivalent to
+ JSON float to be decoded. By default, this is equivalent to
``float(num_str)``. This can be used to use another datatype or parser
for JSON floats (e.g. :class:`decimal.Decimal`).
*parse_int*, if specified, will be called with the string of every
- JSON int to be decoded. By default, this is equivalent to
+ JSON int to be decoded. By default, this is equivalent to
``int(num_str)``. This can be used to use another datatype or parser
for JSON integers (e.g. :class:`float`).
diff --git a/simplejson/decoder.py b/simplejson/decoder.py
index e1f10ae..c28e445 100644
--- a/simplejson/decoder.py
+++ b/simplejson/decoder.py
@@ -46,6 +46,19 @@ BACKSLASH = {
DEFAULT_ENCODING = "utf-8"
+if hasattr(sys, 'get_int_max_str_digits'):
+ bounded_int = int
+else:
+ def bounded_int(s, INT_MAX_STR_DIGITS=4300):
+ """Backport of the integer string length conversion limitation
+
+ https://docs.python.org/3/library/stdtypes.html#int-max-str-digits
+ """
+ if len(s) > INT_MAX_STR_DIGITS:
+ raise ValueError("Exceeds the limit (%s) for integer string conversion: value has %s digits" % (INT_MAX_STR_DIGITS, len(s)))
+ return int(s)
+
+
def scan_four_digit_hex(s, end, _m=re.compile(r'^[0-9a-fA-F]{4}$').match):
"""Scan a four digit hex number from s[end:end + 4]
"""
@@ -349,7 +362,7 @@ class JSONDecoder(object):
self.object_hook = object_hook
self.object_pairs_hook = object_pairs_hook
self.parse_float = parse_float or float
- self.parse_int = parse_int or int
+ self.parse_int = parse_int or bounded_int
self.parse_constant = parse_constant or _CONSTANTS.__getitem__
self.strict = strict
self.parse_object = JSONObject
diff --git a/simplejson/tests/test_decode.py b/simplejson/tests/test_decode.py
index 6960ee5..317b4f9 100644
--- a/simplejson/tests/test_decode.py
+++ b/simplejson/tests/test_decode.py
@@ -2,6 +2,7 @@ from __future__ import absolute_import
import decimal
from unittest import TestCase
+import sys
import simplejson as json
from simplejson.compat import StringIO, b, binary_type
from simplejson import OrderedDict
@@ -117,3 +118,10 @@ class TestDecode(TestCase):
diff = id(x) - id(y)
self.assertRaises(ValueError, j.scan_once, y, diff)
self.assertRaises(ValueError, j.raw_decode, y, i)
+
+ def test_bounded_int(self):
+ # SJ-PT-23-03, limit quadratic number parsing per Python 3.11
+ max_str_digits = getattr(sys, 'get_int_max_str_digits', lambda: 4300)()
+ s = '1' + '0' * (max_str_digits - 1)
+ self.assertEqual(json.loads(s), int(s))
+ self.assertRaises(ValueError, json.loads, s + '0')