|
1 | 1 | # |
2 | | -# Copyright (c) 2017 nexB Inc. and others. All rights reserved. |
| 2 | +# Copyright (c) nexB Inc. and others. All rights reserved. |
3 | 3 | # http://nexb.com and https://github.com/nexB/vulnerablecode/ |
4 | 4 | # The VulnerableCode software is licensed under the Apache License version 2.0. |
5 | 5 | # Data generated with VulnerableCode require an acknowledgment. |
|
18 | 18 | # OR CONDITIONS OF ANY KIND, either express or implied. No content created from |
19 | 19 | # VulnerableCode should be considered or used as legal advice. Consult an Attorney |
20 | 20 | # for any legal advice. |
21 | | -# VulnerableCode is a free software code scanning tool from nexB Inc. and others. |
| 21 | +# VulnerableCode is a free software tool from nexB Inc. and others. |
22 | 22 | # Visit https://github.com/nexB/vulnerablecode/ for support and download. |
23 | 23 |
|
24 | 24 | import dataclasses |
|
51 | 51 | class VulnerabilityReferenceInserter: |
52 | 52 | vulnerability: models.Vulnerability |
53 | 53 | reference_id: Optional[str] = '' |
54 | | - url: Optional[str] = '' |
| 54 | + url: Optional[str] = '' |
55 | 55 |
|
56 | 56 | def __post_init__(self): |
57 | 57 | if not any([self.reference_id, self.url]): |
@@ -165,45 +165,68 @@ def _process_added_advisories(data_source: DataSource) -> None: |
165 | 165 |
|
166 | 166 | def _create_vulnerability_and_references(advisory: Advisory): |
167 | 167 | vuln, vuln_created = _get_or_create_vulnerability(advisory) |
168 | | - vuln_references = set() |
| 168 | + new_vuln_references = set() |
169 | 169 |
|
170 | 170 | if vuln_created: |
171 | 171 | # This means vulnerability didn't previously exist in the DB, so add |
172 | 172 | # the references to bulk create queue without any hesitation |
173 | | - for id_ in advisory.reference_ids: |
174 | | - vuln_references.add(VulnerabilityReferenceInserter(vulnerability=vuln, reference_id=id_)) # nopep8 |
175 | | - |
176 | | - for url in advisory.reference_urls: |
177 | | - vuln_references.add(VulnerabilityReferenceInserter(vulnerability=vuln, url=url)) |
| 173 | + for ref in advisory.vuln_references: |
| 174 | + new_vuln_references.add( |
| 175 | + VulnerabilityReferenceInserter( |
| 176 | + vulnerability=vuln, |
| 177 | + **dataclasses.asdict(ref))) |
178 | 178 |
|
179 | 179 | else: |
180 | | - vuln_refs_qs = models.VulnerabilityReference.objects.filter(vulnerability=vuln) |
181 | | - vuln_ids = {ref.reference_id for ref in vuln_refs_qs} |
182 | | - vuln_urls = {ref.url for ref in vuln_refs_qs} |
| 180 | + # build indexes of existing references to either update or create new ones |
| 181 | + # and avoid creating duplicates |
| 182 | + |
| 183 | + existing_vuln_references = models.VulnerabilityReference.objects.filter(vulnerability=vuln) |
| 184 | + refs_by_id_url = { |
| 185 | + (ref.id, ref.url): ref for ref in existing_vuln_references if ref.id and ref.url} |
| 186 | + refs_by_id = {ref.id: ref for ref in existing_vuln_references if ref.id} |
| 187 | + refs_by_url = {ref.url: ref for ref in existing_vuln_references if ref.url} |
| 188 | + |
| 189 | + for adv_ref in advisory.vuln_references: |
| 190 | + if adv_ref.reference_id and adv_ref.url: |
| 191 | + existing_ref = refs_by_id_url.get((adv_ref.reference_id, adv_ref.url)) |
| 192 | + |
| 193 | + elif adv_ref.reference_id: |
| 194 | + existing_ref = refs_by_id.get(adv_ref.reference_id) |
| 195 | + |
| 196 | + elif adv_ref.url: |
| 197 | + existing_ref = refs_by_url.get(adv_ref.url) |
183 | 198 |
|
184 | | - for id_ in advisory.reference_ids: |
185 | | - # Add the item preventing duplicates pass to through. |
186 | | - if id_ not in vuln_ids: |
187 | | - vuln_ids.add(id_) |
188 | | - vuln_references.add(VulnerabilityReferenceInserter( |
189 | | - vulnerability=vuln, reference_id=id_)) |
| 199 | + if existing_ref: |
| 200 | + # update ref as needed |
| 201 | + if not adv_ref.reference_id or not adv_ref.url: |
| 202 | + continue |
190 | 203 |
|
191 | | - for url in advisory.reference_urls: |
192 | | - # Add the item preventing duplicates pass to through. |
193 | | - if url not in vuln_urls: |
194 | | - vuln_urls.add(url) |
195 | | - vuln_references.add(VulnerabilityReferenceInserter( |
196 | | - vulnerability=vuln, url=url)) |
| 204 | + if adv_ref.reference_id and not existing_ref.reference_id: |
| 205 | + existing_ref.reference_id = adv_ref.reference_id |
197 | 206 |
|
198 | | - return vuln, vuln_created, vuln_references |
| 207 | + if adv_ref.url and not existing_ref.url: |
| 208 | + existing_ref.url = adv_ref.url |
| 209 | + |
| 210 | + existing_ref.save() |
| 211 | + |
| 212 | + else: |
| 213 | + new_vuln_references.add( |
| 214 | + VulnerabilityReferenceInserter( |
| 215 | + vulnerability=vuln, |
| 216 | + reference_id=adv_ref.reference_id, |
| 217 | + url=adv_ref.url, |
| 218 | + ) |
| 219 | + ) |
| 220 | + |
| 221 | + return vuln, vuln_created, new_vuln_references |
199 | 222 |
|
200 | 223 |
|
201 | 224 | def _create_pkg_vuln_refs(vuln: models.Vulnerability, vuln_created: bool, purls: Sequence[PackageURL], is_vulnerable: bool): # nopep8 |
202 | 225 | new_refs = set() |
203 | 226 | for purl in purls: |
204 | 227 | pkg, pkg_created = _get_or_create_package(purl) |
205 | 228 | vuln_pkg_ref = PackageRelatedVulnerabilityInserter( |
206 | | - package=pkg, vulnerability=vuln, is_vulnerable=is_vulnerable) |
| 229 | + package=pkg, vulnerability=vuln, is_vulnerable=is_vulnerable) |
207 | 230 |
|
208 | 231 | if pkg_created or vuln_created: |
209 | 232 | new_refs.add(vuln_pkg_ref) |
@@ -371,13 +394,12 @@ def _insert_vulnerabilities_and_references(batch: Set[Advisory]) -> Set[models.V |
371 | 394 |
|
372 | 395 | vulnerabilities.add(vuln) |
373 | 396 |
|
374 | | - for id_ in advisory.reference_ids: |
375 | | - models.VulnerabilityReference.objects.get_or_create( |
376 | | - vulnerability=vuln, reference_id=id_) |
| 397 | + # TODO: Refactor this method to be atleast as efficient as |
| 398 | + # one used by _process_updated_advisories |
377 | 399 |
|
378 | | - for url in advisory.reference_urls: |
| 400 | + for ref in advisory.vuln_references: |
379 | 401 | models.VulnerabilityReference.objects.get_or_create( |
380 | | - vulnerability=vuln, url=url) |
| 402 | + vulnerability=vuln, reference_id=ref.reference_id, url=ref.url) |
381 | 403 |
|
382 | 404 | return vulnerabilities |
383 | 405 |
|
|
0 commit comments