diff options
Diffstat (limited to 'meta/lib/oe/cve_check.py')
-rw-r--r-- | meta/lib/oe/cve_check.py | 82 |
1 files changed, 82 insertions, 0 deletions
diff --git a/meta/lib/oe/cve_check.py b/meta/lib/oe/cve_check.py index b17390de90..a4b831831b 100644 --- a/meta/lib/oe/cve_check.py +++ b/meta/lib/oe/cve_check.py | |||
@@ -89,3 +89,85 @@ def update_symlinks(target_path, link_path): | |||
89 | if os.path.exists(os.path.realpath(link_path)): | 89 | if os.path.exists(os.path.realpath(link_path)): |
90 | os.remove(link_path) | 90 | os.remove(link_path) |
91 | os.symlink(os.path.basename(target_path), link_path) | 91 | os.symlink(os.path.basename(target_path), link_path) |
92 | |||
93 | def get_patched_cves(d): | ||
94 | """ | ||
95 | Get patches that solve CVEs using the "CVE: " tag. | ||
96 | """ | ||
97 | |||
98 | import re | ||
99 | import oe.patch | ||
100 | |||
101 | pn = d.getVar("PN") | ||
102 | cve_match = re.compile("CVE:( CVE\-\d{4}\-\d+)+") | ||
103 | |||
104 | # Matches the last "CVE-YYYY-ID" in the file name, also if written | ||
105 | # in lowercase. Possible to have multiple CVE IDs in a single | ||
106 | # file name, but only the last one will be detected from the file name. | ||
107 | # However, patch files contents addressing multiple CVE IDs are supported | ||
108 | # (cve_match regular expression) | ||
109 | |||
110 | cve_file_name_match = re.compile(".*([Cc][Vv][Ee]\-\d{4}\-\d+)") | ||
111 | |||
112 | patched_cves = set() | ||
113 | bb.debug(2, "Looking for patches that solves CVEs for %s" % pn) | ||
114 | for url in oe.patch.src_patches(d): | ||
115 | patch_file = bb.fetch.decodeurl(url)[2] | ||
116 | |||
117 | if not os.path.isfile(patch_file): | ||
118 | bb.error("File Not found: %s" % patch_file) | ||
119 | raise FileNotFoundError | ||
120 | |||
121 | # Check patch file name for CVE ID | ||
122 | fname_match = cve_file_name_match.search(patch_file) | ||
123 | if fname_match: | ||
124 | cve = fname_match.group(1).upper() | ||
125 | patched_cves.add(cve) | ||
126 | bb.debug(2, "Found CVE %s from patch file name %s" % (cve, patch_file)) | ||
127 | |||
128 | with open(patch_file, "r", encoding="utf-8") as f: | ||
129 | try: | ||
130 | patch_text = f.read() | ||
131 | except UnicodeDecodeError: | ||
132 | bb.debug(1, "Failed to read patch %s using UTF-8 encoding" | ||
133 | " trying with iso8859-1" % patch_file) | ||
134 | f.close() | ||
135 | with open(patch_file, "r", encoding="iso8859-1") as f: | ||
136 | patch_text = f.read() | ||
137 | |||
138 | # Search for one or more "CVE: " lines | ||
139 | text_match = False | ||
140 | for match in cve_match.finditer(patch_text): | ||
141 | # Get only the CVEs without the "CVE: " tag | ||
142 | cves = patch_text[match.start()+5:match.end()] | ||
143 | for cve in cves.split(): | ||
144 | bb.debug(2, "Patch %s solves %s" % (patch_file, cve)) | ||
145 | patched_cves.add(cve) | ||
146 | text_match = True | ||
147 | |||
148 | if not fname_match and not text_match: | ||
149 | bb.debug(2, "Patch %s doesn't solve CVEs" % patch_file) | ||
150 | |||
151 | return patched_cves | ||
152 | |||
153 | |||
154 | def get_cpe_ids(cve_product, version): | ||
155 | """ | ||
156 | Get list of CPE identifiers for the given product and version | ||
157 | """ | ||
158 | |||
159 | version = version.split("+git")[0] | ||
160 | |||
161 | cpe_ids = [] | ||
162 | for product in cve_product.split(): | ||
163 | # CVE_PRODUCT in recipes may include vendor information for CPE identifiers. If not, | ||
164 | # use wildcard for vendor. | ||
165 | if ":" in product: | ||
166 | vendor, product = product.split(":", 1) | ||
167 | else: | ||
168 | vendor = "*" | ||
169 | |||
170 | cpe_id = f'cpe:2.3:a:{vendor}:{product}:{version}:*:*:*:*:*:*:*' | ||
171 | cpe_ids.append(cpe_id) | ||
172 | |||
173 | return cpe_ids | ||