From 913196940e4f47ce5ce5ec47d564741a38c2330b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gy=C3=B6rgy=20Kiss?= Date: Fri, 15 Nov 2024 19:05:01 +0100 Subject: [PATCH] Make CPE matching case insensitive According to Common Platform Enumeration: Naming Specification Version 2.3 Section 2.3 and Common Platform Enumeration: Name Matching Specification Version 2.3 Section 7.3 and 6.1.3.2 CPE attributes should be lowercased before comparing them. In practice, all CPEs in the CVE Database are in lowercase, that shouldn't be a problem, but comparing with user input (like CPE coming from an SBOM) should work according to the specification. --- cpematcher/core.py | 1 + cpematcher/version.py | 4 ++-- tests/test_core.py | 14 +++++++++++++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/cpematcher/core.py b/cpematcher/core.py index 96e49d4..8547c7d 100644 --- a/cpematcher/core.py +++ b/cpematcher/core.py @@ -77,6 +77,7 @@ def matches(self, other: Self) -> bool: @staticmethod def _glob_equal(value1: str, value2: str) -> bool: + value1, value2 = value1.lower(), value2.lower() # Depending on the order, fnmatch.fnmatch could return False if wildcard # is the first value. As wildcard should always return True in any case, # we reorder the arguments based on that. diff --git a/cpematcher/version.py b/cpematcher/version.py index b90e03d..bf89e45 100644 --- a/cpematcher/version.py +++ b/cpematcher/version.py @@ -2,8 +2,8 @@ class Version: - def __init__(self, version: str): - self.version = version + def __init__(self, version: str | None): + self.version = version and version.lower() def __bool__(self): return bool(self.version) diff --git a/tests/test_core.py b/tests/test_core.py index 9c8e17e..9e0df6a 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -50,9 +50,21 @@ def test_matches_with_same_branch(self): def test_matches_with_exact_version(self): version_cpe = CPE("cpe:2.3:a:apache:activemq:4.1.1:*:*:*:*:*:*:*") - assert version_cpe.matches(version_cpe) + def test_matches_case_insensitive(self): + cpe_str = "cpe:2.3:a:apache:activemq:4.1.1:*:*:*:*:*:*:*" + cpe = CPE(cpe_str) + upper_cpe = CPE(cpe_str.replace("apche", "APACHE")) + assert cpe.matches(upper_cpe) + + cpe_alnum_version = ( + "cpe:2.3:o:microsoft:windows_server_2008:r2:sp1:*:*:*:*:x64:*" + ) + alnum_cpe = CPE(cpe_alnum_version) + cpe_alnum_upper = CPE(cpe_alnum_version.replace("r2:sp1", "R2:SP1")) + assert alnum_cpe.matches(cpe_alnum_upper) + def test_matches_with_version_start_including(self): branch_cpe = CPE(self.template % "4.1.*", version_start_including="4.1.3")