diff options
Diffstat (limited to 'meta/lib')
-rw-r--r-- | meta/lib/oe/cve_check.py | 83 |
1 files changed, 83 insertions, 0 deletions
diff --git a/meta/lib/oe/cve_check.py b/meta/lib/oe/cve_check.py index a1d7c292af..0302beeb4a 100644 --- a/meta/lib/oe/cve_check.py +++ b/meta/lib/oe/cve_check.py | |||
@@ -63,3 +63,86 @@ def _cmpkey(release, patch_l, pre_l, pre_v): | |||
63 | else: | 63 | else: |
64 | _pre = float(pre_v) if pre_v else float('-inf') | 64 | _pre = float(pre_v) if pre_v else float('-inf') |
65 | return _release, _patch, _pre | 65 | return _release, _patch, _pre |
66 | |||
67 | |||
68 | def get_patched_cves(d): | ||
69 | """ | ||
70 | Get patches that solve CVEs using the "CVE: " tag. | ||
71 | """ | ||
72 | |||
73 | import re | ||
74 | import oe.patch | ||
75 | |||
76 | pn = d.getVar("PN") | ||
77 | cve_match = re.compile("CVE:( CVE\-\d{4}\-\d+)+") | ||
78 | |||
79 | # Matches the last "CVE-YYYY-ID" in the file name, also if written | ||
80 | # in lowercase. Possible to have multiple CVE IDs in a single | ||
81 | # file name, but only the last one will be detected from the file name. | ||
82 | # However, patch files contents addressing multiple CVE IDs are supported | ||
83 | # (cve_match regular expression) | ||
84 | |||
85 | cve_file_name_match = re.compile(".*([Cc][Vv][Ee]\-\d{4}\-\d+)") | ||
86 | |||
87 | patched_cves = set() | ||
88 | bb.debug(2, "Looking for patches that solves CVEs for %s" % pn) | ||
89 | for url in oe.patch.src_patches(d): | ||
90 | patch_file = bb.fetch.decodeurl(url)[2] | ||
91 | |||
92 | if not os.path.isfile(patch_file): | ||
93 | bb.error("File Not found: %s" % patch_file) | ||
94 | raise FileNotFoundError | ||
95 | |||
96 | # Check patch file name for CVE ID | ||
97 | fname_match = cve_file_name_match.search(patch_file) | ||
98 | if fname_match: | ||
99 | cve = fname_match.group(1).upper() | ||
100 | patched_cves.add(cve) | ||
101 | bb.debug(2, "Found CVE %s from patch file name %s" % (cve, patch_file)) | ||
102 | |||
103 | with open(patch_file, "r", encoding="utf-8") as f: | ||
104 | try: | ||
105 | patch_text = f.read() | ||
106 | except UnicodeDecodeError: | ||
107 | bb.debug(1, "Failed to read patch %s using UTF-8 encoding" | ||
108 | " trying with iso8859-1" % patch_file) | ||
109 | f.close() | ||
110 | with open(patch_file, "r", encoding="iso8859-1") as f: | ||
111 | patch_text = f.read() | ||
112 | |||
113 | # Search for one or more "CVE: " lines | ||
114 | text_match = False | ||
115 | for match in cve_match.finditer(patch_text): | ||
116 | # Get only the CVEs without the "CVE: " tag | ||
117 | cves = patch_text[match.start()+5:match.end()] | ||
118 | for cve in cves.split(): | ||
119 | bb.debug(2, "Patch %s solves %s" % (patch_file, cve)) | ||
120 | patched_cves.add(cve) | ||
121 | text_match = True | ||
122 | |||
123 | if not fname_match and not text_match: | ||
124 | bb.debug(2, "Patch %s doesn't solve CVEs" % patch_file) | ||
125 | |||
126 | return patched_cves | ||
127 | |||
128 | |||
129 | def get_cpe_ids(cve_product, version): | ||
130 | """ | ||
131 | Get list of CPE identifiers for the given product and version | ||
132 | """ | ||
133 | |||
134 | version = version.split("+git")[0] | ||
135 | |||
136 | cpe_ids = [] | ||
137 | for product in cve_product.split(): | ||
138 | # CVE_PRODUCT in recipes may include vendor information for CPE identifiers. If not, | ||
139 | # use wildcard for vendor. | ||
140 | if ":" in product: | ||
141 | vendor, product = product.split(":", 1) | ||
142 | else: | ||
143 | vendor = "*" | ||
144 | |||
145 | cpe_id = f'cpe:2.3:a:{vendor}:{product}:{version}:*:*:*:*:*:*:*' | ||
146 | cpe_ids.append(cpe_id) | ||
147 | |||
148 | return cpe_ids | ||