xref: /OK3568_Linux_fs/yocto/poky/meta/lib/oeqa/selftest/cases/cve_check.py (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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