Skip to content

Commit 2f69986

Browse files
author
Jussi Kukkonen
committed
Remove iso8601 dependency
Our 'expires' strings are constrained by the ISO8601_DATETIME_SCHEMA which matches regex '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z'. This can be parsed with just a datetime.strptime(): iso8601 module is not needed. * Add formats.expiry_string_to_datetime() helper function * Modify the 3 locations that used iso8601 and the api/metadata.py usage of datetime.strptime() * Remove related unnecessary logger setup * Add the missing exception documentation to relevant functions (in many cases the exception is rather unlikely as the schema has been verified many times before this though...) Fixes theupdateframework#1065 Signed-off-by: Jussi Kukkonen <[email protected]>
1 parent f8606d9 commit 2f69986

File tree

9 files changed

+62
-41
lines changed

9 files changed

+62
-41
lines changed

requirements-pinned.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ cryptography==3.1.1 # via securesystemslib
66
enum34==1.1.6 ; python_version < '3' # via cryptography
77
idna==2.10 # via requests
88
ipaddress==1.0.23 ; python_version < '3' # via cryptography
9-
iso8601==0.1.13
109
pycparser==2.20 # via cffi
1110
pynacl==1.4.0 # via securesystemslib
1211
python-dateutil==2.8.1 # via securesystemslib

requirements.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,3 @@
4444
securesystemslib[colors, crypto, pynacl]
4545
requests
4646
six
47-
iso8601

