1import json 2import os 3from oeqa.selftest.case import OESelftestTestCase 4from oeqa.utils.commands import bitbake, get_bb_vars 5 6class CVECheck(OESelftestTestCase): 7 8 def test_version_compare(self): 9 from oe.cve_check import Version 10 11 result = Version("100") > Version("99") 12 self.assertTrue( result, msg="Failed to compare version '100' > '99'") 13 result = Version("2.3.1") > Version("2.2.3") 14 self.assertTrue( result, msg="Failed to compare version '2.3.1' > '2.2.3'") 15 result = Version("2021-01-21") > Version("2020-12-25") 16 self.assertTrue( result, msg="Failed to compare version '2021-01-21' > '2020-12-25'") 17 result = Version("1.2-20200910") < Version("1.2-20200920") 18 self.assertTrue( result, msg="Failed to compare version '1.2-20200910' < '1.2-20200920'") 19 20 result = Version("1.0") >= Version("1.0beta") 21 self.assertTrue( result, msg="Failed to compare version '1.0' >= '1.0beta'") 22 result = Version("1.0-rc2") > Version("1.0-rc1") 23 self.assertTrue( result, msg="Failed to compare version '1.0-rc2' > '1.0-rc1'") 24 result = Version("1.0.alpha1") < Version("1.0") 25 self.assertTrue( result, msg="Failed to compare version '1.0.alpha1' < '1.0'") 26 result = Version("1.0_dev") <= Version("1.0") 27 self.assertTrue( result, msg="Failed to compare version '1.0_dev' <= '1.0'") 28 29 # ignore "p1" and "p2", so these should be equal 30 result = Version("1.0p2") == Version("1.0p1") 31 self.assertTrue( result ,msg="Failed to compare version '1.0p2' to '1.0p1'") 32 # ignore the "b" and "r" 33 result = Version("1.0b") == Version("1.0r") 34 self.assertTrue( result ,msg="Failed to compare version '1.0b' to '1.0r'") 35 36 # consider the trailing alphabet as patched level when comparing 37 result = Version("1.0b","alphabetical") < Version("1.0r","alphabetical") 38 self.assertTrue( result ,msg="Failed to compare version with suffix '1.0b' < '1.0r'") 39 result = Version("1.0b","alphabetical") > Version("1.0","alphabetical") 40 self.assertTrue( result ,msg="Failed to compare version with suffix '1.0b' > '1.0'") 41 42 # consider the trailing "p" and "patch" as patched released when comparing 43 result = Version("1.0","patch") < Version("1.0p1","patch") 44 self.assertTrue( result ,msg="Failed to compare version with suffix '1.0' < '1.0p1'") 45 result = Version("1.0p2","patch") > Version("1.0p1","patch") 46 self.assertTrue( result ,msg="Failed to compare version with suffix '1.0p2' > '1.0p1'") 47 result = Version("1.0_patch2","patch") < Version("1.0_patch3","patch") 48 self.assertTrue( result ,msg="Failed to compare version with suffix '1.0_patch2' < '1.0_patch3'") 49 50 51 def test_convert_cve_version(self): 52 from oe.cve_check import convert_cve_version 53 54 # Default format 55 self.assertEqual(convert_cve_version("8.3"), "8.3") 56 self.assertEqual(convert_cve_version(""), "") 57 58 # OpenSSL format version 59 self.assertEqual(convert_cve_version("1.1.1t"), "1.1.1t") 60 61 # OpenSSH format 62 self.assertEqual(convert_cve_version("8.3_p1"), "8.3p1") 63 self.assertEqual(convert_cve_version("8.3_p22"), "8.3p22") 64 65 # Linux kernel format 66 self.assertEqual(convert_cve_version("6.2_rc8"), "6.2-rc8") 67 self.assertEqual(convert_cve_version("6.2_rc31"), "6.2-rc31") 68 69 70 def test_recipe_report_json(self): 71 config = """ 72INHERIT += "cve-check" 73CVE_CHECK_FORMAT_JSON = "1" 74""" 75 self.write_config(config) 76 77 vars = get_bb_vars(["CVE_CHECK_SUMMARY_DIR", "CVE_CHECK_SUMMARY_FILE_NAME_JSON"]) 78 summary_json = os.path.join(vars["CVE_CHECK_SUMMARY_DIR"], vars["CVE_CHECK_SUMMARY_FILE_NAME_JSON"]) 79 recipe_json = os.path.join(vars["CVE_CHECK_SUMMARY_DIR"], "m4-native_cve.json") 80 81 try: 82 os.remove(summary_json) 83 os.remove(recipe_json) 84 except FileNotFoundError: 85 pass 86 87 bitbake("m4-native -c cve_check") 88 89 def check_m4_json(filename): 90 with open(filename) as f: 91 report = json.load(f) 92 self.assertEqual(report["version"], "1") 93 self.assertEqual(len(report["package"]), 1) 94 package = report["package"][0] 95 self.assertEqual(package["name"], "m4-native") 96 found_cves = { issue["id"]: issue["status"] for issue in package["issue"]} 97 self.assertIn("CVE-2008-1687", found_cves) 98 self.assertEqual(found_cves["CVE-2008-1687"], "Patched") 99 100 self.assertExists(summary_json) 101 check_m4_json(summary_json) 102 self.assertExists(recipe_json) 103 check_m4_json(recipe_json) 104 105 106 def test_image_json(self): 107 config = """ 108INHERIT += "cve-check" 109CVE_CHECK_FORMAT_JSON = "1" 110""" 111 self.write_config(config) 112 113 vars = get_bb_vars(["CVE_CHECK_DIR", "CVE_CHECK_SUMMARY_DIR", "CVE_CHECK_SUMMARY_FILE_NAME_JSON"]) 114 report_json = os.path.join(vars["CVE_CHECK_SUMMARY_DIR"], vars["CVE_CHECK_SUMMARY_FILE_NAME_JSON"]) 115 print(report_json) 116 try: 117 os.remove(report_json) 118 except FileNotFoundError: 119 pass 120 121 bitbake("core-image-minimal-initramfs") 122 self.assertExists(report_json) 123 124 # Check that the summary report lists at least one package 125 with open(report_json) as f: 126 report = json.load(f) 127 self.assertEqual(report["version"], "1") 128 self.assertGreater(len(report["package"]), 1) 129 130 # Check that a random recipe wrote a recipe report to deploy/cve/ 131 recipename = report["package"][0]["name"] 132 recipe_report = os.path.join(vars["CVE_CHECK_DIR"], recipename + "_cve.json") 133 self.assertExists(recipe_report) 134 with open(recipe_report) as f: 135 report = json.load(f) 136 self.assertEqual(report["version"], "1") 137 self.assertEqual(len(report["package"]), 1) 138 self.assertEqual(report["package"][0]["name"], recipename) 139 140 141 def test_recipe_report_json_unpatched(self): 142 config = """ 143INHERIT += "cve-check" 144CVE_CHECK_FORMAT_JSON = "1" 145CVE_CHECK_REPORT_PATCHED = "0" 146""" 147 self.write_config(config) 148 149 vars = get_bb_vars(["CVE_CHECK_SUMMARY_DIR", "CVE_CHECK_SUMMARY_FILE_NAME_JSON"]) 150 summary_json = os.path.join(vars["CVE_CHECK_SUMMARY_DIR"], vars["CVE_CHECK_SUMMARY_FILE_NAME_JSON"]) 151 recipe_json = os.path.join(vars["CVE_CHECK_SUMMARY_DIR"], "m4-native_cve.json") 152 153 try: 154 os.remove(summary_json) 155 os.remove(recipe_json) 156 except FileNotFoundError: 157 pass 158 159 bitbake("m4-native -c cve_check") 160 161 def check_m4_json(filename): 162 with open(filename) as f: 163 report = json.load(f) 164 self.assertEqual(report["version"], "1") 165 self.assertEqual(len(report["package"]), 1) 166 package = report["package"][0] 167 self.assertEqual(package["name"], "m4-native") 168 #m4 had only Patched CVEs, so the issues array will be empty 169 self.assertEqual(package["issue"], []) 170 171 self.assertExists(summary_json) 172 check_m4_json(summary_json) 173 self.assertExists(recipe_json) 174 check_m4_json(recipe_json) 175 176 177 def test_recipe_report_json_ignored(self): 178 config = """ 179INHERIT += "cve-check" 180CVE_CHECK_FORMAT_JSON = "1" 181CVE_CHECK_REPORT_PATCHED = "1" 182""" 183 self.write_config(config) 184 185 vars = get_bb_vars(["CVE_CHECK_SUMMARY_DIR", "CVE_CHECK_SUMMARY_FILE_NAME_JSON"]) 186 summary_json = os.path.join(vars["CVE_CHECK_SUMMARY_DIR"], vars["CVE_CHECK_SUMMARY_FILE_NAME_JSON"]) 187 recipe_json = os.path.join(vars["CVE_CHECK_SUMMARY_DIR"], "logrotate_cve.json") 188 189 try: 190 os.remove(summary_json) 191 os.remove(recipe_json) 192 except FileNotFoundError: 193 pass 194 195 bitbake("logrotate -c cve_check") 196 197 def check_m4_json(filename): 198 with open(filename) as f: 199 report = json.load(f) 200 self.assertEqual(report["version"], "1") 201 self.assertEqual(len(report["package"]), 1) 202 package = report["package"][0] 203 self.assertEqual(package["name"], "logrotate") 204 found_cves = { issue["id"]: issue["status"] for issue in package["issue"]} 205 # m4 CVE should not be in logrotate 206 self.assertNotIn("CVE-2008-1687", found_cves) 207 # logrotate has both Patched and Ignored CVEs 208 self.assertIn("CVE-2011-1098", found_cves) 209 self.assertEqual(found_cves["CVE-2011-1098"], "Patched") 210 self.assertIn("CVE-2011-1548", found_cves) 211 self.assertEqual(found_cves["CVE-2011-1548"], "Ignored") 212 self.assertIn("CVE-2011-1549", found_cves) 213 self.assertEqual(found_cves["CVE-2011-1549"], "Ignored") 214 self.assertIn("CVE-2011-1550", found_cves) 215 self.assertEqual(found_cves["CVE-2011-1550"], "Ignored") 216 217 self.assertExists(summary_json) 218 check_m4_json(summary_json) 219 self.assertExists(recipe_json) 220 check_m4_json(recipe_json) 221