From fcf465d04bac45e447378ec02b50f019ca2864e8 Mon Sep 17 00:00:00 2001 From: "Yann E. MORIN" Date: Sat, 10 Feb 2024 22:24:59 +0100 Subject: [PATCH] utils/checkpackagelib: add check for CPE variables set to default values Now that we can specify that the default values for the CPE_ID variables are valid, without having to actually set one (or more) to their default, add a check-package check that validates that the CPE_ID variables are indeed not set to their default. It also validates that CPE_ID_VALID is not set when another CPE_ID variable is set to a non-default value. Add an anchor in the manual so that we can easily point to it. Signed-off-by: Yann E. MORIN Cc: Fabrice Fontaine Cc: Ricardo Martincoski Signed-off-by: Thomas Petazzoni --- docs/manual/adding-packages-generic.adoc | 2 +- utils/checkpackagelib/lib_mk.py | 73 ++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/docs/manual/adding-packages-generic.adoc b/docs/manual/adding-packages-generic.adoc index ce8608682f..9d365a10ca 100644 --- a/docs/manual/adding-packages-generic.adoc +++ b/docs/manual/adding-packages-generic.adoc @@ -511,7 +511,7 @@ LIBFOO_IGNORE_CVES += CVE-2020-12345 LIBFOO_IGNORE_CVES += CVE-2020-54321 ---------------------- -* +LIBFOO_CPE_ID_*+ variables is a set of variables that allows the +* [[cpe-id]] +LIBFOO_CPE_ID_*+ variables is a set of variables that allows the package to define its https://nvd.nist.gov/products/cpe[CPE identifier]. The available variables are: + diff --git a/utils/checkpackagelib/lib_mk.py b/utils/checkpackagelib/lib_mk.py index d340882971..ce2ab5157c 100644 --- a/utils/checkpackagelib/lib_mk.py +++ b/utils/checkpackagelib/lib_mk.py @@ -366,3 +366,76 @@ class VariableWithBraces(_CheckFunction): return ["{}:{}: use $() to delimit variables, not ${{}}" .format(self.filename, lineno), text] + + +class CPEVariables(_CheckFunction): + """ + Check that the values for the CPE variables are not the default. + - CPE_ID_* variables must not be set to their default + - CPE_ID_VALID must not be set if a non-default CPE_ID variable is set + """ + def before(self): + pkg, _ = os.path.splitext(os.path.basename(self.filename)) + self.CPE_fields_defaults = { + "VALID": "NO", + "PREFIX": "cpe:2.3:a", + "VENDOR": f"{pkg}_project", + "PRODUCT": pkg, + "VERSION": None, + "UPDATE": "*", + } + self.valid = None + self.non_defaults = 0 + self.CPE_FIELDS_RE = re.compile( + r"^\s*(.+_CPE_ID_({}))\s*=\s*(.+)$" + .format("|".join(self.CPE_fields_defaults)), + ) + self.VERSION_RE = re.compile( + rf"^(HOST_)?{pkg.upper().replace('-', '_')}_VERSION\s*=\s*(.+)$", + ) + self.COMMENT_RE = re.compile(r"^\s*#.*") + + def check_line(self, lineno, text): + text = self.COMMENT_RE.sub('', text.rstrip()) + + # WARNING! The VERSION_RE can _also_ match the same lines as CPE_FIELDS_RE, + # but not the other way around. So we must first check for CPE_FIELDS_RE, + # and if not matched, then and only then check for VERSION_RE. + match = self.CPE_FIELDS_RE.match(text) + if match: + var, field, val = match.groups() + return self._check_field(lineno, text, field, var, val) + + match = self.VERSION_RE.match(text) + if match: + self.CPE_fields_defaults["VERSION"] = match.groups()[1] + + def after(self): + # "VALID" counts in the non-defaults; so when "VALID" is present, + # 1 non-default means only "VALID" is present, so that's OK. + if self.valid and self.non_defaults > 1: + return ["{}:{}: 'YES' is implied when a non-default CPE_ID field is specified: {} ({}#cpe-id)".format( + self.filename, + self.valid["lineno"], + self.valid["text"], + self.url_to_manual, + )] + + def _check_field(self, lineno, text, field, var, val): + if field == "VERSION" and self.CPE_fields_defaults[field] is None: + return ["{}:{}: expecting package version to be set before CPE_ID_VERSION".format( + self.filename, + lineno, + )] + if val == self.CPE_fields_defaults[field]: + return ["{}:{}: '{}' is the default value for {} ({}#cpe-id)".format( + self.filename, + lineno, + val, + var, + self.url_to_manual, + )] + else: + if field == "VALID": + self.valid = {"lineno": lineno, "text": text} + self.non_defaults += 1