@@ -138,6 +138,77 @@ function scoreToSeverity(score: number): FindingSeverity {
138138 return 'None' ; // Should not happen if thresholds cover 0
139139}
140140
141+ const SEVERITY_RANK : Record < FindingSeverity , number > = {
142+ None : 0 ,
143+ Info : 1 ,
144+ Low : 2 ,
145+ Medium : 3 ,
146+ High : 4 ,
147+ Critical : 5 ,
148+ } ;
149+
150+ function higherSeverity ( a : FindingSeverity , b : FindingSeverity ) : FindingSeverity {
151+ return SEVERITY_RANK [ a ] >= SEVERITY_RANK [ b ] ? a : b ;
152+ }
153+
154+ /**
155+ * Maps OSV/GitHub advisory severity labels (e.g. database_specific.severity) to FindingSeverity.
156+ */
157+ function mapOsvSeverityLabel ( label : string ) : FindingSeverity | null {
158+ switch ( label . toUpperCase ( ) ) {
159+ case 'CRITICAL' :
160+ return 'Critical' ;
161+ case 'HIGH' :
162+ return 'High' ;
163+ case 'MODERATE' :
164+ case 'MEDIUM' :
165+ return 'Medium' ;
166+ case 'LOW' :
167+ return 'Low' ;
168+ default :
169+ return null ;
170+ }
171+ }
172+
173+ /**
174+ * Reads ecosystem severity from an OSV vuln when CVSS v3 is absent.
175+ */
176+ function parseOsvEcosystemSeverity ( vuln : OSVulnerability ) : FindingSeverity | null {
177+ const raw = vuln . database_specific ?. severity ;
178+ if ( typeof raw !== 'string' ) {
179+ return null ;
180+ }
181+ return mapOsvSeverityLabel ( raw ) ;
182+ }
183+
184+ /**
185+ * Computes max severity across vulns: CVSS v3 first, then OSV ecosystem severity,
186+ * else Medium when vulns exist but no score is available (batch API often omits CVSS).
187+ */
188+ function computeMaxSeverityFromVulns ( vulns : OSVulnerability [ ] ) : FindingSeverity {
189+ let maxCvss = 0 ;
190+ let maxOsvSeverity : FindingSeverity | null = null ;
191+
192+ for ( const vuln of vulns ) {
193+ maxCvss = Math . max ( maxCvss , getHighestCvssScore ( vuln . severity ) ) ;
194+ const osvSeverity = parseOsvEcosystemSeverity ( vuln ) ;
195+ if ( osvSeverity ) {
196+ maxOsvSeverity = maxOsvSeverity
197+ ? higherSeverity ( maxOsvSeverity , osvSeverity )
198+ : osvSeverity ;
199+ }
200+ }
201+
202+ if ( maxCvss > 0 ) {
203+ return scoreToSeverity ( maxCvss ) ;
204+ }
205+ if ( maxOsvSeverity ) {
206+ return maxOsvSeverity ;
207+ }
208+ // OSV batch responses may omit CVSS; still surface known vulns at Medium minimum.
209+ return 'Medium' ;
210+ }
211+
141212// Define the return type for the detection function
142213type DetectedFilesMap = { [ key in PackageManager ] ?: { manifest ?: string , lock ?: string } } ;
143214
@@ -339,12 +410,7 @@ export async function lookupCves(dependencies: DependencyInfo[]): Promise<Depend
339410 if ( targetFinding ) {
340411 if ( result && result . vulns && result . vulns . length > 0 ) {
341412 targetFinding . vulnerabilities = result . vulns ;
342- // Calculate max severity for this dependency
343- let maxCvss = 0 ;
344- for ( const vuln of result . vulns ) {
345- maxCvss = Math . max ( maxCvss , getHighestCvssScore ( vuln . severity ) ) ;
346- }
347- targetFinding . maxSeverity = scoreToSeverity ( maxCvss ) ;
413+ targetFinding . maxSeverity = computeMaxSeverityFromVulns ( result . vulns ) ;
348414 } else {
349415 targetFinding . maxSeverity = 'None' ; // Explicitly set to None if no vulns found
350416 }
0 commit comments