From 54274e9079b76b4897b6bbbe3378df3b1d387c50 Mon Sep 17 00:00:00 2001 From: Valentin Haudiquet Date: Thu, 17 Sep 2026 19:52:32 +0200 Subject: [PATCH] version: reject an empty debian revision DebianVersion::parse accepted '1.0-' (empty revision after rsplit on the last hyphen), where dpkg rejects it with 'revision number is empty'; downstream filename construction produced garbage like 'foo_1.0-.dsc'. Keep the start-digit warning-only semantics of dpkg (no new check there) and the accepted '1.0--1' split. --- src/debian/version.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/debian/version.rs b/src/debian/version.rs index 578bf1a..47f7e95 100644 --- a/src/debian/version.rs +++ b/src/debian/version.rs @@ -48,6 +48,12 @@ impl DebianVersion { } } if let Some(rev) = &debian_revision { + if rev.is_empty() { + // dpkg rejects a trailing hyphen: "bad syntax: revision + // number is empty". Native versions (no `-` at all) are + // handled above and stay valid. + return Err(format!("empty debian revision in '{}'", raw)); + } for c in rev.chars() { if !(c.is_ascii_alphanumeric() || matches!(c, '.' | '+' | '~') || !c.is_ascii()) { return Err(format!( @@ -329,6 +335,27 @@ mod tests { assert!(DebianVersion::parse("1.0~rc1-2").is_ok()); } + /// dpkg rejects a trailing `-` ("bad syntax: revision number is + /// empty") but accepts `1.0--1`, where the revision is the text after + /// the *last* hyphen (upstream `1.0-` + revision `1`). + #[test] + fn version_empty_revision() { + let err = DebianVersion::parse("1.0-").unwrap_err(); + assert!(err.contains("empty"), "unexpected message: {err}"); + + assert!(DebianVersion::parse("1.0-").is_err()); + // Epoch variants take the same path. + assert!(DebianVersion::parse("3:1.0-").is_err()); + assert!(DebianVersion::parse("1.0-1").is_ok()); + // Native versions (no revision at all) are still fine. + assert!(DebianVersion::parse("1.0").is_ok()); + assert!(DebianVersion::parse("3:1.0").is_ok()); + + let v = DebianVersion::parse("1.0--1").unwrap(); + assert_eq!(v.upstream, "1.0-"); + assert_eq!(v.debian_revision.as_deref(), Some("1")); + } + fn cmp_sign(a: &DebianVersion, b: &DebianVersion) -> i32 { match a.cmp(b) { std::cmp::Ordering::Less => -1,