1616
1717import json
1818import re
19+ from cvss import CVSS2 , CVSS3 , CVSS4
1920from time import sleep
2021
2122from django .db import models
@@ -81,6 +82,9 @@ class CVSS(models.Model):
8182 version = models .DecimalField (max_digits = 2 , decimal_places = 1 )
8283 vector_string = models .CharField (max_length = 255 , blank = True , null = True )
8384
85+ class Meta :
86+ unique_together = ['score' , 'severity' , 'version' , 'vector_string' ]
87+
8488 def __str__ (self ):
8589 return f'{ self .score } ({ self .severity } ) [{ self .vector_string } ]'
8690
@@ -109,8 +113,37 @@ def __str__(self):
109113 def get_absolute_url (self ):
110114 return reverse ('security:cve_detail' , args = [self .cve_id ])
111115
116+ def add_cvss_score (self , vector_string , score = None , severity = None , version = None ):
117+ if not version :
118+ version = vector_string .split ('/' )[0 ].replace ('CVSS:' , '' )
119+ if version .startswith ('2' ):
120+ cvss_score = CVSS2 (vector_string )
121+ elif version .startswith ('3' ):
122+ cvss_score = CVSS3 (vector_string )
123+ elif version .startswith ('4' ):
124+ cvss_score = CVSS4 (vector_string )
125+ if not score :
126+ score = cvss_score .base_score
127+ if not severity :
128+ severity = cvss_score .severities ()[0 ]
129+ existing = self .cvss_scores .filter (version = version , vector_string = vector_string )
130+ if existing :
131+ cvss = existing .first ()
132+ else :
133+ cvss , created = CVSS .objects .get_or_create (
134+ version = version ,
135+ vector_string = vector_string ,
136+ score = score ,
137+ severity = severity ,
138+ )
139+ cvss .score = score
140+ cvss .severity = severity
141+ cvss .save ()
142+ self .cvss_scores .add (cvss )
143+
112144 def fetch_cve_data (self , fetch_nist_data = False , sleep_secs = 6 ):
113145 self .fetch_mitre_cve_data ()
146+ self .fetch_osv_dev_cve_data ()
114147 if fetch_nist_data :
115148 self .fetch_nist_cve_data ()
116149 sleep (sleep_secs ) # rate limited, see https://nvd.nist.gov/developers/start-here
@@ -125,6 +158,29 @@ def fetch_mitre_cve_data(self):
125158 cve_json = json .loads (data )
126159 self .parse_mitre_cve_data (cve_json )
127160
161+ def fetch_osv_dev_cve_data (self ):
162+ osv_dev_cve_url = f'https://api.osv.dev/v1/vulns/{ self .cve_id } '
163+ res = get_url (osv_dev_cve_url )
164+ if res .status_code == 404 :
165+ error_message .send (sender = None , text = f'404 - Skipping { self .cve_id } - { osv_dev_cve_url } ' )
166+ return
167+ data = fetch_content (res , f'Fetching { self .cve_id } OSV data' )
168+ cve_json = json .loads (data )
169+ self .parse_osv_dev_cve_data (cve_json )
170+
171+ def parse_osv_dev_cve_data (self , cve_json ):
172+ from security .utils import get_or_create_reference
173+ references = cve_json .get ('references' )
174+ if references :
175+ for reference in references :
176+ ref_type = reference .get ('type' ).capitalize ()
177+ url = reference .get ('url' )
178+ get_or_create_reference (ref_type , url )
179+ scores = cve_json .get ('severity' )
180+ if scores :
181+ for score in scores :
182+ self .add_cvss_score (vector_string = score .get ('score' ))
183+
128184 def fetch_nist_cve_data (self ):
129185 nist_cve_url = f'https://services.nvd.nist.gov/rest/json/cves/2.0?cveId={ self .cve_id } '
130186 res = get_url (nist_cve_url )
@@ -149,13 +205,12 @@ def parse_nist_cve_data(self, cve_json):
149205 for scores in score_data :
150206 for key , value in scores .items ():
151207 if key .startswith ('cvssData' ):
152- cvss_score , created = CVSS .objects .get_or_create (
208+ self .add_cvss_score (
209+ vector_string = value .get ('vectorString' ),
153210 score = value .get ('baseScore' ),
154211 severity = value .get ('baseSeverity' ),
155- version = value .get ('version' ),
156- vector_string = value .get ('vectorString' ),
212+ version = value .get ('version' )
157213 )
158- self .cvss_scores .add (cvss_score )
159214 references = cve .get ('references' )
160215 for reference in references :
161216 ref_type = 'Link'
@@ -210,11 +265,10 @@ def parse_mitre_cve_data(self, cve_json):
210265 if metric .get ('format' ) == 'CVSS' :
211266 for key , value in metric .items ():
212267 if key .startswith ('cvss' ):
213- cvss_score , created = CVSS .objects .get_or_create (
268+ self .add_cvss_score (
269+ vector_string = value .get ('vectorString' ),
214270 score = value .get ('baseScore' ),
215271 severity = value .get ('baseSeverity' ),
216- version = value .get ('version' ),
217- vector_string = value .get ('vectorString' ),
272+ version = value .get ('version' )
218273 )
219- self .cvss_scores .add (cvss_score )
220274 self .save ()
0 commit comments