Skip to content

Commit 1535645

Browse files
committed
🏗️ Add option to whether create custom vulcodes or not
Signed-off-by: Shivam Sandbhor <shivam.sandbhor@gmail.com>
1 parent d17123f commit 1535645

3 files changed

Lines changed: 183 additions & 156 deletions

File tree

vulnerabilities/import_runner.py

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ def __init__(self, importer: models.Importer, batch_size: int):
9898
self.importer = importer
9999
self.batch_size = batch_size
100100

101-
def run(self, cutoff_date: datetime.datetime = None) -> None:
101+
def run(self, create_vulcodes=True, cutoff_date: datetime.datetime = None) -> None:
102102
"""
103103
Create a data source for the given importer and store the data retrieved in the database.
104104
@@ -112,7 +112,7 @@ def run(self, cutoff_date: datetime.datetime = None) -> None:
112112
logger.info(f"Starting import for {self.importer.name}.")
113113
data_source = self.importer.make_data_source(self.batch_size, cutoff_date=cutoff_date)
114114
with data_source:
115-
process_advisories(data_source)
115+
process_advisories(data_source, create_vulcodes)
116116
self.importer.last_run = datetime.datetime.now(tz=datetime.timezone.utc)
117117
self.importer.data_source_cfg = dataclasses.asdict(data_source.config)
118118
self.importer.save()
@@ -133,7 +133,7 @@ def get_vuln_pkg_refs(vulnerability, package):
133133
)
134134

135135

136-
def process_advisories(data_source: DataSource) -> None:
136+
def process_advisories(data_source: DataSource, create_vulcodes) -> None:
137137
bulk_create_vuln_refs = set()
138138
bulk_create_vuln_pkg_refs = set()
139139
# Treat updated_advisories and added_advisories as same. Eventually
@@ -143,6 +143,9 @@ def process_advisories(data_source: DataSource) -> None:
143143
for batch in advisory_batches:
144144
for advisory in batch:
145145
try:
146+
if not advisory.identifier and not create_vulcodes:
147+
continue
148+
146149
vuln, vuln_created = _get_or_create_vulnerability(advisory)
147150
for vuln_ref in advisory.vuln_references:
148151
ref = VulnerabilityReferenceInserter(
@@ -151,11 +154,9 @@ def process_advisories(data_source: DataSource) -> None:
151154
reference_id=vuln_ref.reference_id,
152155
)
153156

154-
if vuln_created or not vuln_ref_exists(
155-
vuln, vuln_ref.url, vuln_ref.reference_id
156-
):
157-
# A vulnerability reference can't exist if the
158-
# vulnerability is just created so insert it
157+
if vuln_created or not vuln_ref_exists(vuln, vuln_ref.url, vuln_ref.reference_id):
158+
# A vulnerability reference can't exist if the vulnerability is just created so
159+
# insert it
159160
bulk_create_vuln_refs.add(ref)
160161

161162
for purl in chain(advisory.impacted_package_urls, advisory.resolved_package_urls):
@@ -175,12 +176,20 @@ def process_advisories(data_source: DataSource) -> None:
175176
existing_ref = get_vuln_pkg_refs(vuln, pkg)
176177
if not existing_ref:
177178
bulk_create_vuln_pkg_refs.add(pkg_vuln_ref)
179+
# A vulnerability-package relationship does not exist already if either the
180+
# vulnerability or the package is just created.
178181

179182
else:
180-
# This handles conflicts between existing data and obtained data
181-
if existing_ref[0].is_vulnerable != pkg_vuln_ref.is_vulnerable:
182-
handle_conflicts([existing_ref[0], pkg_vuln_ref.to_model_object()])
183-
existing_ref.delete()
183+
# insert only if it there is no existing vulnerability-package relationship.
184+
existing_ref = get_vuln_pkg_refs(vuln, pkg)
185+
if not existing_ref:
186+
bulk_create_vuln_pkg_refs.add(pkg_vuln_ref)
187+
188+
else:
189+
# This handles conflicts between existing data and obtained data
190+
if existing_ref[0].is_vulnerable != pkg_vuln_ref.is_vulnerable:
191+
handle_conflicts([existing_ref[0], pkg_vuln_ref.to_model_object()])
192+
existing_ref.delete()
184193
except Exception:
185194
# TODO: store error but continue
186195
logger.error(
@@ -249,20 +258,13 @@ def handle_conflicts(conflicts):
249258
def _get_or_create_vulnerability(
250259
advisory: Advisory,
251260
) -> Tuple[models.Vulnerability, bool]:
252-
if advisory.identifier:
253-
query_kwargs = {"identifier": advisory.identifier}
254-
elif advisory.summary:
255-
query_kwargs = {"summary": advisory.summary}
256-
else:
257-
return models.Vulnerability.objects.create(), True
258-
259-
try:
260-
vuln, created = models.Vulnerability.objects.get_or_create(**query_kwargs)
261-
# Eventually we only want to keep summary from NVD and ignore other descriptions.
262-
if advisory.summary and vuln.summary != advisory.summary:
263-
vuln.summary = advisory.summary
264-
vuln.save()
265-
return vuln, created
261+
262+
vuln, created = models.Vulnerability.objects.get_or_create(identifier=advisory.identifier)
263+
264+
# Eventually we only want to keep summary from NVD and ignore other descriptions.
265+
if advisory.summary and vuln.summary != advisory.summary:
266+
vuln.summary = advisory.summary
267+
vuln.save()
266268

267269
except Exception:
268270
logger.error(

vulnerabilities/management/commands/import.py

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -32,55 +32,64 @@
3232

3333

3434
class Command(BaseCommand):
35-
help = 'Import vulnerability data'
35+
help = "Import vulnerability data"
3636

3737
def add_arguments(self, parser):
3838
parser.add_argument(
39-
'--list',
40-
action='store_true',
41-
help='List available data sources',
39+
"--list",
40+
action="store_true",
41+
help="List available data sources",
42+
)
43+
parser.add_argument(
44+
"--all", action="store_true", help="Import data from all available sources"
4245
)
43-
parser.add_argument('--all', action='store_true',
44-
help='Import data from all available sources')
4546

4647
parser.add_argument(
47-
'--cutoff-date',
48+
"--cutoff-date",
4849
type=datetime.fromisoformat,
49-
help='ISO8601 formatted timestamp denoting the maximum age of vulnerability '
50-
'information to import.',
50+
help="ISO8601 formatted timestamp denoting the maximum age of vulnerability "
51+
"information to import.",
5152
)
52-
parser.add_argument('sources', nargs='*',
53-
help='Data sources from which to import')
53+
parser.add_argument("sources", nargs="*", help="Data sources from which to import")
5454

5555
parser.add_argument(
56-
'--batch_size', help='The batch size to be used for bulk inserting data')
56+
"--batch_size", help="The batch size to be used for bulk inserting data"
57+
)
58+
59+
parser.add_argument(
60+
"--cv",
61+
action="store_true",
62+
help="This will import and assign id's to vulnerabilities without any identifiers",
63+
)
5764

5865
def handle(self, *args, **options):
5966
# load_importers() seeds the DB with Importers
6067
load_importers()
61-
if options['list']:
68+
if options["list"]:
6269
self.list_sources()
6370
return
6471

65-
if options['batch_size']:
66-
self.batch_size = options['batch_size']
72+
if options["batch_size"]:
73+
self.batch_size = options["batch_size"]
74+
75+
self.create_vulcodes = options["cv"]
6776

68-
if options['all']:
69-
self._import_data(Importer.objects.all(), options['cutoff_date'])
77+
if options["all"]:
78+
self._import_data(Importer.objects.all(), options["cutoff_date"])
7079
return
7180

72-
sources = options['sources']
81+
sources = options["sources"]
7382
if not sources:
7483
raise CommandError(
75-
'Please provide at least one data source to import from or use "--all".')
84+
'Please provide at least one data source to import from or use "--all".'
85+
)
7686

77-
self.import_data(sources, options['cutoff_date'])
87+
self.import_data(sources, options["cutoff_date"])
7888

7989
def list_sources(self):
8090
importers = Importer.objects.all()
81-
self.stdout.write(
82-
'Vulnerability data can be imported from the following sources:')
83-
self.stdout.write(', '.join([i.name for i in importers]))
91+
self.stdout.write("Vulnerability data can be imported from the following sources:")
92+
self.stdout.write(", ".join([i.name for i in importers]))
8493

8594
def import_data(self, names, cutoff_date):
8695
importers = []
@@ -93,15 +102,18 @@ def import_data(self, names, cutoff_date):
93102
unknown_importers.add(name)
94103

95104
if unknown_importers:
96-
unknown_importers = ', '.join(unknown_importers)
97-
raise CommandError(f'Unknown data sources: {unknown_importers}')
105+
unknown_importers = ", ".join(unknown_importers)
106+
raise CommandError(f"Unknown data sources: {unknown_importers}")
98107

99108
self._import_data(importers, cutoff_date)
100109

101110
def _import_data(self, importers, cutoff_date):
102111
for importer in importers:
103-
self.stdout.write(f'Importing data from {importer.name}')
104-
batch_size = int(getattr(self, 'batch_size', 10))
105-
ImportRunner(importer, batch_size).run(cutoff_date=cutoff_date)
112+
self.stdout.write(f"Importing data from {importer.name}")
113+
batch_size = int(getattr(self, "batch_size", 10))
114+
ImportRunner(importer, batch_size).run(
115+
cutoff_date=cutoff_date, create_vulcodes=self.create_vulcodes
116+
)
106117
self.stdout.write(
107-
self.style.SUCCESS(f'Successfully imported data from {importer.name}'))
118+
self.style.SUCCESS(f"Successfully imported data from {importer.name}")
119+
)

0 commit comments

Comments
 (0)