Skip to content

Commit c70c967

Browse files
committed
Fix: version comparison for RPM
In case two RPM versions are compared and have a different separator they are checked lexicographical. In reality they should not be compared and are interchangable. To solve issues with this, we now normalize the version string to only contain a single '.' as a separator.
1 parent d5fd003 commit c70c967

File tree

1 file changed

+60
-7
lines changed

1 file changed

+60
-7
lines changed

rust/src/notus/packages/rpm.rs

Lines changed: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ static RE_VERSION: Lazy<Regex> = lazy_regex!(
6565
)?$"
6666
);
6767

68-
/// Represent a based Redhat package
68+
/// Represent a based Redhat package based on https://rpm.org/docs/6.0.x/man/rpm-version.7
6969
#[derive(Debug, PartialEq, Clone)]
7070
pub struct Rpm {
7171
name: String,
@@ -87,6 +87,23 @@ fn find_any_exception(name: &str) -> String {
8787
"".to_string()
8888
}
8989

90+
fn normalize(version: &str) -> String {
91+
let mut result = String::with_capacity(version.len());
92+
let mut prev_dot = false;
93+
for c in version.chars() {
94+
if c == '.' || c == '+' || c == '_' {
95+
if !prev_dot {
96+
result.push('.');
97+
}
98+
prev_dot = true;
99+
} else {
100+
result.push(c);
101+
prev_dot = false;
102+
}
103+
}
104+
result
105+
}
106+
90107
impl PartialOrd for Rpm {
91108
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
92109
if self.name != other.name {
@@ -154,8 +171,8 @@ impl Package for Rpm {
154171
Some(Rpm {
155172
name: name.to_string(),
156173
epoch,
157-
version: PackageVersion(version.to_string()),
158-
release: PackageVersion(release.to_string()),
174+
version: PackageVersion(normalize(version)),
175+
release: PackageVersion(normalize(release)),
159176
arch: arch.to_string(),
160177
module: (module_name.to_string(), module_stream.to_string()),
161178
})
@@ -195,8 +212,8 @@ impl Package for Rpm {
195212
Some(Rpm {
196213
name: name.to_string(),
197214
epoch,
198-
version: PackageVersion(version.to_string()),
199-
release: PackageVersion(release.to_string()),
215+
version: PackageVersion(normalize(version)),
216+
release: PackageVersion(normalize(release)),
200217
arch: arch.to_string(),
201218
module: (module_name.to_string(), module_stream.to_string()),
202219
})
@@ -346,14 +363,50 @@ mod rpm_tests {
346363
assert_eq!(package.version, PackageVersion("16.20.2".to_string()));
347364
assert_eq!(
348365
package.release,
349-
PackageVersion("3.module+el8.8.0+1543+5f4d09d5".to_string())
366+
PackageVersion("3.module.el8.8.0.1543.5f4d09d5".to_string())
350367
);
351368
assert_eq!(package.module, ("nodejs".to_string(), "16".to_string()));
352369
assert_eq!(package.arch, "x86_64");
353370
assert_eq!(
354371
package.get_version(),
355-
"1:16.20.2-3.module+el8.8.0+1543+5f4d09d5.x86_64"
372+
"1:16.20.2-3.module.el8.8.0.1543.5f4d09d5.x86_64"
373+
);
374+
}
375+
376+
#[test]
377+
fn test_normalization_from_full_name() {
378+
let package1 = Rpm::from_full_name("javapackages-filesystem-5.3.0-2.module+el8+2598+06babf2e.noarch@javapackages-tools:201801").unwrap();
379+
let package2 = Rpm::from_full_name("javapackages-filesystem-5.3.0-2.module+el8.10.0+23274+27840b45.noarch@javapackages-tools:201801").unwrap();
380+
381+
assert_eq!(package1.epoch, 0);
382+
assert_eq!(package1.arch, "noarch");
383+
assert_eq!(package1.name, "javapackages-filesystem");
384+
assert_eq!(package1.version, PackageVersion("5.3.0".to_string()));
385+
assert_eq!(
386+
package1.release,
387+
PackageVersion("2.module.el8.2598.06babf2e".to_string())
356388
);
389+
assert_eq!(
390+
package1.get_version(),
391+
"5.3.0-2.module.el8.2598.06babf2e.noarch"
392+
);
393+
394+
assert_eq!(package2.epoch, 0);
395+
assert_eq!(package2.arch, "noarch");
396+
assert_eq!(package2.name, "javapackages-filesystem");
397+
assert_eq!(package2.version, PackageVersion("5.3.0".to_string()));
398+
assert_eq!(
399+
package2.release,
400+
PackageVersion("2.module.el8.10.0.23274.27840b45".to_string())
401+
);
402+
assert_eq!(
403+
package2.get_version(),
404+
"5.3.0-2.module.el8.10.0.23274.27840b45.noarch"
405+
);
406+
407+
// Test if ^ is greater than normal separator
408+
let package2 = Rpm::from_full_name("javapackages-filesystem-5.3.0-2.module+el8^10.0+23274+27840b45.noarch@javapackages-tools:201801").unwrap();
409+
assert!(package1 < package2);
357410
}
358411

359412
#[test]

0 commit comments

Comments
 (0)