diff --git a/utils/checkpackagelib/lib_mk.py b/utils/checkpackagelib/lib_mk.py index 5d48e8929c..eb5fbf474c 100644 --- a/utils/checkpackagelib/lib_mk.py +++ b/utils/checkpackagelib/lib_mk.py @@ -6,6 +6,7 @@ import os import re +from pathlib import Path from checkpackagelib.base import _CheckFunction from checkpackagelib.lib import ConsecutiveEmptyLines # noqa: F401 @@ -117,6 +118,47 @@ class Indent(_CheckFunction): text] +class MissingCVEPatch(_CheckFunction): + PATCH_COMMENT = re.compile(r"^#\s*(\S+\.patch)\s*$") + IGNORE_CVES = re.compile(r"^[A-Z0-9_]+_IGNORE_CVES\s*\+?=") + CVE_TAG_IN_PATCH = re.compile(r"^CVE: *CVE-\d+-\d+$") + + def before(self): + self.pending_patches = [] + self.package_dir = Path(self.filename).parent + + def check_patch_files(self): + for patch_lineno, patch_name, patch_text in self.pending_patches: + patch_file = self.package_dir / patch_name + self.pending_patch = None + + if not patch_file.is_file(): + return ["{}:{}: patch file '{}' mentioned for ignored CVEs is missing" + .format(self.filename, patch_lineno, patch_name), + patch_text] + + if not any(map(self.CVE_TAG_IN_PATCH.match, patch_file.open())): + return ["{}: patch file '{}' is missing 'CVE:' tag" + .format(self.filename, patch_name), + patch_text] + + def check_line(self, lineno, text): + m = self.PATCH_COMMENT.match(text.rstrip()) + if m: + self.pending_patches.append((lineno, m.group(1), text)) + return + + # other comments do not break the association between the patch + # comment and the following _IGNORE_CVES assignment + if text.lstrip().startswith("#"): + return + + if self.IGNORE_CVES.search(text): + return self.check_patch_files() + + self.pending_patches = [] + + class OverriddenVariable(_CheckFunction): CONCATENATING = re.compile(r"^([A-Z0-9_]+)\s*(\+|:|)=\s*\$\(\1\)") END_CONDITIONAL = re.compile(r"^\s*({})".format("|".join(end_conditional))) diff --git a/utils/checkpackagelib/test_lib_mk.py b/utils/checkpackagelib/test_lib_mk.py index 3f97c25b2d..21effa9489 100644 --- a/utils/checkpackagelib/test_lib_mk.py +++ b/utils/checkpackagelib/test_lib_mk.py @@ -197,6 +197,63 @@ def test_Indent(testname, filename, string, expected): assert warnings == expected +MissingCVEPatch = [ + ('patches present', + {'0001-some-fix.patch': 'CVE: CVE-2000-1234', '0002-other-fix.patch': 'CVE: CVE-2000-1234'}, + '# 0001-some-fix.patch\n' + '# 0002-other-fix.patch\n' + 'FOO_IGNORE_CVES += CVE-2000-1234\n', + []), + ('patch missing', + {'0002-other-fix.patch': 'CVE: CVE-2000-1234'}, + '# 0001-some-fix.patch\n' + '# 0002-other-fix.patch\n' + 'FOO_IGNORE_CVES += CVE-2000-1234\n', + [['{}:1: patch file \'0001-some-fix.patch\' mentioned for ignored CVEs is missing', + '# 0001-some-fix.patch\n']]), + ('CVE tag missing', + {'0001-some-fix.patch': ''}, + '# 0001-some-fix.patch\n' + 'FOO_IGNORE_CVES += CVE-2000-1234\n', + [['{}: patch file \'0001-some-fix.patch\' is missing \'CVE:\' tag', + '# 0001-some-fix.patch\n']]), + ('patch in version subdir', + {'1.0/0001-some-fix.patch': 'CVE: CVE-2000-1234'}, + '# 1.0/0001-some-fix.patch\n' + 'FOO_IGNORE_CVES += CVE-2000-1234\n', + []), + ('comments between patch and ignore', + {}, + '# 0001-some-fix.patch\n' + '# this entry is not stale\n' + 'FOO_IGNORE_CVES += CVE-2000-1234\n', + [['{}:1: patch file \'0001-some-fix.patch\' mentioned for ignored CVEs is missing', + '# 0001-some-fix.patch\n']]), + ('comment not followed by ignore', + {}, + '# 0001-some-fix.patch\n' + 'FOO_DEPENDENCIES = host-foo\n' + 'FOO_IGNORE_CVES += CVE-2000-1234\n', + []), + ('ignore without patch comment', + {}, + 'FOO_IGNORE_CVES += CVE-2000-1234\n', + []), + ] + + +@pytest.mark.parametrize('testname,patches,string,expected', MissingCVEPatch) +def test_MissingCVEPatch(testname, patches, string, expected, tmp_path): + filename = str(tmp_path / 'foo.mk') + for patch_name, content in patches.items(): + patch_path = tmp_path / patch_name + patch_path.parent.mkdir(parents=True, exist_ok=True) + patch_path.write_text(content) + warnings = util.check_file(m.MissingCVEPatch, filename, string) + expected = [[w[0].format(filename)] + w[1:] for w in expected] + assert warnings == expected + + OverriddenVariable = [ ('simple assignment', 'any.mk',