From 1c822181977c3bcd63e5b1f5715314580afceb22 Mon Sep 17 00:00:00 2001 From: Adam Setch Date: Tue, 16 May 2023 11:38:23 -0400 Subject: [PATCH] feat(vulnerabilities): handle medium and unknown severities (#22257) Co-authored-by: Jamie Magee --- lib/util/string.ts | 11 ++++++++ lib/util/template/index.ts | 2 +- lib/util/vulnerability/utils.spec.ts | 28 +++++++++++++++++++ lib/util/vulnerability/utils.ts | 2 ++ lib/workers/repository/process/types.ts | 2 +- .../process/vulnerabilities.spec.ts | 2 +- .../repository/process/vulnerabilities.ts | 22 +++++++-------- 7 files changed, 54 insertions(+), 15 deletions(-) diff --git a/lib/util/string.ts b/lib/util/string.ts index 4030956cdd923c..7a4d7d87608fb4 100644 --- a/lib/util/string.ts +++ b/lib/util/string.ts @@ -56,3 +56,14 @@ export function looseEquals( export function isDockerDigest(input: string): boolean { return /^sha256:[a-f0-9]{64}$/i.test(input); } + +export function titleCase(input: string): string { + const words = input.toLowerCase().split(' '); + + for (let i = 0; i < words.length; i++) { + const word = words[i]; + words[i] = word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(); + } + + return words.join(' '); +} diff --git a/lib/util/template/index.ts b/lib/util/template/index.ts index da16eaa90f8cfd..5c3e1261e29a73 100644 --- a/lib/util/template/index.ts +++ b/lib/util/template/index.ts @@ -145,7 +145,7 @@ export const allowedFields = { versioning: 'The versioning scheme in use', versions: 'An array of ChangeLogRelease objects in the upgrade', vulnerabilitySeverity: - 'The severity for a vulnerability alert upgrade (eg: LOW, MODERATE, HIGH, CRITICAL)', + 'The severity for a vulnerability alert upgrade (LOW, MEDIUM, MODERATE, HIGH, CRITICAL, UNKNOWN)', }; const prBodyFields = [ diff --git a/lib/util/vulnerability/utils.spec.ts b/lib/util/vulnerability/utils.spec.ts index 0d9aa2ee34bae1..4ba086a5394d85 100644 --- a/lib/util/vulnerability/utils.spec.ts +++ b/lib/util/vulnerability/utils.spec.ts @@ -85,6 +85,20 @@ describe('util/vulnerability/utils', () => { expect(severity).toBe('MODERATE'); }); + it('child MEDIUM vulnerability severity rating is maintained', () => { + const parentConfig = { + vulnerabilitySeverity: 'LOW', + }; + + const childConfig = { + vulnerabilitySeverity: 'MEDIUM', + }; + + const severity = getHighestVulnerabilitySeverity(parentConfig, childConfig); + + expect(severity).toBe('MEDIUM'); + }); + it('parent LOW vulnerability severity rating is maintained', () => { const parentConfig = { vulnerabilitySeverity: 'LOW', @@ -113,6 +127,20 @@ describe('util/vulnerability/utils', () => { expect(severity).toBe('LOW'); }); + it('child UNKNOWN vulnerability severity rating is maintained', () => { + const parentConfig = { + vulnerabilitySeverity: 'CRITICAL', + }; + + const childConfig = { + vulnerabilitySeverity: 'UNKNOWN', + }; + + const severity = getHighestVulnerabilitySeverity(parentConfig, childConfig); + + expect(severity).toBe('UNKNOWN'); + }); + it('handled undefined parent and child vulnerability severity', () => { const parentConfig = { vulnerabilitySeverity: undefined, diff --git a/lib/util/vulnerability/utils.ts b/lib/util/vulnerability/utils.ts index 5607a6de9237b8..bc451a7db53658 100644 --- a/lib/util/vulnerability/utils.ts +++ b/lib/util/vulnerability/utils.ts @@ -1,8 +1,10 @@ const severityOrder: Record = { LOW: 1, + MEDIUM: 2, MODERATE: 2, HIGH: 3, CRITICAL: 4, + UNKNOWN: 5, }; export function getHighestVulnerabilitySeverity< diff --git a/lib/workers/repository/process/types.ts b/lib/workers/repository/process/types.ts index e59c569da1d8ab..c0de869e16ab17 100644 --- a/lib/workers/repository/process/types.ts +++ b/lib/workers/repository/process/types.ts @@ -21,5 +21,5 @@ export interface DependencyVulnerabilities { export interface SeverityDetails { cvssVector: string; score: string; - severityLevel?: string; + severityLevel: string; } diff --git a/lib/workers/repository/process/vulnerabilities.spec.ts b/lib/workers/repository/process/vulnerabilities.spec.ts index acfb0424435dba..eb63590dea7a21 100644 --- a/lib/workers/repository/process/vulnerabilities.spec.ts +++ b/lib/workers/repository/process/vulnerabilities.spec.ts @@ -944,7 +944,7 @@ describe('workers/repository/process/vulnerabilities', () => { No details. #### Severity - Unknown severity. + Unknown #### References No references. diff --git a/lib/workers/repository/process/vulnerabilities.ts b/lib/workers/repository/process/vulnerabilities.ts index 93483f716a0009..9ff98a803dfb6a 100644 --- a/lib/workers/repository/process/vulnerabilities.ts +++ b/lib/workers/repository/process/vulnerabilities.ts @@ -18,6 +18,7 @@ import { import { sanitizeMarkdown } from '../../../util/markdown'; import * as p from '../../../util/promises'; import { regEx } from '../../../util/regex'; +import { titleCase } from '../../../util/string'; import type { DependencyVulnerabilities, SeverityDetails, @@ -470,7 +471,7 @@ export class Vulnerabilities { matchCurrentVersion: depVersion, allowedVersions: fixedVersion, isVulnerabilityAlert: true, - vulnerabilitySeverity: severityDetails.severityLevel?.toUpperCase(), + vulnerabilitySeverity: severityDetails.severityLevel, prBodyNotes: this.generatePrBodyNotes(vulnerability, affected), force: { ...packageFileConfig.vulnerabilityAlerts, @@ -481,9 +482,7 @@ export class Vulnerabilities { private evaluateCvssVector(vector: string): [string, string] { try { const parsedCvss: CvssScore = parseCvssVector(vector); - const severityLevel = - parsedCvss.cvss3OverallSeverityText.charAt(0).toUpperCase() + - parsedCvss.cvss3OverallSeverityText.slice(1); + const severityLevel = parsedCvss.cvss3OverallSeverityText; return [parsedCvss.baseScore.toFixed(1), severityLevel]; } catch (err) { @@ -532,10 +531,8 @@ export class Vulnerabilities { if (severityDetails.cvssVector) { content += `- CVSS Score: ${severityDetails.score}\n`; content += `- Vector String: \`${severityDetails.cvssVector}\`\n`; - } else if (severityDetails.severityLevel) { - content += `${severityDetails.severityLevel}\n`; } else { - content += 'Unknown severity.\n'; + content += `${titleCase(severityDetails.severityLevel)}\n`; } content += `\n#### References\n${ @@ -566,7 +563,7 @@ export class Vulnerabilities { vulnerability: Osv.Vulnerability, affected: Osv.Affected ): SeverityDetails { - let severityLevel: string | undefined; + let severityLevel = 'UNKNOWN'; let score = 'Unknown'; const cvssVector = @@ -576,15 +573,16 @@ export class Vulnerabilities { if (cvssVector) { const [baseScore, severity] = this.evaluateCvssVector(cvssVector); - severityLevel = severity; - score = baseScore ? `${baseScore} / 10 (${severityLevel})` : 'Unknown'; + severityLevel = severity.toUpperCase(); + score = baseScore + ? `${baseScore} / 10 (${titleCase(severityLevel)})` + : 'Unknown'; } else if ( vulnerability.id.startsWith('GHSA-') && vulnerability.database_specific?.severity ) { const severity = vulnerability.database_specific.severity as string; - severityLevel = - severity.charAt(0).toUpperCase() + severity.slice(1).toLowerCase(); + severityLevel = severity.toUpperCase(); } return {