From a297ebbb8a277c7c3fcb44f6da182dde71447442 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Apr 27 2021 17:10:26 +0000 Subject: Add max/min safe integer JSON cannot safely handle integers outside range ``-(2**53) - 1`` to ``(2**53) - 1``. Add constants for safe integers and limit the Int parameter to safe JSON values. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER See: https://pagure.io/freeipa/issue/8802 See: https://pagure.io/freeipa/issue/8361 Signed-off-by: Christian Heimes Reviewed-By: Rob Crittenden Reviewed-By: Alexander Bokovoy Reviewed-By: Fraser Tweedale --- diff --git a/ipalib/parameters.py b/ipalib/parameters.py index 22c82f6..dfdf105 100644 --- a/ipalib/parameters.py +++ b/ipalib/parameters.py @@ -130,6 +130,13 @@ from ipapython.dn import DN from ipapython.dnsutil import DNSName +MAX_UINT32 = (1 << 32) - 1 +# JavaScript Number.MAX_SAFE_INTEGER / Number.MIN_SAFE_INTEGER +# JSON cannot safely encode values outside this range as regular number +MAX_SAFE_INTEGER = (2**53) - 1 +MIN_SAFE_INTEGER = -MAX_SAFE_INTEGER + + def _is_null(value): if value: return False @@ -1093,6 +1100,12 @@ class Int(Number): allowed_types = (int,) type_error = _('must be an integer') + MININT = MININT + MAXINT = MAXINT + MAX_UINT32 = MAX_UINT32 + MAX_SAFE_INTEGER = MAX_SAFE_INTEGER + MIN_SAFE_INTEGER = MIN_SAFE_INTEGER + kwargs = Param.kwargs + ( ('minvalue', int, int(MININT)), ('maxvalue', int, int(MAXINT)), @@ -1124,6 +1137,16 @@ class Int(Number): '%s: minvalue > maxvalue (minvalue=%r, maxvalue=%r)' % ( self.nice, self.minvalue, self.maxvalue) ) + if self.minvalue < self.MIN_SAFE_INTEGER: + raise ValueError( + f"minvalue {self.minvalue} outside range of safe JSON " + f"integer limit {self.MIN_SAFE_INTEGER}" + ) + if self.maxvalue > self.MAX_SAFE_INTEGER: + raise ValueError( + f"maxvalue {self.maxvalue} outside range of safe JSON " + f"integer limit {self.MAX_SAFE_INTEGER}" + ) def _convert_scalar(self, value, index=None): """ diff --git a/ipatests/test_ipalib/test_parameters.py b/ipatests/test_ipalib/test_parameters.py index d71faf4..86326c4 100644 --- a/ipatests/test_ipalib/test_parameters.py +++ b/ipatests/test_ipalib/test_parameters.py @@ -1318,6 +1318,23 @@ class test_Int(ClassChecker): param = self.cls('my_number') check_int_scalar_conversions(param) + def test_safe_json(self): + param = self.cls( + "large", + minvalue=self.cls.MIN_SAFE_INTEGER, + maxvalue=self.cls.MAX_SAFE_INTEGER + ) + for value in (-((2 ** 53) - 1), 0, (2 ** 53) - 1): + param.validate(value) + for value in (-2 ** 53, 2 ** 53): + with pytest.raises(errors.ValidationError): + param.validate(value) + + with pytest.raises(ValueError): + self.cls("toolarge", maxvalue=2**53) + with pytest.raises(ValueError): + self.cls("toosmall", minvalue=-2**53) + class test_Decimal(ClassChecker): """