Patrick Williams | 92b42cb | 2022-09-03 06:53:57 -0500 | [diff] [blame^] | 1 | # |
| 2 | # Copyright OpenEmbedded Contributors |
| 3 | # |
| 4 | # SPDX-License-Identifier: MIT |
| 5 | # |
| 6 | |
Andrew Geissler | d583833 | 2022-05-27 11:33:10 -0500 | [diff] [blame] | 7 | import json |
| 8 | import os |
Andrew Geissler | d1e8949 | 2021-02-12 15:35:20 -0600 | [diff] [blame] | 9 | from oeqa.selftest.case import OESelftestTestCase |
Andrew Geissler | d583833 | 2022-05-27 11:33:10 -0500 | [diff] [blame] | 10 | from oeqa.utils.commands import bitbake, get_bb_vars |
Andrew Geissler | d1e8949 | 2021-02-12 15:35:20 -0600 | [diff] [blame] | 11 | |
| 12 | class CVECheck(OESelftestTestCase): |
| 13 | |
| 14 | def test_version_compare(self): |
Andrew Geissler | d583833 | 2022-05-27 11:33:10 -0500 | [diff] [blame] | 15 | from oe.cve_check import Version |
| 16 | |
Andrew Geissler | d1e8949 | 2021-02-12 15:35:20 -0600 | [diff] [blame] | 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'") |
Andrew Geissler | 95ac1b8 | 2021-03-31 14:34:31 -0500 | [diff] [blame] | 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'") |
Andrew Geissler | d583833 | 2022-05-27 11:33:10 -0500 | [diff] [blame] | 55 | |
| 56 | |
| 57 | def test_recipe_report_json(self): |
| 58 | config = """ |
| 59 | INHERIT += "cve-check" |
| 60 | CVE_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 = """ |
| 95 | INHERIT += "cve-check" |
| 96 | CVE_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) |
Andrew Geissler | 615f2f1 | 2022-07-15 14:00:58 -0500 | [diff] [blame] | 126 | |
| 127 | |
| 128 | def test_recipe_report_json_unpatched(self): |
| 129 | config = """ |
| 130 | INHERIT += "cve-check" |
| 131 | CVE_CHECK_FORMAT_JSON = "1" |
| 132 | CVE_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 = """ |
| 166 | INHERIT += "cve-check" |
| 167 | CVE_CHECK_FORMAT_JSON = "1" |
| 168 | CVE_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) |