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