From 4cd9e150d09abe9889cc6a156c3d018dd4a146b5 Mon Sep 17 00:00:00 2001 From: Jussi Kukkonen Date: Wed, 26 Jan 2022 10:55:53 +0200 Subject: [PATCH] Metadata API: Accept X.Y spec_version All TUF implementations used to use "1.0" as the spec version and most of them have never modified that value since. Accept two-digit spec_version for legacy compatibility: it is strictly speaking against the current spec (which requires semver) but there should be no harm in doing this and it allows us to deserialize metadata generated by e.g. go-tuf. Fixes #1751 Signed-off-by: Jussi Kukkonen --- tests/test_metadata_serialization.py | 6 ++++-- tuf/api/metadata.py | 14 ++++++++------ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/tests/test_metadata_serialization.py b/tests/test_metadata_serialization.py index 3714eec00c..f116d33326 100644 --- a/tests/test_metadata_serialization.py +++ b/tests/test_metadata_serialization.py @@ -44,8 +44,8 @@ class TestSerialization(unittest.TestCase): "_type wrong type": '{"_type": "foo", "spec_version": "1.0.0", "version": 1, "expires": "2030-01-01T00:00:00Z", "meta": {}}', "version wrong type": '{"_type": "signed", "spec_version": "1.0.0", "version": "a", "expires": "2030-01-01T00:00:00Z", "meta": {}}', "invalid spec_version str": '{"_type": "signed", "spec_version": "abc", "version": 1, "expires": "2030-01-01T00:00:00Z", "meta": {}}', - "two digit spec_version": '{"_type": "signed", "spec_version": "1.2.a", "version": 1, "expires": "2030-01-01T00:00:00Z", "meta": {}}', - "no digit spec_version": '{"_type": "signed", "spec_version": "a.b.c", "version": 1, "expires": "2030-01-01T00:00:00Z", "meta": {}}', + "non-number spec_version": '{"_type": "signed", "spec_version": "1.2.a", "version": 1, "expires": "2030-01-01T00:00:00Z", "meta": {}}', + "one part spec_version": '{"_type": "signed", "spec_version": "1", "version": 1, "expires": "2030-01-01T00:00:00Z", "meta": {}}', "different major spec_version": '{"_type": "signed", "spec_version": "0.0.0", "version": 1, "expires": "2030-01-01T00:00:00Z", "meta": {}}', "version 0": '{"_type": "signed", "spec_version": "1.0.0", "version": 0, "expires": "2030-01-01T00:00:00Z", "meta": {}}', "version below 0": '{"_type": "signed", "spec_version": "1.0.0", "version": -1, "expires": "2030-01-01T00:00:00Z", "meta": {}}', @@ -254,6 +254,8 @@ def test_invalid_timestamp_serialization(self, test_case_data: str) -> None: valid_timestamps: utils.DataSet = { "all": '{ "_type": "timestamp", "spec_version": "1.0.0", "version": 1, "expires": "2030-01-01T00:00:00Z", \ "meta": {"snapshot.json": {"hashes": {"sha256" : "abc"}, "version": 1}}}', + "two part spec_version": '{ "_type": "timestamp", "spec_version": "1.9000", "version": 1, "expires": "2030-01-01T00:00:00Z", \ + "meta": {"snapshot.json": {"hashes": {"sha256" : "abc"}, "version": 1}}}', "unrecognized field": '{ "_type": "timestamp", "spec_version": "1.0.0", "version": 1, "expires": "2030-01-01T00:00:00Z", \ "meta": {"snapshot.json": {"hashes": {"sha256" : "abc"}, "version": 1}}, "foo": "bar"}', } diff --git a/tuf/api/metadata.py b/tuf/api/metadata.py index cd73985e8c..ce552fad83 100644 --- a/tuf/api/metadata.py +++ b/tuf/api/metadata.py @@ -431,16 +431,18 @@ def __init__( expires: datetime, unrecognized_fields: Optional[Mapping[str, Any]] = None, ): + # Accept semver (X.Y.Z) but also X.Y for legacy compatibility spec_list = spec_version.split(".") if ( - len(spec_list) != 3 + len(spec_list) not in [2, 3] or not all(el.isdigit() for el in spec_list) - or spec_list[0] != SPECIFICATION_VERSION[0] ): - raise ValueError( - f"Unsupported spec_version, got {spec_list}, " - f"supported {'.'.join(SPECIFICATION_VERSION)}" - ) + raise ValueError(f"Failed to parse spec_version {spec_version}") + + # major version must match + if spec_list[0] != SPECIFICATION_VERSION[0]: + raise ValueError(f"Unsupported spec_version {spec_version}") + self.spec_version = spec_version self.expires = expires