From a662ea62956a60d312de7fd9eb05317e78a3b0fe Mon Sep 17 00:00:00 2001 From: Koshiro Onuki Date: Tue, 2 Jun 2026 18:25:05 +0900 Subject: [PATCH] Accept TUF spec versions in the 1.0.x line --- tuf/src/metadata.rs | 10 ++++++++++ tuf/src/pouf/pouf1/shims.rs | 33 +++++++++++++++++++++++++++++---- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/tuf/src/metadata.rs b/tuf/src/metadata.rs index 9f87a96..25a1aec 100644 --- a/tuf/src/metadata.rs +++ b/tuf/src/metadata.rs @@ -3871,6 +3871,16 @@ mod test { assert!(serde_json::from_value::(targets).is_err()); } + #[test] + fn deserialize_json_root_accepts_1_0_x_spec_version() { + let mut root = make_root(); + let _ = root + .as_object_mut() + .unwrap() + .insert("spec_version".into(), json!("1.0.31")); + assert!(serde_json::from_value::(root).is_ok()); + } + // Refuse to deserialize delegations with duplicated roles #[test] fn deserialize_json_delegations_duplicated_roles() { diff --git a/tuf/src/pouf/pouf1/shims.rs b/tuf/src/pouf/pouf1/shims.rs index 95af983..7c80ed2 100644 --- a/tuf/src/pouf/pouf1/shims.rs +++ b/tuf/src/pouf/pouf1/shims.rs @@ -14,12 +14,35 @@ use { const SPEC_VERSION: &str = "1.0"; -// Ensure the given spec version matches our spec version. +fn is_semver_numeric_identifier(identifier: &str) -> bool { + if identifier.is_empty() { + return false; + } + + if !identifier.bytes().all(|b| b.is_ascii_digit()) { + return false; + } + + identifier == "0" || !identifier.starts_with('0') +} + +// Ensure the given spec version stays within the supported TUF 1.0.x line. // // We also need to handle the literal "1.0" here, despite that fact that it is not a valid version // according to the SemVer spec, because it is already baked into some of the old roots. fn valid_spec_version(other: &str) -> bool { - matches!(other, "1.0" | "1.0.0") + if other == SPEC_VERSION { + return true; + } + + let Some(patch) = other + .strip_prefix(SPEC_VERSION) + .and_then(|suffix| suffix.strip_prefix('.')) + else { + return false; + }; + + is_semver_numeric_identifier(patch) } fn parse_datetime(ts: &str) -> Result> { @@ -637,13 +660,15 @@ mod test { #[test] fn spec_version_validation() { - let valid_spec_versions = ["1.0.0", "1.0"]; + let valid_spec_versions = ["1.0.0", "1.0", "1.0.1", "1.0.34", "1.0.999"]; for version in valid_spec_versions { assert!(valid_spec_version(version), "{:?} should be valid", version); } - let invalid_spec_versions = ["1.0.1", "1.1.0", "2.0.0", "3.0"]; + let invalid_spec_versions = [ + "1.1.0", "2.0.0", "3.0", "1", "1.1", "1.0.beta", "1.0.0.1", "1.0.00", "1.0.01", + ]; for version in invalid_spec_versions { assert!(