From 26337ac124f4dd1885a44ed65822f2944ad69f3d Mon Sep 17 00:00:00 2001 From: Johannes Feichtner <343448+Churro@users.noreply.github.com> Date: Sat, 15 Jun 2024 08:27:04 +0200 Subject: [PATCH] fix(vulnerabilities): do not force exact patch version in OSV alerts (#29666) --- .../repository/process/lookup/index.ts | 2 +- .../process/vulnerabilities.spec.ts | 151 ++++++++++++++++-- .../repository/process/vulnerabilities.ts | 17 +- 3 files changed, 155 insertions(+), 15 deletions(-) diff --git a/lib/workers/repository/process/lookup/index.ts b/lib/workers/repository/process/lookup/index.ts index 7de76649743106..bdbafe9f8d0420 100644 --- a/lib/workers/repository/process/lookup/index.ts +++ b/lib/workers/repository/process/lookup/index.ts @@ -361,7 +361,7 @@ export async function lookupUpdates( unconstrainedValue || versioning.isCompatible(v.version, compareValue), ); - if (config.isVulnerabilityAlert && !config.osvVulnerabilityAlerts) { + if (config.isVulnerabilityAlert) { filteredReleases = filteredReleases.slice(0, 1); } const buckets: Record = {}; diff --git a/lib/workers/repository/process/vulnerabilities.spec.ts b/lib/workers/repository/process/vulnerabilities.spec.ts index 25a66605bbc904..29f81b96700fe7 100644 --- a/lib/workers/repository/process/vulnerabilities.spec.ts +++ b/lib/workers/repository/process/vulnerabilities.spec.ts @@ -104,13 +104,13 @@ describe('workers/repository/process/vulnerabilities', () => { { packageName: 'django', depVersion: '3.2', - fixedVersion: '==3.3.8', + fixedVersion: '>= 3.3.8', datasource: 'pypi', }, { packageName: 'django', depVersion: '3.2', - fixedVersion: '==3.2.16', + fixedVersion: '>= 3.2.16', datasource: 'pypi', }, ]); @@ -501,7 +501,7 @@ describe('workers/repository/process/vulnerabilities', () => { 'Vulnerability GO-2022-0187 affects stdlib 1.7.5', ); expect(logger.logger.debug).toHaveBeenCalledWith( - 'Setting allowed version 1.7.6 to fix vulnerability GO-2022-0187 in stdlib 1.7.5', + 'Setting allowed version >= 1.7.6 to fix vulnerability GO-2022-0187 in stdlib 1.7.5', ); expect(config.packageRules).toHaveLength(1); expect(config.packageRules).toMatchObject([ @@ -509,7 +509,7 @@ describe('workers/repository/process/vulnerabilities', () => { matchDatasources: ['go'], matchPackageNames: ['stdlib'], matchCurrentVersion: '1.7.5', - allowedVersions: '1.7.6', + allowedVersions: '>= 1.7.6', isVulnerabilityAlert: true, }, ]); @@ -581,7 +581,7 @@ describe('workers/repository/process/vulnerabilities', () => { matchDatasources: ['pypi'], matchPackageNames: ['django'], matchCurrentVersion: '3.2', - allowedVersions: '==3.2.16', + allowedVersions: '>= 3.2.16', isVulnerabilityAlert: true, }, ]); @@ -643,14 +643,14 @@ describe('workers/repository/process/vulnerabilities', () => { matchDatasources: ['pypi'], matchPackageNames: ['django'], matchCurrentVersion: '3.2', - allowedVersions: '==3.2.16', + allowedVersions: '>= 3.2.16', isVulnerabilityAlert: true, }, { matchDatasources: ['pypi'], matchPackageNames: ['django'], matchCurrentVersion: '3.2', - allowedVersions: '==3.3.8', + allowedVersions: '>= 3.3.8', isVulnerabilityAlert: true, }, ]); @@ -744,7 +744,7 @@ describe('workers/repository/process/vulnerabilities', () => { matchDatasources: ['crate'], matchPackageNames: ['tiny_http'], matchCurrentVersion: '0.1.2', - allowedVersions: '0.6.3', + allowedVersions: '>= 0.6.3', isVulnerabilityAlert: true, prBodyNotes: [ '\n\n' + @@ -826,14 +826,14 @@ describe('workers/repository/process/vulnerabilities', () => { matchDatasources: ['npm'], matchPackageNames: ['lodash'], matchCurrentVersion: '4.17.10', - allowedVersions: '4.17.11', + allowedVersions: '>= 4.17.11', isVulnerabilityAlert: true, }, { matchDatasources: ['npm'], matchPackageNames: ['lodash'], matchCurrentVersion: '4.17.10', - allowedVersions: '4.17.20', + allowedVersions: '>= 4.17.20', isVulnerabilityAlert: true, }, ]); @@ -882,6 +882,131 @@ describe('workers/repository/process/vulnerabilities', () => { expect(config.packageRules).toHaveLength(0); }); + it('describe fixed version as ecosystem-specific version constraint', async () => { + const packageFiles: Record = { + maven: [ + { + deps: [ + { + depName: 'com.guicedee.services:log4j-core', + currentValue: '1.0.10.1', + datasource: 'maven', + }, + ], + packageFile: 'some-file1', + }, + ], + nuget: [ + { + deps: [ + { + depName: 'SharpZipLib', + currentValue: '1.3.0', + datasource: 'nuget', + }, + ], + packageFile: 'some-file2', + }, + ], + npm: [ + { + deps: [ + { + depName: 'lodash', + currentValue: '4.17.15', + datasource: 'npm', + }, + ], + packageFile: 'some-file3', + }, + ], + }; + getVulnerabilitiesMock.mockResolvedValue([ + { + id: 'GHSA-jfh8-c2jp-5v3q', + modified: '', + affected: [ + { + package: { + name: 'com.guicedee.services:log4j-core', + ecosystem: 'Maven', + purl: 'pkg:maven/com.guicedee.services/log4j-core', + }, + ranges: [ + { + type: 'ECOSYSTEM', + events: [{ introduced: '0' }, { fixed: '1.2.1.2-jre17' }], + }, + ], + }, + ], + }, + { + id: ' GHSA-mm6g-mmq6-53ff', + modified: '', + affected: [ + { + package: { + name: 'SharpZipLib', + ecosystem: 'NuGet', + purl: 'pkg:nuget/SharpZipLib', + }, + ranges: [ + { + type: 'ECOSYSTEM', + events: [{ introduced: '0' }, { fixed: '1.3.3' }], + }, + ], + }, + ], + }, + { + id: 'GHSA-29mw-wpgm-hmr9', + modified: '', + affected: [ + { + package: { + name: 'lodash', + ecosystem: 'npm', + purl: 'pkg:npm/lodash', + }, + ranges: [ + { + type: 'SEMVER', + events: [{ introduced: '0' }, { fixed: '4.17.21' }], + }, + ], + }, + ], + }, + ]); + + await vulnerabilities.appendVulnerabilityPackageRules( + config, + packageFiles, + ); + expect(config.packageRules).toMatchObject([ + { + matchDatasources: ['maven'], + matchPackageNames: ['com.guicedee.services:log4j-core'], + matchCurrentVersion: '1.0.10.1', + allowedVersions: '[1.2.1.2-jre17,)', + }, + { + matchDatasources: ['nuget'], + matchPackageNames: ['SharpZipLib'], + matchCurrentVersion: '1.3.0', + allowedVersions: '1.3.3', + }, + { + matchDatasources: ['npm'], + matchPackageNames: ['lodash'], + matchCurrentVersion: '4.17.15', + allowedVersions: '>= 4.17.21', + }, + ]); + }); + it('describe last_affected version as ecosystem-specific version constraint', async () => { const packageFiles: Record = { maven: [ @@ -1065,7 +1190,7 @@ describe('workers/repository/process/vulnerabilities', () => { matchDatasources: ['pypi'], matchPackageNames: ['django-mfa2'], matchCurrentVersion: '2.5.0', - allowedVersions: '==2.5.1', + allowedVersions: '>= 2.5.1', isVulnerabilityAlert: true, prBodyNotes: [ '\n\n' + @@ -1127,7 +1252,7 @@ describe('workers/repository/process/vulnerabilities', () => { matchDatasources: ['npm'], matchPackageNames: ['lodash'], matchCurrentVersion: '4.17.10', - allowedVersions: '4.17.11', + allowedVersions: '>= 4.17.11', isVulnerabilityAlert: true, prBodyNotes: [ '\n\n' + @@ -1212,7 +1337,7 @@ describe('workers/repository/process/vulnerabilities', () => { matchDatasources: ['crate'], matchPackageNames: ['sys-info'], matchCurrentVersion: '0.6.0', - allowedVersions: '0.8.0', + allowedVersions: '>= 0.8.0', isVulnerabilityAlert: true, prBodyNotes: [ '\n\n' + diff --git a/lib/workers/repository/process/vulnerabilities.ts b/lib/workers/repository/process/vulnerabilities.ts index 4d4497f6035765..2168f9142e03ff 100644 --- a/lib/workers/repository/process/vulnerabilities.ts +++ b/lib/workers/repository/process/vulnerabilities.ts @@ -409,7 +409,7 @@ export class Vulnerabilities { this.isVersionGt(version, depVersion, versioningApi), ); if (fixedVersion) { - return ecosystem === 'PyPI' ? `==${fixedVersion}` : fixedVersion; + return this.getFixedVersionByEcosystem(fixedVersion, ecosystem); } lastAffectedVersions.sort((a, b) => versioningApi.sortVersions(a, b)); @@ -423,6 +423,21 @@ export class Vulnerabilities { return null; } + private getFixedVersionByEcosystem( + fixedVersion: string, + ecosystem: Ecosystem, + ): string { + if (ecosystem === 'Maven') { + return `[${fixedVersion},)`; + } else if (ecosystem === 'NuGet') { + // TODO: add support for nuget version ranges when #26150 is merged + return fixedVersion; + } + + // crates.io, Go, Hex, npm, RubyGems, PyPI + return `>= ${fixedVersion}`; + } + private getLastAffectedByEcosystem( lastAffected: string, ecosystem: Ecosystem,