setup.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,6 @@
114114
},
115115
python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4",
116116
install_requires = [
117-
'iso8601>=0.1.12',
118117
'requests>=2.19.1',
119118
'six>=1.11.0',
120119
'securesystemslib>=0.16.0'

tests/test_formats.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,23 @@ def test_build_dict_conforming_to_schema(self):
723723

724724

725725

726+
def test_expiry_string_to_datetime(self):
727+
dt = tuf.formats.expiry_string_to_datetime('1985-10-21T13:20:00Z')
728+
self.assertEqual(dt, datetime.datetime(1985, 10, 21, 13, 20, 0))
729+
dt = tuf.formats.expiry_string_to_datetime('2038-01-19T03:14:08Z')
730+
self.assertEqual(dt, datetime.datetime(2038, 1, 19, 3, 14, 8))
731+
732+
# First 3 fail via securesystemslib schema, last one because of strptime()
733+
invalid_inputs = [
734+
'2038-1-19T03:14:08Z', # leading zeros not optional
735+
'2038-01-19T031408Z', # strict time parsing
736+
'2038-01-19T03:14:08Z-06:00', # timezone not allowed
737+
'2038-13-19T03:14:08Z', # too many months
738+
]
739+
for invalid_input in invalid_inputs:
740+
with self.assertRaises(securesystemslib.exceptions.FormatError):
741+
tuf.formats.expiry_string_to_datetime(invalid_input)
742+
726743

727744

728745
def test_unix_timestamp_to_datetime(self):

tuf/api/metadata.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -294,9 +294,8 @@ def from_dict(cls, signed_dict: JsonDict) -> 'Signed':
294294
# Convert 'expires' TUF metadata string to a datetime object, which is
295295
# what the constructor expects and what we store. The inverse operation
296296
# is implemented in 'to_dict'.
297-
signed_dict['expires'] = datetime.strptime(
298-
signed_dict['expires'],
299-
"%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=None)
297+
signed_dict['expires'] = tuf.formats.expiry_string_to_datetime(
298+
signed_dict['expires'])
300299
# NOTE: We write the converted 'expires' back into 'signed_dict' above
301300
# so that we can pass it to the constructor as '**signed_dict' below,
302301
# along with other fields that belong to Signed subclasses.

tuf/client/updater.py

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,6 @@
145145
import securesystemslib.keys
146146
import securesystemslib.util
147147
import six
148-
import iso8601
149148
import requests.exceptions
150149

151150
# The Timestamp role does not have signed metadata about it; otherwise we
@@ -163,11 +162,6 @@
163162
# See 'log.py' to learn how logging is handled in TUF.
164163
logger = logging.getLogger(__name__)
165164

166-
# Disable 'iso8601' logger messages to prevent 'iso8601' from clogging the
167-
# log file.
168-
iso8601_logger = logging.getLogger('iso8601')
169-
iso8601_logger.disabled = True
170-
171165

172166
class MultiRepoUpdater(object):
173167
"""
@@ -2377,30 +2371,24 @@ def _ensure_not_expired(self, metadata_object, metadata_rolename):
23772371
<Exceptions>
23782372
tuf.exceptions.ExpiredMetadataError:
23792373
If 'metadata_rolename' has expired.
2380-
2374+
securesystemslib.exceptions.FormatError:
2375+
If the expiration cannot be parsed correctly
23812376
<Side Effects>
23822377
None.
23832378
23842379
<Returns>
23852380
None.
23862381
"""
23872382

2388-
# Extract the expiration time.
2389-
expires = metadata_object['expires']
2390-
2391-
# If the current time has surpassed the expiration date, raise an
2392-
# exception. 'expires' is in
2393-
# 'securesystemslib.formats.ISO8601_DATETIME_SCHEMA' format (e.g.,
2394-
# '1985-10-21T01:22:00Z'.) Convert it to a unix timestamp and compare it
2383+
# Extract the expiration time. Convert it to a unix timestamp and compare it
23952384
# against the current time.time() (also in Unix/POSIX time format, although
23962385
# with microseconds attached.)
2397-
current_time = int(time.time())
2398-
2399-
# Generate a user-friendly error message if 'expires' is less than the
2400-
# current time (i.e., a local time.)
2401-
expires_datetime = iso8601.parse_date(expires)
2386+
expires_datetime = tuf.formats.expiry_string_to_datetime(
2387+
metadata_object['expires'])
24022388
expires_timestamp = tuf.formats.datetime_to_unix_timestamp(expires_datetime)
24032389

2390+
current_time = int(time.time())
2391+
24042392
if expires_timestamp < current_time:
24052393
message = 'Metadata '+repr(metadata_rolename)+' expired on ' + \
24062394
expires_datetime.ctime() + ' (UTC).'

tuf/formats.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,37 @@ def build_dict_conforming_to_schema(schema, **kwargs):
612612

613613

614614

615+
616+
def expiry_string_to_datetime(expires):
617+
"""
618+
<Purpose>
619+
Convert an expiry string to a datetime object.
620+
<Arguments>
621+
expires:
622+
The expiry date-time string in the ISO8601 format that is defined
623+
in securesystemslib.ISO8601_DATETIME_SCHEMA. E.g. '2038-01-19T03:14:08Z'
624+
<Exceptions>
625+
securesystemslib.exceptions.FormatError, if 'expires' cannot be
626+
parsed correctly.
627+
<Side Effects>
628+
None.
629+
<Returns>
630+
A datetime object representing the expiry time.
631+
"""
632+
633+
# Raise 'securesystemslib.exceptions.FormatError' if there is a mismatch.
634+
securesystemslib.formats.ISO8601_DATETIME_SCHEMA.check_match(expires)
635+
636+
try:
637+
return datetime.datetime.strptime(expires, "%Y-%m-%dT%H:%M:%SZ")
638+
except ValueError as error:
639+
six.raise_from(securesystemslib.exceptions.FormatError(
640+
'Failed to parse ' + repr(expires) + ' as an expiry time'),
641+
error)
642+
643+
644+
645+
615646
def datetime_to_unix_timestamp(datetime_object):
616647
"""
617648
<Purpose>

tuf/repository_lib.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@
5252
import securesystemslib.hash
5353
import securesystemslib.interface
5454
import securesystemslib.util
55-
import iso8601
5655
import six
5756

5857
import securesystemslib.storage
@@ -61,11 +60,6 @@
6160
# See 'log.py' to learn how logging is handled in TUF.
6261
logger = logging.getLogger(__name__)
6362

64-
# Disable 'iso8601' logger messages to prevent 'iso8601' from clogging the
65-
# log file.
66-
iso8601_logger = logging.getLogger('iso8601')
67-
iso8601_logger.disabled = True
68-
6963
# The extension of TUF metadata.
7064
METADATA_EXTENSION = '.json'
7165

@@ -704,7 +698,8 @@ def _log_warning_if_expires_soon(rolename, expires_iso8601_timestamp,
704698
# unix timestamp, subtract from current time.time() (also in POSIX time)
705699
# and compare against 'seconds_remaining_to_warn'. Log a warning message
706700
# to console if 'rolename' expires soon.
707-
datetime_object = iso8601.parse_date(expires_iso8601_timestamp)
701+
datetime_object = tuf.formats.expiry_string_to_datetime(
702+
expires_iso8601_timestamp)
708703
expires_unix_timestamp = \
709704
tuf.formats.datetime_to_unix_timestamp(datetime_object)
710705
seconds_until_expires = expires_unix_timestamp - int(time.time())

tuf/repository_tool.py

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@
5353
import securesystemslib.keys
5454
import securesystemslib.formats
5555
import securesystemslib.util
56-
import iso8601
5756
import six
5857

5958
import securesystemslib.storage
@@ -1317,15 +1316,12 @@ def expiration(self):
13171316
<Purpose>
13181317
A getter method that returns the role's expiration datetime.
13191318
1320-
>>>
1321-
>>>
1322-
>>>
1323-
13241319
<Arguments>
13251320
None.
13261321
13271322
<Exceptions>
1328-
None.
1323+
securesystemslib.exceptions.FormatError, if the expiration cannot be
1324+
parsed correctly
13291325
13301326
<Side Effects>
13311327
None.
@@ -1337,9 +1333,7 @@ def expiration(self):
13371333
roleinfo = tuf.roledb.get_roleinfo(self.rolename, self._repository_name)
13381334
expires = roleinfo['expires']
13391335

1340-
expires_datetime_object = iso8601.parse_date(expires)
1341-
1342-
return expires_datetime_object
1336+
return tuf.formats.expiry_string_to_datetime(expires)
13431337

13441338

13451339

0 commit comments

Comments
 (0)