From 57ca3570e911bd290ce0e44bdebaa4b5b4ceca13 Mon Sep 17 00:00:00 2001 From: Tim Burke Date: Thu, 16 Jan 2020 10:07:33 -0800 Subject: [PATCH] Allow Timestamp comparisons against out-of-range values Prior to the related change, clients may have written down X-Delete-At headers that are outside of the Timestamp range, for example. Change-Id: Ib8ae7ebcbdb32e0aa58446bd1ef949e5e2f63e74 Related-Change: I23666ec8a067d829eaf9bfe54bd086c320b3429e Related-Bug: 1821204 Partial-Bug: 1860149 --- swift/common/utils.py | 28 ++++++++++++++++------------ test/unit/common/test_utils.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/swift/common/utils.py b/swift/common/utils.py index af21036d79..de2fa4f13f 100644 --- a/swift/common/utils.py +++ b/swift/common/utils.py @@ -1231,7 +1231,7 @@ class Timestamp(object): compatible for normalized timestamps which do not include an offset. """ - def __init__(self, timestamp, offset=0, delta=0): + def __init__(self, timestamp, offset=0, delta=0, check_bounds=True): """ Create a new Timestamp. @@ -1275,10 +1275,11 @@ class Timestamp(object): raise ValueError( 'delta must be greater than %d' % (-1 * self.raw)) self.timestamp = float(self.raw * PRECISION) - if self.timestamp < 0: - raise ValueError('timestamp cannot be negative') - if self.timestamp >= 10000000000: - raise ValueError('timestamp too large') + if check_bounds: + if self.timestamp < 0: + raise ValueError('timestamp cannot be negative') + if self.timestamp >= 10000000000: + raise ValueError('timestamp too large') @classmethod def now(cls, offset=0, delta=0): @@ -1352,21 +1353,24 @@ class Timestamp(object): if other is None: return False if not isinstance(other, Timestamp): - other = Timestamp(other) + try: + other = Timestamp(other, check_bounds=False) + except ValueError: + return False return self.internal == other.internal def __ne__(self, other): - if other is None: - return True - if not isinstance(other, Timestamp): - other = Timestamp(other) - return self.internal != other.internal + return not (self == other) def __lt__(self, other): if other is None: return False if not isinstance(other, Timestamp): - other = Timestamp(other) + other = Timestamp(other, check_bounds=False) + if other.timestamp < 0: + return False + if other.timestamp >= 10000000000: + return True return self.internal < other.internal def __hash__(self): diff --git a/test/unit/common/test_utils.py b/test/unit/common/test_utils.py index 9d4657cca4..fa22dfbd9c 100644 --- a/test/unit/common/test_utils.py +++ b/test/unit/common/test_utils.py @@ -858,6 +858,37 @@ class TestTimestamp(unittest.TestCase): self.assertIn(ts_0, d) # sanity self.assertIn(ts_0_also, d) + def test_out_of_range_comparisons(self): + now = utils.Timestamp.now() + + def check_is_later(val): + self.assertTrue(now != val) + self.assertFalse(now == val) + self.assertTrue(now <= val) + self.assertTrue(now < val) + self.assertTrue(val > now) + self.assertTrue(val >= now) + + check_is_later(1e30) + check_is_later(1579753284000) # someone gave us ms instead of s! + check_is_later('1579753284000') + check_is_later(b'1e15') + check_is_later(u'1.e+10_f') + + def check_is_earlier(val): + self.assertTrue(now != val) + self.assertFalse(now == val) + self.assertTrue(now >= val) + self.assertTrue(now > val) + self.assertTrue(val < now) + self.assertTrue(val <= now) + + check_is_earlier(-1) + check_is_earlier(-0.1) + check_is_earlier('-9999999') + check_is_earlier(b'-9999.999') + check_is_earlier(u'-1234_5678') + class TestTimestampEncoding(unittest.TestCase):