From beb91f5c10762bd0e0cfb9494c10e1604b4f8a6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Scha=CC=88fer?= <101886095+PeterSchafer@users.noreply.github.com> Date: Mon, 30 Oct 2023 14:50:02 +0100 Subject: [PATCH 01/13] chore: introduce observerable in ProductService --- src/snyk/common/services/productService.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/snyk/common/services/productService.ts b/src/snyk/common/services/productService.ts index 62b6c69b3..f4e2eef52 100644 --- a/src/snyk/common/services/productService.ts +++ b/src/snyk/common/services/productService.ts @@ -1,4 +1,4 @@ -import { Subscription } from 'rxjs'; +import { Subject, Subscription } from 'rxjs'; import { AnalysisStatusProvider } from '../analysis/statusProvider'; import { IConfiguration } from '../configuration/configuration'; import { IWorkspaceTrust } from '../configuration/trustedFolders'; @@ -30,6 +30,7 @@ export interface IProductService extends AnalysisStatusProvider, Disposable { export abstract class ProductService extends AnalysisStatusProvider implements IProductService { private _result: ProductResult; + readonly newResultAvailable$ = new Subject; // Track running scan count. Assumption: server sends N success/error messages for N scans in progress. private runningScanCount = 0; @@ -168,6 +169,7 @@ export abstract class ProductService extends AnalysisStatusProvider implement this.analysisFinished(); this.runningScanCount = 0; + this.newResultAvailable$.next(); this.refreshTreeView(); } } From 8080007c4033f61885f23340a7db9afb7c1681ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Scha=CC=88fer?= <101886095+PeterSchafer@users.noreply.github.com> Date: Mon, 30 Oct 2023 17:08:28 +0100 Subject: [PATCH 02/13] feat: enable LS based Vulnerability Count Service --- src/snyk/base/modules/baseSnykModule.ts | 2 + src/snyk/extension.ts | 16 +- .../ossVulnerabilityCountServiceLS.ts | 276 ++++++++++++++++++ .../vulnerabilityCountProviderLS.ts | 230 +++++++++++++++ 4 files changed, 516 insertions(+), 8 deletions(-) create mode 100644 src/snyk/snykOss/services/vulnerabilityCount/ossVulnerabilityCountServiceLS.ts create mode 100644 src/snyk/snykOss/services/vulnerabilityCount/vulnerabilityCountProviderLS.ts diff --git a/src/snyk/base/modules/baseSnykModule.ts b/src/snyk/base/modules/baseSnykModule.ts index 4dc05fdbd..01b0367d8 100644 --- a/src/snyk/base/modules/baseSnykModule.ts +++ b/src/snyk/base/modules/baseSnykModule.ts @@ -26,6 +26,7 @@ import SnykEditorsWatcher from '../../snykCode/watchers/editorsWatcher'; import { OssServiceLanguageServer } from '../../snykOss/ossServiceLanguageServer'; import { OssService } from '../../snykOss/services/ossService'; import { OssVulnerabilityCountService } from '../../snykOss/services/vulnerabilityCount/ossVulnerabilityCountService'; +import { OssVulnerabilityCountServiceLS } from '../../snykOss/services/vulnerabilityCount/ossVulnerabilityCountServiceLS'; import { IAuthenticationService } from '../services/authenticationService'; import { ScanModeService } from '../services/scanModeService'; import SnykStatusBarItem, { IStatusBarItem } from '../statusBarItem/statusBarItem'; @@ -51,6 +52,7 @@ export default abstract class BaseSnykModule implements IBaseSnykModule { protected commandController: CommandController; protected scanModeService: ScanModeService; protected ossVulnerabilityCountService: OssVulnerabilityCountService; + protected ossVulnerabilityCountServiceLanguageServer: OssVulnerabilityCountServiceLS; protected advisorScoreDisposable: AdvisorService; protected languageServer: ILanguageServer; diff --git a/src/snyk/extension.ts b/src/snyk/extension.ts index 71e42fe4f..8085d6d9d 100644 --- a/src/snyk/extension.ts +++ b/src/snyk/extension.ts @@ -79,8 +79,8 @@ import { OssServiceLanguageServer } from './snykOss/ossServiceLanguageServer'; import { OssDetailPanelProvider } from './snykOss/providers/ossDetailPanelProvider'; import OssIssueTreeProvider from './snykOss/providers/ossVulnerabilityTreeProvider'; import { OssService } from './snykOss/services/ossService'; -import { OssVulnerabilityCountService } from './snykOss/services/vulnerabilityCount/ossVulnerabilityCountService'; -import { ModuleVulnerabilityCountProvider } from './snykOss/services/vulnerabilityCount/vulnerabilityCountProvider'; +import { OssVulnerabilityCountServiceLS } from './snykOss/services/vulnerabilityCount/ossVulnerabilityCountServiceLS'; +import { ModuleVulnerabilityCountProviderLS } from './snykOss/services/vulnerabilityCount/vulnerabilityCountProviderLS'; import { OssVulnerabilityTreeProvider } from './snykOss/views/ossVulnerabilityTreeProvider'; import { OssSuggestionWebviewProvider } from './snykOss/views/suggestion/ossSuggestionWebviewProvider'; import { DailyScanJob } from './snykOss/watchers/dailyScanJob'; @@ -414,24 +414,24 @@ class SnykExtension extends SnykLib implements IExtension { this.initDependencyDownload(); - this.ossVulnerabilityCountService = new OssVulnerabilityCountService( + this.ossVulnerabilityCountServiceLanguageServer = new OssVulnerabilityCountServiceLS( vsCodeWorkspace, vsCodeWindow, vsCodeLanguages, - new ModuleVulnerabilityCountProvider( - this.ossService, + new ModuleVulnerabilityCountProviderLS( + this.ossServiceLanguageServer, languageClientAdapter, new UriAdapter(), new TextDocumentAdapter(), ), - this.ossService, + this.ossServiceLanguageServer, Logger, new EditorDecorator(vsCodeWindow, vsCodeLanguages, new ThemeColorAdapter()), new CodeActionKindAdapter(), this.analytics, configuration, ); - this.ossVulnerabilityCountService.activate(); + this.ossVulnerabilityCountServiceLanguageServer.activate(); this.advisorScoreDisposable = new AdvisorService( vsCodeWindow, @@ -461,7 +461,7 @@ class SnykExtension extends SnykLib implements IExtension { } public async deactivate(): Promise { - this.ossVulnerabilityCountService.dispose(); + this.ossVulnerabilityCountServiceLanguageServer.dispose(); await this.languageServer.stop(); await this.analytics.flush(); await ErrorReporter.flush(); diff --git a/src/snyk/snykOss/services/vulnerabilityCount/ossVulnerabilityCountServiceLS.ts b/src/snyk/snykOss/services/vulnerabilityCount/ossVulnerabilityCountServiceLS.ts new file mode 100644 index 000000000..c8159c81a --- /dev/null +++ b/src/snyk/snykOss/services/vulnerabilityCount/ossVulnerabilityCountServiceLS.ts @@ -0,0 +1,276 @@ +import { Subscription } from 'rxjs'; +import { IAnalytics } from '../../../common/analytics/itly'; +import { IConfiguration } from '../../../common/configuration/configuration'; +import { ILog } from '../../../common/logger/interfaces'; +import { getSupportedLanguage, isValidModuleName } from '../../../common/parsing'; +import { ModuleParserProvider } from '../../../common/services/moduleParserProvider'; +import { Language } from '../../../common/types'; +import { ICodeActionKindAdapter } from '../../../common/vscode/codeAction'; +import { IVSCodeLanguages } from '../../../common/vscode/languages'; +import { Diagnostic, DiagnosticCollection, Disposable, TextDocument } from '../../../common/vscode/types'; +import { IVSCodeWindow } from '../../../common/vscode/window'; +import { IVSCodeWorkspace } from '../../../common/vscode/workspace'; +import { DIAGNOSTICS_OSS_COLLECTION_NAME } from '../../../snykCode/constants/analysis'; +import { EditorDecorator } from '../../editor/editorDecorator'; +import { VulnerabilityCountHoverProvider } from '../../hoverProvider/vulnerabilityCountHoverProvider'; +import { messages } from '../../messages/vulnerabilityCount'; +import { OssServiceLanguageServer } from '../../ossServiceLanguageServer'; +import { VulnerabilityCountEmitter, VulnerabilityCountEvents } from '../../vulnerabilityCountEmitter'; +import { ImportedModule, ModuleVulnerabilityCount, ModuleVulnerabilityCountSeverity } from './importedModule'; +import { ModuleVulnerabilityCountProviderLS } from './vulnerabilityCountProviderLS'; + +export enum SupportedLanguage { + TypeScript, + JavaScript, + HTML, + PJSON, +} + +export class OssVulnerabilityCountServiceLS implements Disposable { + protected disposables: Disposable[] = []; + protected ossScanFinishedSubscription: Subscription; + + private fileEmitters = new Map(); + private diagnostics: DiagnosticCollection | undefined; + + constructor( + private readonly workspace: IVSCodeWorkspace, + private readonly window: IVSCodeWindow, + private readonly languages: IVSCodeLanguages, + private readonly vulnerabilityCountProvider: ModuleVulnerabilityCountProviderLS, + private readonly ossService: OssServiceLanguageServer, + private readonly logger: ILog, + private readonly editorDecorator: EditorDecorator, + private readonly codeActionKindProvider: ICodeActionKindAdapter, + private readonly analytics: IAnalytics, + private readonly configuration: IConfiguration, + ) {} + + activate(): boolean { + this.disposables.push( + (this.diagnostics = this.languages.createDiagnosticCollection(DIAGNOSTICS_OSS_COLLECTION_NAME)), + this.workspace.onDidChangeTextDocument(ev => { + if (ev.contentChanges.length) { + this.processFile(ev.document); + } + }), + this.window.onDidChangeActiveTextEditor(ev => { + if (ev) { + this.processFile(ev.document); + } + }), + + // register hover provider + new VulnerabilityCountHoverProvider(this.languages, this.analytics).register(this.diagnostics), + ); + + // Subscribe to OSS scan finished updates + this.ossScanFinishedSubscription = this.ossService.newResultAvailable$.subscribe(() => this.processActiveEditor()); + + // [JAVASCRIPT, TYPESCRIPT, PJSON, HTML].forEach(language => { + // const provider = new VulnerabilityCodeActionProvider( + // this.ossService, + // this.vulnerabilityCountProvider, + // this.codeActionKindProvider, + // this.analytics, + // ); + // this.disposables.push( + // this.languages.registerCodeActionsProvider(language, provider, { + // providedCodeActionKinds: provider.codeActionKinds, + // }), + // ); + //}); + + this.processActiveEditor(); + + return true; + } + + processActiveEditor(): void { + const activeEditor = this.window.getActiveTextEditor(); + if (activeEditor) { + this.processFile(activeEditor.document); + } + } + + dispose(): void { + while (this.disposables.length) { + const disposable = this.disposables.pop(); + if (disposable) { + disposable.dispose(); + } + } + + for (const emitter of this.fileEmitters.values()) { + emitter.removeAllListeners(); + } + + this.ossScanFinishedSubscription.unsubscribe(); + } + + processFile(document: TextDocument): boolean { + if (!document) { + return false; + } + + const { fileName, languageId } = document; + const supportedLanguage = getSupportedLanguage(fileName, languageId); + if (supportedLanguage === null || !this.shouldProcessFile(fileName, supportedLanguage)) { + return false; + } + + let emitter = this.fileEmitters.get(fileName); + if (emitter) { + emitter.removeAllListeners(); + this.editorDecorator.resetDecorations(fileName); + } else { + emitter = new VulnerabilityCountEmitter(); + this.fileEmitters.set(fileName, emitter); + } + + emitter.on(VulnerabilityCountEvents.Error, e => { + this.logger.error(`Error counting module vulnerabilities: ${e}`); + this.editorDecorator.resetDecorations(fileName); + }); + + emitter.on(VulnerabilityCountEvents.Start, (modules: ImportedModule[]) => { + this.editorDecorator.setScanStartDecorations(fileName, modules); + }); + emitter.on(VulnerabilityCountEvents.Scanned, (vulnerabilityCount: ModuleVulnerabilityCount) => { + this.editorDecorator.setScannedDecoration(vulnerabilityCount, true); + }); + + emitter.on(VulnerabilityCountEvents.Done, (modules: ModuleVulnerabilityCount[]) => { + this.editorDecorator.setScanDoneDecorations(fileName, modules); + this.updateDiagnostics(document, modules); + }); + + // Start + void this.getImportedModules(fileName, document.getText(), supportedLanguage, emitter); + return true; + } + + private updateDiagnostics(document: TextDocument, modules: ModuleVulnerabilityCount[]): void { + if (!this.diagnostics) { + return; + } + + const diagnostics: Diagnostic[] = []; + for (const module of modules) { + if (!module.hasCount || !module.range) { + continue; + } + + const diagnosticMessage = this.getDiagnosticMessage(module); + if (!diagnosticMessage.length) { + continue; + } + + const range = this.languages.createRange( + module.range.start.line - 1, + module.range.start.column, + module.range.end.line - 1, + module.range.end.column, + ); + + const diagnostic = this.languages.createDiagnostic(range, diagnosticMessage, 1); // Warning severity + diagnostics.push({ + ...diagnostic, + source: DIAGNOSTICS_OSS_COLLECTION_NAME, + code: module.mostSevereVulnerabilityId, + }); + } + + this.diagnostics.set(document.uri, diagnostics); + } + + private shouldProcessFile(fileName: string, language: Language): boolean { + if ([Language.TypeScript, Language.JavaScript, Language.PJSON].includes(language)) { + const ossResult = this.vulnerabilityCountProvider.getResultArray(); + if (!ossResult) { + return false; + } + + for (const fileResult of ossResult) { + if (this.vulnerabilityCountProvider.isFilePartOfOssTest(fileName, fileResult)) { + return true; + } + } + + return false; + } + + return true; + } + + private async getImportedModules( + fileName: string, + content: string, + language: Language, + emitter: VulnerabilityCountEmitter, + ): Promise { + try { + const modules = this.getModules(fileName, content, language).filter(isValidModuleName); + emitter.startScanning(modules); + + const promises = modules + .map(module => this.vulnerabilityCountProvider.getVulnerabilityCount(fileName, module, language)) + .map(promise => + promise.then(module => { + emitter.scanned(module); + return module; + }), + ); + const testedModules = await Promise.all(promises); + emitter.done(testedModules); + } catch (e) { + emitter.error(e); + } + } + + private getModules(fileName: string, source: string, language: Language): ImportedModule[] { + const parser = ModuleParserProvider.getInstance(language, this.logger, this.configuration); + if (!parser) { + return []; + } + + return parser.getModules(fileName, source, language); + } + + private getDiagnosticMessage(module: ModuleVulnerabilityCount): string { + if (!module.count) { + return ''; + } + + let message = messages.diagnosticMessagePrefix(module); + message += this.getSeverityCountMessage( + [ + ModuleVulnerabilityCountSeverity.Critical, + ModuleVulnerabilityCountSeverity.High, + ModuleVulnerabilityCountSeverity.Medium, + ModuleVulnerabilityCountSeverity.Low, + ], + module, + ); + message += messages.decoratorMessage(module.count); + return message; + } + + private getSeverityCountMessage( + severities: ModuleVulnerabilityCountSeverity[], + module: ModuleVulnerabilityCount, + ): string { + if (!module.severityCounts) { + return module.count ? module.count : ''; + } + + const content: string[] = []; + for (const severity of severities) { + if (module.severityCounts[severity] > 0) { + content.push(`${module.severityCounts[severity]} ${severity}`); + } + } + + return content.join(', '); + } +} diff --git a/src/snyk/snykOss/services/vulnerabilityCount/vulnerabilityCountProviderLS.ts b/src/snyk/snykOss/services/vulnerabilityCount/vulnerabilityCountProviderLS.ts new file mode 100644 index 000000000..f1d746aa9 --- /dev/null +++ b/src/snyk/snykOss/services/vulnerabilityCount/vulnerabilityCountProviderLS.ts @@ -0,0 +1,230 @@ +import { CliError } from '../../../cli/services/cliService'; +import { Language } from '../../../common/types'; +import { ILanguageClientAdapter } from '../../../common/vscode/languageClient'; +import { ITextDocumentAdapter } from '../../../common/vscode/textdocument'; +import { InlineValueText, LSPTextDocument } from '../../../common/vscode/types'; +import { IUriAdapter } from '../../../common/vscode/uri'; +import { OssFileResult, OssResultBody, OssSeverity, OssVulnerability, isResultCliError } from '../../ossResult'; +import { OssServiceLanguageServer } from '../../ossServiceLanguageServer'; +import { ImportedModule, ModuleVulnerabilityCount, SeverityCounts } from './importedModule'; + +export class ModuleVulnerabilityCountProviderLS { + constructor( + private readonly ossService: OssServiceLanguageServer, + private readonly lca: ILanguageClientAdapter, + private readonly uriAdapter: IUriAdapter, + private readonly textDocumentAdapter: ITextDocumentAdapter, + ) {} + + async getVulnerabilityCount( + fileName: string, + module: ImportedModule, + language: Language, + ): Promise { + const notCalculated = { + name: module.name, + fileName: module.fileName, + line: null, + range: null, + hasCount: false, + }; + + if ([Language.TypeScript, Language.JavaScript, Language.PJSON].includes(language)) { + const ossResult = this.getResultArray(); + if (!ossResult) { + return notCalculated; + } + + return this.mapOssResult(module, ossResult); + } else if (language == Language.HTML && module.loc) { + const uri = this.uriAdapter.file(fileName).toString(); + const doc: LSPTextDocument = this.textDocumentAdapter.create(uri, 'HTML', 1, ''); + const line = module.loc.start.line - 1; + const param = { + textDocument: { uri: doc.uri }, + range: { + start: { line: line, character: module.loc.start.column }, + end: { line: line, character: module.loc.end.column }, + }, + }; + const inlineValues: InlineValueText[] = await this.lca + .getLanguageClient() + .sendRequest('textDocument/inlineValue', param); + + if (inlineValues.length > 0) { + return { + name: module.name, + version: module.version, + fileName: module.fileName, + line: module.line, + range: module.loc, + count: inlineValues[0].text, + hasCount: true, + } as ModuleVulnerabilityCount; + } + } + + return notCalculated; + } + + isFilePartOfOssTest(filePath: string, ossFileResult: OssFileResult): boolean { + if (isResultCliError(ossFileResult)) { + return false; + } + + // File is considered to be part of OSS test if it has common root directory between OSS result path and filename path. + // This is since package.json always lies in the root directory folder of a project. + return filePath.startsWith(ossFileResult.path); + } + + private mapOssResult(module: ImportedModule, ossResult: ReadonlyArray): ModuleVulnerabilityCount { + const notCalculated = { + name: module.name, + fileName: module.fileName, + line: null, + hasCount: false, + range: null, + }; + + for (const fileResult of ossResult) { + if (!this.isFilePartOfOssTest(module.fileName, fileResult)) { + continue; + } + + const vulnerabilities = this.getUniqueVulnerabilities((fileResult as OssResultBody).vulnerabilities); + + // Sum up all vulnerabilities detected in first-level dependencies by OSS matching the imported module name. + // Ideally we want to use the same mechanism as NPM for determining the version used within users code. For now we stick with direct-vulnerability surfacing only. + const directVulnerabilities = vulnerabilities + .filter(v => v.name === module.name) + .filter(v => v.from.length == 2 && v.from[1].startsWith(module.name)); + const vulnerabilityCount = directVulnerabilities.length; + + // NPM allows declaration of the same direct dependency with multiple versions of it (e.g. {"dependencies": "webpack": "^4.44.1", "webpack": "^4.44.2",}). Thus, we should account for vulnerabilities that can be in different versions of the same package. + const hasSingleVersionVulnerability = directVulnerabilities.every( + vuln => vuln.version == directVulnerabilities[0].version, + ); + + let moduleVersion; + if (directVulnerabilities.length && hasSingleVersionVulnerability) { + moduleVersion = directVulnerabilities[0].version; + } + + const severityCounts = this.getSeverityCounts(directVulnerabilities); + const mostSevereVulnerability = this.getMostSevereVulnerability(directVulnerabilities); + + return { + name: module.name, + version: moduleVersion, + fileName: module.fileName, + count: `${vulnerabilityCount}`, + line: module.line, + range: module.loc, + hasCount: vulnerabilityCount > 0, + severityCounts, + mostSevereVulnerabilityId: mostSevereVulnerability?.id, + }; + } + + return notCalculated; + } + + private getSeverityCounts(directVulnerabilities: OssVulnerability[]): SeverityCounts { + return directVulnerabilities + .map(v => v.severity) + .reduce( + (arr, severity) => ({ + ...arr, + [severity]: directVulnerabilities.filter(v => v.severity == severity).length, + }), + {} as SeverityCounts, + ); + } + + private getMostSevereVulnerability(vulnerabilities: OssVulnerability[]): OssVulnerability | null { + return vulnerabilities.sort((a, b) => { + if (!a.cvssScore && !b.cvssScore) return 0; + if (!a.cvssScore) return 1; + if (!b.cvssScore) return -1; + + const cvssScore1 = parseFloat(a.cvssScore); + const cvssScore2 = parseFloat(b.cvssScore); + if (cvssScore1 > cvssScore2) { + return -1; + } else if (cvssScore1 < cvssScore2) { + return 1; + } + + return 0; + })?.[0]; + } + + public getResultArray = (): ReadonlyArray | undefined => { + if (!this.ossService.result) { + return undefined; + } + + let tempResultArray: OssFileResult[] = []; + let resultCache = new Map(); + + for (const [, value] of this.ossService.result) { + // value is Error + if (value instanceof Error) { + tempResultArray.push(new CliError(value as Error)); + } + // value is Issue[] + else { + for (const issue of value) { + // try to access list of vulns for the current file + let res = resultCache.get(issue.filePath); + + // add list of vulns to local cache if not there yet + if (res === undefined) { + res = { + path: issue.filePath, + vulnerabilities: [], + projectName: issue.additionalData.projectName, + displayTargetFile: issue.additionalData.displayTargetFile, + packageManager: issue.additionalData.packageManager, + }; + resultCache.set(issue.filePath, res); + } + + let tempVuln: OssVulnerability = { + id: issue.id, + license: issue.additionalData.license, + identifiers: issue.additionalData.identifiers, + title: issue.title, + description: issue.additionalData.description, + language: issue.additionalData.language, + packageManager: issue.additionalData.packageManager, + packageName: issue.additionalData.packageName, + severity: issue.severity as unknown as OssSeverity, + name: issue.additionalData.name, + version: issue.additionalData.version, + exploit: issue.additionalData.exploit, + + CVSSv3: issue.additionalData.CVSSv3, + cvssScore: issue.additionalData.cvssScore, + + fixedIn: issue.additionalData.fixedIn, + from: issue.additionalData.from, + upgradePath: issue.additionalData.upgradePath, + isPatchable: issue.additionalData.isPatchable, + isUpgradable: issue.additionalData.isUpgradable, + }; + res.vulnerabilities.push(tempVuln); + } + } + } + + // copy cached results to final result arra + resultCache.forEach(value => tempResultArray.push(value)); + + return tempResultArray; + }; + + private getUniqueVulnerabilities(vulnerabilities: OssVulnerability[]): OssVulnerability[] { + return vulnerabilities.filter((val, i, arr) => arr.findIndex(el => el.id === val.id) == i); + } +} From 8e795a1e545432b8485baafb2292d0cc8765f9ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Scha=CC=88fer?= <101886095+PeterSchafer@users.noreply.github.com> Date: Mon, 30 Oct 2023 18:28:35 +0100 Subject: [PATCH 03/13] fix: creating subject --- src/snyk/common/services/productService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/snyk/common/services/productService.ts b/src/snyk/common/services/productService.ts index f4e2eef52..efb4f06b1 100644 --- a/src/snyk/common/services/productService.ts +++ b/src/snyk/common/services/productService.ts @@ -30,7 +30,7 @@ export interface IProductService extends AnalysisStatusProvider, Disposable { export abstract class ProductService extends AnalysisStatusProvider implements IProductService { private _result: ProductResult; - readonly newResultAvailable$ = new Subject; + readonly newResultAvailable$ = new Subject(); // Track running scan count. Assumption: server sends N success/error messages for N scans in progress. private runningScanCount = 0; From b97b472548fde5e3ee480ab3546bc8135ccb9aab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Scha=CC=88fer?= <101886095+PeterSchafer@users.noreply.github.com> Date: Mon, 30 Oct 2023 18:28:59 +0100 Subject: [PATCH 04/13] chore: introduce static getOssIssueArgCommand() --- src/snyk/snykOss/services/ossService.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/snyk/snykOss/services/ossService.ts b/src/snyk/snykOss/services/ossService.ts index 0a1cda3d7..3a76ead48 100644 --- a/src/snyk/snykOss/services/ossService.ts +++ b/src/snyk/snykOss/services/ossService.ts @@ -15,7 +15,7 @@ import { IWebViewProvider } from '../../common/views/webviewProvider'; import { ExtensionContext } from '../../common/vscode/extensionContext'; import { IVSCodeWorkspace } from '../../common/vscode/workspace'; import { messages } from '../messages/test'; -import { isResultCliError, OssFileResult, OssResult, OssSeverity, OssVulnerability } from '../ossResult'; +import { OssFileResult, OssResult, OssSeverity, OssVulnerability, isResultCliError } from '../ossResult'; import { OssIssueCommandArg } from '../views/ossVulnerabilityTreeProvider'; import { DailyScanJob } from '../watchers/dailyScanJob'; import createManifestFileWatcher from '../watchers/manifestFileWatcher'; @@ -173,6 +173,13 @@ export class OssService extends CliService { getOssIssueCommandArg( vulnerability: OssVulnerability, allVulnerabilities: OssVulnerability[], + ): Promise { + return OssService.getOssIssueCommandArg(vulnerability, allVulnerabilities); + } + + static getOssIssueCommandArg( + vulnerability: OssVulnerability, + allVulnerabilities: OssVulnerability[], ): Promise { return new Promise((resolve, reject) => { const matchingIdVulnerabilities = allVulnerabilities.filter(v => v.id === vulnerability.id); From cc738340b1b9716915f6ec033bcba1214a4a822f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Scha=CC=88fer?= <101886095+PeterSchafer@users.noreply.github.com> Date: Mon, 30 Oct 2023 18:43:32 +0100 Subject: [PATCH 05/13] chore: adapt vuln count code action provider --- .../vulnerabilityCodeActionProviderLS.ts | 80 +++++++++++++++++++ .../ossVulnerabilityCountServiceLS.ts | 27 ++++--- 2 files changed, 94 insertions(+), 13 deletions(-) create mode 100644 src/snyk/snykOss/codeActions/vulnerabilityCodeActionProviderLS.ts diff --git a/src/snyk/snykOss/codeActions/vulnerabilityCodeActionProviderLS.ts b/src/snyk/snykOss/codeActions/vulnerabilityCodeActionProviderLS.ts new file mode 100644 index 000000000..de35e9bdb --- /dev/null +++ b/src/snyk/snykOss/codeActions/vulnerabilityCodeActionProviderLS.ts @@ -0,0 +1,80 @@ +import { IAnalytics } from '../../common/analytics/itly'; +import { OpenCommandIssueType, OpenIssueCommandArg } from '../../common/commands/types'; +import { SNYK_OPEN_ISSUE_COMMAND } from '../../common/constants/commands'; +import { IDE_NAME } from '../../common/constants/general'; +import { ICodeActionKindAdapter } from '../../common/vscode/codeAction'; +import { + CodeAction, + CodeActionContext, + CodeActionKind, + CodeActionProvider, + Command, + ProviderResult, + Range, + Selection, + TextDocument, +} from '../../common/vscode/types'; +import { DIAGNOSTICS_OSS_COLLECTION_NAME } from '../../snykCode/constants/analysis'; +import { messages } from '../messages/vulnerabilityCount'; +import { isResultCliError } from '../ossResult'; +import { OssService } from '../services/ossService'; +import { ModuleVulnerabilityCountProviderLS } from '../services/vulnerabilityCount/vulnerabilityCountProviderLS'; + +export class VulnerabilityCodeActionProviderLS implements CodeActionProvider { + public codeActionKinds: ReadonlyArray = [this.codeActionKindProvider.getQuickFix()]; + + constructor( + private readonly vulnerabilityCountProvider: ModuleVulnerabilityCountProviderLS, + private readonly codeActionKindProvider: ICodeActionKindAdapter, + private readonly analytics: IAnalytics, + ) {} + + async provideCodeActions( + document: TextDocument, + _: Range | Selection, + context: CodeActionContext, + ): Promise> { + const ossDiagnostics = context.diagnostics.filter(d => d.source === DIAGNOSTICS_OSS_COLLECTION_NAME); + if (!ossDiagnostics.length) { + return; + } + + const ossResult = this.vulnerabilityCountProvider.getResultArray(); + if (!ossResult) { + return; + } + + const fileResult = ossResult.find( + res => !isResultCliError(res) && this.vulnerabilityCountProvider.isFilePartOfOssTest(document.fileName, res), + ); + + if (!fileResult || isResultCliError(fileResult)) { + return; + } + + for (const diagnostic of ossDiagnostics) { + const vulnerability = fileResult.vulnerabilities.find(vuln => vuln.id === diagnostic.code); + if (!vulnerability) { + continue; + } + + const command: Command = { + command: SNYK_OPEN_ISSUE_COMMAND, + title: messages.showMostSevereVulnerability, + arguments: [ + { + issueType: OpenCommandIssueType.OssVulnerability, + issue: await OssService.getOssIssueCommandArg(vulnerability, fileResult.vulnerabilities), + } as OpenIssueCommandArg, + ], + }; + + this.analytics.logQuickFixIsDisplayed({ + quickFixType: ['Show Most Severe Vulnerability'], + ide: IDE_NAME, + }); + + return [command]; + } + } +} diff --git a/src/snyk/snykOss/services/vulnerabilityCount/ossVulnerabilityCountServiceLS.ts b/src/snyk/snykOss/services/vulnerabilityCount/ossVulnerabilityCountServiceLS.ts index c8159c81a..ab4b58355 100644 --- a/src/snyk/snykOss/services/vulnerabilityCount/ossVulnerabilityCountServiceLS.ts +++ b/src/snyk/snykOss/services/vulnerabilityCount/ossVulnerabilityCountServiceLS.ts @@ -1,6 +1,7 @@ import { Subscription } from 'rxjs'; import { IAnalytics } from '../../../common/analytics/itly'; import { IConfiguration } from '../../../common/configuration/configuration'; +import { HTML, JAVASCRIPT, PJSON, TYPESCRIPT } from '../../../common/constants/languageConsts'; import { ILog } from '../../../common/logger/interfaces'; import { getSupportedLanguage, isValidModuleName } from '../../../common/parsing'; import { ModuleParserProvider } from '../../../common/services/moduleParserProvider'; @@ -11,6 +12,7 @@ import { Diagnostic, DiagnosticCollection, Disposable, TextDocument } from '../. import { IVSCodeWindow } from '../../../common/vscode/window'; import { IVSCodeWorkspace } from '../../../common/vscode/workspace'; import { DIAGNOSTICS_OSS_COLLECTION_NAME } from '../../../snykCode/constants/analysis'; +import { VulnerabilityCodeActionProviderLS } from '../../codeActions/vulnerabilityCodeActionProviderLS'; import { EditorDecorator } from '../../editor/editorDecorator'; import { VulnerabilityCountHoverProvider } from '../../hoverProvider/vulnerabilityCountHoverProvider'; import { messages } from '../../messages/vulnerabilityCount'; @@ -67,19 +69,18 @@ export class OssVulnerabilityCountServiceLS implements Disposable { // Subscribe to OSS scan finished updates this.ossScanFinishedSubscription = this.ossService.newResultAvailable$.subscribe(() => this.processActiveEditor()); - // [JAVASCRIPT, TYPESCRIPT, PJSON, HTML].forEach(language => { - // const provider = new VulnerabilityCodeActionProvider( - // this.ossService, - // this.vulnerabilityCountProvider, - // this.codeActionKindProvider, - // this.analytics, - // ); - // this.disposables.push( - // this.languages.registerCodeActionsProvider(language, provider, { - // providedCodeActionKinds: provider.codeActionKinds, - // }), - // ); - //}); + [JAVASCRIPT, TYPESCRIPT, PJSON, HTML].forEach(language => { + const provider = new VulnerabilityCodeActionProviderLS( + this.vulnerabilityCountProvider, + this.codeActionKindProvider, + this.analytics, + ); + this.disposables.push( + this.languages.registerCodeActionsProvider(language, provider, { + providedCodeActionKinds: provider.codeActionKinds, + }), + ); + }); this.processActiveEditor(); From 60f5d0605e810c76f2dfbd60dc3cbc63a1809c52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Scha=CC=88fer?= <101886095+PeterSchafer@users.noreply.github.com> Date: Fri, 3 Nov 2023 13:02:33 +0100 Subject: [PATCH 06/13] chore: remove dependency to old OSSService --- .../vulnerabilityCodeActionProviderLS.ts | 27 ++++++++++++++++--- src/snyk/snykOss/services/ossService.ts | 7 ----- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/snyk/snykOss/codeActions/vulnerabilityCodeActionProviderLS.ts b/src/snyk/snykOss/codeActions/vulnerabilityCodeActionProviderLS.ts index de35e9bdb..1d398a5f5 100644 --- a/src/snyk/snykOss/codeActions/vulnerabilityCodeActionProviderLS.ts +++ b/src/snyk/snykOss/codeActions/vulnerabilityCodeActionProviderLS.ts @@ -1,3 +1,4 @@ +import marked from 'marked'; import { IAnalytics } from '../../common/analytics/itly'; import { OpenCommandIssueType, OpenIssueCommandArg } from '../../common/commands/types'; import { SNYK_OPEN_ISSUE_COMMAND } from '../../common/constants/commands'; @@ -16,9 +17,9 @@ import { } from '../../common/vscode/types'; import { DIAGNOSTICS_OSS_COLLECTION_NAME } from '../../snykCode/constants/analysis'; import { messages } from '../messages/vulnerabilityCount'; -import { isResultCliError } from '../ossResult'; -import { OssService } from '../services/ossService'; +import { OssVulnerability, isResultCliError } from '../ossResult'; import { ModuleVulnerabilityCountProviderLS } from '../services/vulnerabilityCount/vulnerabilityCountProviderLS'; +import { OssIssueCommandArg } from '../views/ossVulnerabilityTreeProvider'; export class VulnerabilityCodeActionProviderLS implements CodeActionProvider { public codeActionKinds: ReadonlyArray = [this.codeActionKindProvider.getQuickFix()]; @@ -64,7 +65,7 @@ export class VulnerabilityCodeActionProviderLS implements CodeActionProvider { arguments: [ { issueType: OpenCommandIssueType.OssVulnerability, - issue: await OssService.getOssIssueCommandArg(vulnerability, fileResult.vulnerabilities), + issue: await this.getOssIssueCommandArg(vulnerability, fileResult.vulnerabilities), } as OpenIssueCommandArg, ], }; @@ -77,4 +78,24 @@ export class VulnerabilityCodeActionProviderLS implements CodeActionProvider { return [command]; } } + + getOssIssueCommandArg( + vulnerability: OssVulnerability, + allVulnerabilities: OssVulnerability[], + ): Promise { + return new Promise((resolve, reject) => { + const matchingIdVulnerabilities = allVulnerabilities.filter(v => v.id === vulnerability.id); + marked.parse(vulnerability.description, (err, overviewHtml) => { + if (err) { + return reject(err); + } + + return resolve({ + ...vulnerability, + matchingIdVulnerabilities: matchingIdVulnerabilities, + overviewHtml, + }); + }); + }); + } } diff --git a/src/snyk/snykOss/services/ossService.ts b/src/snyk/snykOss/services/ossService.ts index 3a76ead48..547785b01 100644 --- a/src/snyk/snykOss/services/ossService.ts +++ b/src/snyk/snykOss/services/ossService.ts @@ -173,13 +173,6 @@ export class OssService extends CliService { getOssIssueCommandArg( vulnerability: OssVulnerability, allVulnerabilities: OssVulnerability[], - ): Promise { - return OssService.getOssIssueCommandArg(vulnerability, allVulnerabilities); - } - - static getOssIssueCommandArg( - vulnerability: OssVulnerability, - allVulnerabilities: OssVulnerability[], ): Promise { return new Promise((resolve, reject) => { const matchingIdVulnerabilities = allVulnerabilities.filter(v => v.id === vulnerability.id); From fb021c228c76b4a20d46ffda2e5d980fd608e4b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Scha=CC=88fer?= <101886095+PeterSchafer@users.noreply.github.com> Date: Fri, 3 Nov 2023 13:16:49 +0100 Subject: [PATCH 07/13] fix: linter & cleanup --- src/snyk/base/modules/baseSnykModule.ts | 2 -- .../vulnerabilityCount/vulnerabilityCountProviderLS.ts | 8 ++++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/snyk/base/modules/baseSnykModule.ts b/src/snyk/base/modules/baseSnykModule.ts index 01b0367d8..5681dafdf 100644 --- a/src/snyk/base/modules/baseSnykModule.ts +++ b/src/snyk/base/modules/baseSnykModule.ts @@ -25,7 +25,6 @@ import { ICodeSettings } from '../../snykCode/codeSettings'; import SnykEditorsWatcher from '../../snykCode/watchers/editorsWatcher'; import { OssServiceLanguageServer } from '../../snykOss/ossServiceLanguageServer'; import { OssService } from '../../snykOss/services/ossService'; -import { OssVulnerabilityCountService } from '../../snykOss/services/vulnerabilityCount/ossVulnerabilityCountService'; import { OssVulnerabilityCountServiceLS } from '../../snykOss/services/vulnerabilityCount/ossVulnerabilityCountServiceLS'; import { IAuthenticationService } from '../services/authenticationService'; import { ScanModeService } from '../services/scanModeService'; @@ -51,7 +50,6 @@ export default abstract class BaseSnykModule implements IBaseSnykModule { protected advisorService?: AdvisorProvider; protected commandController: CommandController; protected scanModeService: ScanModeService; - protected ossVulnerabilityCountService: OssVulnerabilityCountService; protected ossVulnerabilityCountServiceLanguageServer: OssVulnerabilityCountServiceLS; protected advisorScoreDisposable: AdvisorService; protected languageServer: ILanguageServer; diff --git a/src/snyk/snykOss/services/vulnerabilityCount/vulnerabilityCountProviderLS.ts b/src/snyk/snykOss/services/vulnerabilityCount/vulnerabilityCountProviderLS.ts index f1d746aa9..79dc556de 100644 --- a/src/snyk/snykOss/services/vulnerabilityCount/vulnerabilityCountProviderLS.ts +++ b/src/snyk/snykOss/services/vulnerabilityCount/vulnerabilityCountProviderLS.ts @@ -164,13 +164,13 @@ export class ModuleVulnerabilityCountProviderLS { return undefined; } - let tempResultArray: OssFileResult[] = []; - let resultCache = new Map(); + const tempResultArray: OssFileResult[] = []; + const resultCache = new Map(); for (const [, value] of this.ossService.result) { // value is Error if (value instanceof Error) { - tempResultArray.push(new CliError(value as Error)); + tempResultArray.push(new CliError(value)); } // value is Issue[] else { @@ -190,7 +190,7 @@ export class ModuleVulnerabilityCountProviderLS { resultCache.set(issue.filePath, res); } - let tempVuln: OssVulnerability = { + const tempVuln: OssVulnerability = { id: issue.id, license: issue.additionalData.license, identifiers: issue.additionalData.identifiers, From 33f6baeef340904c772e54aaa5bdbb9f7565092d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Scha=CC=88fer?= <101886095+PeterSchafer@users.noreply.github.com> Date: Fri, 3 Nov 2023 15:13:04 +0100 Subject: [PATCH 08/13] chore: adapt test to use LS versions of classes --- .../vulnerabilityCountProviderLS.ts | 2 +- .../ossVulnerabilityCountService.test.ts | 29 ++++++------- .../vulnerabilityCountProvider.test.ts | 42 ++++++++++--------- 3 files changed, 38 insertions(+), 35 deletions(-) diff --git a/src/snyk/snykOss/services/vulnerabilityCount/vulnerabilityCountProviderLS.ts b/src/snyk/snykOss/services/vulnerabilityCount/vulnerabilityCountProviderLS.ts index 79dc556de..b1b20675a 100644 --- a/src/snyk/snykOss/services/vulnerabilityCount/vulnerabilityCountProviderLS.ts +++ b/src/snyk/snykOss/services/vulnerabilityCount/vulnerabilityCountProviderLS.ts @@ -224,7 +224,7 @@ export class ModuleVulnerabilityCountProviderLS { return tempResultArray; }; - private getUniqueVulnerabilities(vulnerabilities: OssVulnerability[]): OssVulnerability[] { + public getUniqueVulnerabilities(vulnerabilities: OssVulnerability[]): OssVulnerability[] { return vulnerabilities.filter((val, i, arr) => arr.findIndex(el => el.id === val.id) == i); } } diff --git a/src/test/unit/snykOss/services/vulnerabilityCount/ossVulnerabilityCountService.test.ts b/src/test/unit/snykOss/services/vulnerabilityCount/ossVulnerabilityCountService.test.ts index 1e8277bc5..5a444dceb 100644 --- a/src/test/unit/snykOss/services/vulnerabilityCount/ossVulnerabilityCountService.test.ts +++ b/src/test/unit/snykOss/services/vulnerabilityCount/ossVulnerabilityCountService.test.ts @@ -4,34 +4,35 @@ import sinon from 'sinon'; import { IAnalytics } from '../../../../../snyk/common/analytics/itly'; import { IConfiguration } from '../../../../../snyk/common/configuration/configuration'; import { ICodeActionKindAdapter } from '../../../../../snyk/common/vscode/codeAction'; +import { ILanguageClientAdapter } from '../../../../../snyk/common/vscode/languageClient'; import { IVSCodeLanguages } from '../../../../../snyk/common/vscode/languages'; +import { ITextDocumentAdapter } from '../../../../../snyk/common/vscode/textdocument'; import { IThemeColorAdapter } from '../../../../../snyk/common/vscode/theme'; import { TextDocument, TextEditor } from '../../../../../snyk/common/vscode/types'; +import { IUriAdapter } from '../../../../../snyk/common/vscode/uri'; import { IVSCodeWindow } from '../../../../../snyk/common/vscode/window'; import { IVSCodeWorkspace } from '../../../../../snyk/common/vscode/workspace'; import { EditorDecorator } from '../../../../../snyk/snykOss/editor/editorDecorator'; import { OssFileResult } from '../../../../../snyk/snykOss/ossResult'; -import { OssService } from '../../../../../snyk/snykOss/services/ossService'; -import { OssVulnerabilityCountService } from '../../../../../snyk/snykOss/services/vulnerabilityCount/ossVulnerabilityCountService'; -import { ModuleVulnerabilityCountProvider } from '../../../../../snyk/snykOss/services/vulnerabilityCount/vulnerabilityCountProvider'; +import { OssServiceLanguageServer } from '../../../../../snyk/snykOss/ossServiceLanguageServer'; +import { OssVulnerabilityCountServiceLS } from '../../../../../snyk/snykOss/services/vulnerabilityCount/ossVulnerabilityCountServiceLS'; +import { ModuleVulnerabilityCountProviderLS } from '../../../../../snyk/snykOss/services/vulnerabilityCount/vulnerabilityCountProviderLS'; import { LoggerMock } from '../../../mocks/logger.mock'; -import { ILanguageClientAdapter } from '../../../../../snyk/common/vscode/languageClient'; -import { IUriAdapter } from '../../../../../snyk/common/vscode/uri'; -import { ITextDocumentAdapter } from '../../../../../snyk/common/vscode/textdocument'; suite('OSS VulnerabilityCountService', () => { let workspace: IVSCodeWorkspace; let window: IVSCodeWindow; let languages: IVSCodeLanguages; - let ossVulnerabilityCountService: OssVulnerabilityCountService; - let ossService: OssService; - let vulnerabilityCountProvider: ModuleVulnerabilityCountProvider; + let ossVulnerabilityCountService: OssVulnerabilityCountServiceLS; + let ossService: OssServiceLanguageServer; + let vulnerabilityCountProvider: ModuleVulnerabilityCountProviderLS; setup(() => { const logger = new LoggerMock(); ossService = { scanFinished$: EMPTY, - } as unknown as OssService; + newResultAvailable$: EMPTY, + } as unknown as OssServiceLanguageServer; workspace = {} as IVSCodeWorkspace; window = { createTextEditorDecorationType: sinon.fake(), @@ -41,7 +42,7 @@ suite('OSS VulnerabilityCountService', () => { registerCodeActionsProvider: sinon.fake(), registerHoverProvider: sinon.fake(), } as unknown as IVSCodeLanguages; - vulnerabilityCountProvider = new ModuleVulnerabilityCountProvider( + vulnerabilityCountProvider = new ModuleVulnerabilityCountProviderLS( ossService, {} as ILanguageClientAdapter, {} as IUriAdapter, @@ -55,7 +56,7 @@ suite('OSS VulnerabilityCountService', () => { const analytics = {} as IAnalytics; const configuration = {} as IConfiguration; - ossVulnerabilityCountService = new OssVulnerabilityCountService( + ossVulnerabilityCountService = new OssVulnerabilityCountServiceLS( workspace, window, languages, @@ -125,7 +126,7 @@ suite('OSS VulnerabilityCountService', () => { }); test("Doesn't process if file is supported and OSS scan hasn't run", () => { - ossService.getResultArray = () => undefined; + vulnerabilityCountProvider.getResultArray = () => undefined; const tsDocument = { fileName: 'C:\\git\\project\\test.ts', languageId: 'typescript', @@ -150,7 +151,7 @@ suite('OSS VulnerabilityCountService', () => { languageId: 'typescript', getText: () => 'const x = require("react")', } as TextDocument; - ossService.getResultArray = () => [{} as OssFileResult]; + vulnerabilityCountProvider.getResultArray = () => [{} as OssFileResult]; sinon.stub(vulnerabilityCountProvider, 'isFilePartOfOssTest').returns(true); const processed = ossVulnerabilityCountService.processFile(document); diff --git a/src/test/unit/snykOss/services/vulnerabilityCount/vulnerabilityCountProvider.test.ts b/src/test/unit/snykOss/services/vulnerabilityCount/vulnerabilityCountProvider.test.ts index fb5eb9ca4..79c933516 100644 --- a/src/test/unit/snykOss/services/vulnerabilityCount/vulnerabilityCountProvider.test.ts +++ b/src/test/unit/snykOss/services/vulnerabilityCount/vulnerabilityCountProvider.test.ts @@ -2,17 +2,17 @@ import { deepStrictEqual, strictEqual } from 'assert'; import sinon from 'sinon'; import { CliError } from '../../../../../snyk/cli/services/cliService'; import { Language } from '../../../../../snyk/common/types'; -import { OssResultBody, OssVulnerability } from '../../../../../snyk/snykOss/ossResult'; -import { OssService } from '../../../../../snyk/snykOss/services/ossService'; -import { ImportedModule } from '../../../../../snyk/snykOss/services/vulnerabilityCount/importedModule'; -import { ModuleVulnerabilityCountProvider } from '../../../../../snyk/snykOss/services/vulnerabilityCount/vulnerabilityCountProvider'; import { ILanguageClientAdapter } from '../../../../../snyk/common/vscode/languageClient'; -import { IUriAdapter } from '../../../../../snyk/common/vscode/uri'; import { ITextDocumentAdapter } from '../../../../../snyk/common/vscode/textdocument'; +import { IUriAdapter } from '../../../../../snyk/common/vscode/uri'; +import { OssResultBody, OssVulnerability } from '../../../../../snyk/snykOss/ossResult'; +import { OssServiceLanguageServer } from '../../../../../snyk/snykOss/ossServiceLanguageServer'; +import { ImportedModule } from '../../../../../snyk/snykOss/services/vulnerabilityCount/importedModule'; +import { ModuleVulnerabilityCountProviderLS } from '../../../../../snyk/snykOss/services/vulnerabilityCount/vulnerabilityCountProviderLS'; suite('OSS ModuleVulnerabilityCountProvider', () => { - let ossService: OssService; - let vulnerabilityCountProvider: ModuleVulnerabilityCountProvider; + let ossService: OssServiceLanguageServer; + let vulnerabilityCountProvider: ModuleVulnerabilityCountProviderLS; const sampleFilePath = 'C:\\git\\project\\test.js'; const sampleModuleName = 'mongo-express'; @@ -54,8 +54,8 @@ suite('OSS ModuleVulnerabilityCountProvider', () => { ]; setup(() => { - ossService = {} as OssService; - vulnerabilityCountProvider = new ModuleVulnerabilityCountProvider( + ossService = {} as OssServiceLanguageServer; + vulnerabilityCountProvider = new ModuleVulnerabilityCountProviderLS( ossService, {} as ILanguageClientAdapter, {} as IUriAdapter, @@ -68,7 +68,7 @@ suite('OSS ModuleVulnerabilityCountProvider', () => { }); test('Not calculated if JS/TS results are not provided', async () => { - ossService.getResultArray = () => undefined; + vulnerabilityCountProvider.getResultArray = () => undefined; const tsCount = await vulnerabilityCountProvider.getVulnerabilityCount( 'test.ts', @@ -89,8 +89,8 @@ suite('OSS ModuleVulnerabilityCountProvider', () => { }); test('Gets TS/JS imported module vulnerability results correctly', async () => { - ossService.getResultArray = () => sampleOssResults; - ossService.getUniqueVulnerabilities = () => sampleOssResults[0].vulnerabilities; + vulnerabilityCountProvider.getResultArray = () => sampleOssResults; + vulnerabilityCountProvider.getUniqueVulnerabilities = () => sampleOssResults[0].vulnerabilities; const count = await vulnerabilityCountProvider.getVulnerabilityCount( 'test.ts', @@ -116,8 +116,8 @@ suite('OSS ModuleVulnerabilityCountProvider', () => { }); test('Gets package.json dependency vulnerability results correctly', async () => { - ossService.getResultArray = () => sampleOssResults; - ossService.getUniqueVulnerabilities = () => sampleOssResults[0].vulnerabilities; + vulnerabilityCountProvider.getResultArray = () => sampleOssResults; + vulnerabilityCountProvider.getUniqueVulnerabilities = () => sampleOssResults[0].vulnerabilities; const count = await vulnerabilityCountProvider.getVulnerabilityCount( 'test.ts', @@ -145,8 +145,8 @@ suite('OSS ModuleVulnerabilityCountProvider', () => { ], }, ]; - ossService.getResultArray = () => ossResultsWithIndirectVulnerability; - ossService.getUniqueVulnerabilities = () => ossResultsWithIndirectVulnerability[0].vulnerabilities; + vulnerabilityCountProvider.getResultArray = () => ossResultsWithIndirectVulnerability; + vulnerabilityCountProvider.getUniqueVulnerabilities = () => ossResultsWithIndirectVulnerability[0].vulnerabilities; const count = await vulnerabilityCountProvider.getVulnerabilityCount( 'test.ts', @@ -177,8 +177,9 @@ suite('OSS ModuleVulnerabilityCountProvider', () => { ], }, ]; - ossService.getResultArray = () => ossResultsWithMultipleVersionsVulnerability; - ossService.getUniqueVulnerabilities = () => ossResultsWithMultipleVersionsVulnerability[0].vulnerabilities; + vulnerabilityCountProvider.getResultArray = () => ossResultsWithMultipleVersionsVulnerability; + vulnerabilityCountProvider.getUniqueVulnerabilities = () => + ossResultsWithMultipleVersionsVulnerability[0].vulnerabilities; const count = await vulnerabilityCountProvider.getVulnerabilityCount( 'test.ts', @@ -207,8 +208,9 @@ suite('OSS ModuleVulnerabilityCountProvider', () => { ], }, ]; - ossService.getResultArray = () => ossResultsWithMultipleVersionsVulnerability; - ossService.getUniqueVulnerabilities = () => ossResultsWithMultipleVersionsVulnerability[0].vulnerabilities; + vulnerabilityCountProvider.getResultArray = () => ossResultsWithMultipleVersionsVulnerability; + vulnerabilityCountProvider.getUniqueVulnerabilities = () => + ossResultsWithMultipleVersionsVulnerability[0].vulnerabilities; const count = await vulnerabilityCountProvider.getVulnerabilityCount( 'test.ts', From 687b58d016d2d12f3612da6c37922b6937e4a62e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Scha=CC=88fer?= <101886095+PeterSchafer@users.noreply.github.com> Date: Fri, 3 Nov 2023 15:13:15 +0100 Subject: [PATCH 09/13] chore: delete now obsolote classes --- .../vulnerabilityCodeActionProvider.ts | 81 ----- .../ossVulnerabilityCountService.ts | 278 ------------------ .../vulnerabilityCountProvider.ts | 161 ---------- 3 files changed, 520 deletions(-) delete mode 100644 src/snyk/snykOss/codeActions/vulnerabilityCodeActionProvider.ts delete mode 100644 src/snyk/snykOss/services/vulnerabilityCount/ossVulnerabilityCountService.ts delete mode 100644 src/snyk/snykOss/services/vulnerabilityCount/vulnerabilityCountProvider.ts diff --git a/src/snyk/snykOss/codeActions/vulnerabilityCodeActionProvider.ts b/src/snyk/snykOss/codeActions/vulnerabilityCodeActionProvider.ts deleted file mode 100644 index 68b1b3535..000000000 --- a/src/snyk/snykOss/codeActions/vulnerabilityCodeActionProvider.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { IAnalytics } from '../../common/analytics/itly'; -import { OpenCommandIssueType, OpenIssueCommandArg } from '../../common/commands/types'; -import { SNYK_OPEN_ISSUE_COMMAND } from '../../common/constants/commands'; -import { IDE_NAME } from '../../common/constants/general'; -import { ICodeActionKindAdapter } from '../../common/vscode/codeAction'; -import { - CodeAction, - CodeActionContext, - CodeActionKind, - CodeActionProvider, - Command, - ProviderResult, - Range, - Selection, - TextDocument, -} from '../../common/vscode/types'; -import { DIAGNOSTICS_OSS_COLLECTION_NAME } from '../../snykCode/constants/analysis'; -import { messages } from '../messages/vulnerabilityCount'; -import { isResultCliError } from '../ossResult'; -import { OssService } from '../services/ossService'; -import { ModuleVulnerabilityCountProvider } from '../services/vulnerabilityCount/vulnerabilityCountProvider'; - -export class VulnerabilityCodeActionProvider implements CodeActionProvider { - public codeActionKinds: ReadonlyArray = [this.codeActionKindProvider.getQuickFix()]; - - constructor( - private readonly ossService: OssService, - private readonly vulnerabilityCountProvider: ModuleVulnerabilityCountProvider, - private readonly codeActionKindProvider: ICodeActionKindAdapter, - private readonly analytics: IAnalytics, - ) {} - - async provideCodeActions( - document: TextDocument, - _: Range | Selection, - context: CodeActionContext, - ): Promise> { - const ossDiagnostics = context.diagnostics.filter(d => d.source === DIAGNOSTICS_OSS_COLLECTION_NAME); - if (!ossDiagnostics.length) { - return; - } - - const ossResult = this.ossService.getResultArray(); - if (!ossResult) { - return; - } - - const fileResult = ossResult.find( - res => !isResultCliError(res) && this.vulnerabilityCountProvider.isFilePartOfOssTest(document.fileName, res), - ); - - if (!fileResult || isResultCliError(fileResult)) { - return; - } - - for (const diagnostic of ossDiagnostics) { - const vulnerability = fileResult.vulnerabilities.find(vuln => vuln.id === diagnostic.code); - if (!vulnerability) { - continue; - } - - const command: Command = { - command: SNYK_OPEN_ISSUE_COMMAND, - title: messages.showMostSevereVulnerability, - arguments: [ - { - issueType: OpenCommandIssueType.OssVulnerability, - issue: await this.ossService.getOssIssueCommandArg(vulnerability, fileResult.vulnerabilities), - } as OpenIssueCommandArg, - ], - }; - - this.analytics.logQuickFixIsDisplayed({ - quickFixType: ['Show Most Severe Vulnerability'], - ide: IDE_NAME, - }); - - return [command]; - } - } -} diff --git a/src/snyk/snykOss/services/vulnerabilityCount/ossVulnerabilityCountService.ts b/src/snyk/snykOss/services/vulnerabilityCount/ossVulnerabilityCountService.ts deleted file mode 100644 index 787d0f36c..000000000 --- a/src/snyk/snykOss/services/vulnerabilityCount/ossVulnerabilityCountService.ts +++ /dev/null @@ -1,278 +0,0 @@ -import { Subscription } from 'rxjs'; -import { IAnalytics } from '../../../common/analytics/itly'; -import { IConfiguration } from '../../../common/configuration/configuration'; -import { HTML, JAVASCRIPT, PJSON, TYPESCRIPT } from '../../../common/constants/languageConsts'; -import { ILog } from '../../../common/logger/interfaces'; -import { getSupportedLanguage, isValidModuleName } from '../../../common/parsing'; -import { ModuleParserProvider } from '../../../common/services/moduleParserProvider'; -import { Language } from '../../../common/types'; -import { ICodeActionKindAdapter } from '../../../common/vscode/codeAction'; -import { IVSCodeLanguages } from '../../../common/vscode/languages'; -import { Diagnostic, DiagnosticCollection, Disposable, TextDocument } from '../../../common/vscode/types'; -import { IVSCodeWindow } from '../../../common/vscode/window'; -import { IVSCodeWorkspace } from '../../../common/vscode/workspace'; -import { DIAGNOSTICS_OSS_COLLECTION_NAME } from '../../../snykCode/constants/analysis'; -import { VulnerabilityCodeActionProvider } from '../../codeActions/vulnerabilityCodeActionProvider'; -import { EditorDecorator } from '../../editor/editorDecorator'; -import { VulnerabilityCountHoverProvider } from '../../hoverProvider/vulnerabilityCountHoverProvider'; -import { messages } from '../../messages/vulnerabilityCount'; -import { VulnerabilityCountEmitter, VulnerabilityCountEvents } from '../../vulnerabilityCountEmitter'; -import { OssService } from '../ossService'; -import { ImportedModule, ModuleVulnerabilityCount, ModuleVulnerabilityCountSeverity } from './importedModule'; -import { ModuleVulnerabilityCountProvider } from './vulnerabilityCountProvider'; - -export enum SupportedLanguage { - TypeScript, - JavaScript, - HTML, - PJSON, -} - -export class OssVulnerabilityCountService implements Disposable { - protected disposables: Disposable[] = []; - protected ossScanFinishedSubscription: Subscription; - - private fileEmitters = new Map(); - private diagnostics: DiagnosticCollection | undefined; - - constructor( - private readonly workspace: IVSCodeWorkspace, - private readonly window: IVSCodeWindow, - private readonly languages: IVSCodeLanguages, - private readonly vulnerabilityCountProvider: ModuleVulnerabilityCountProvider, - private readonly ossService: OssService, - private readonly logger: ILog, - private readonly editorDecorator: EditorDecorator, - private readonly codeActionKindProvider: ICodeActionKindAdapter, - private readonly analytics: IAnalytics, - private readonly configuration: IConfiguration, - ) {} - - activate(): boolean { - this.disposables.push( - (this.diagnostics = this.languages.createDiagnosticCollection(DIAGNOSTICS_OSS_COLLECTION_NAME)), - this.workspace.onDidChangeTextDocument(ev => { - if (ev.contentChanges.length) { - this.processFile(ev.document); - } - }), - this.window.onDidChangeActiveTextEditor(ev => { - if (ev) { - this.processFile(ev.document); - } - }), - - // register hover provider - new VulnerabilityCountHoverProvider(this.languages, this.analytics).register(this.diagnostics), - ); - - // Subscribe to OSS scan finished updates - this.ossScanFinishedSubscription = this.ossService.scanFinished$.subscribe(() => this.processActiveEditor()); - - [JAVASCRIPT, TYPESCRIPT, PJSON, HTML].forEach(language => { - const provider = new VulnerabilityCodeActionProvider( - this.ossService, - this.vulnerabilityCountProvider, - this.codeActionKindProvider, - this.analytics, - ); - this.disposables.push( - this.languages.registerCodeActionsProvider(language, provider, { - providedCodeActionKinds: provider.codeActionKinds, - }), - ); - }); - - this.processActiveEditor(); - - return true; - } - - processActiveEditor(): void { - const activeEditor = this.window.getActiveTextEditor(); - if (activeEditor) { - this.processFile(activeEditor.document); - } - } - - dispose(): void { - while (this.disposables.length) { - const disposable = this.disposables.pop(); - if (disposable) { - disposable.dispose(); - } - } - - for (const emitter of this.fileEmitters.values()) { - emitter.removeAllListeners(); - } - - this.ossScanFinishedSubscription.unsubscribe(); - } - - processFile(document: TextDocument): boolean { - if (!document) { - return false; - } - - const { fileName, languageId } = document; - const supportedLanguage = getSupportedLanguage(fileName, languageId); - if (supportedLanguage === null || !this.shouldProcessFile(fileName, supportedLanguage)) { - return false; - } - - let emitter = this.fileEmitters.get(fileName); - if (emitter) { - emitter.removeAllListeners(); - this.editorDecorator.resetDecorations(fileName); - } else { - emitter = new VulnerabilityCountEmitter(); - this.fileEmitters.set(fileName, emitter); - } - - emitter.on(VulnerabilityCountEvents.Error, e => { - this.logger.error(`Error counting module vulnerabilities: ${e}`); - this.editorDecorator.resetDecorations(fileName); - }); - - emitter.on(VulnerabilityCountEvents.Start, (modules: ImportedModule[]) => { - this.editorDecorator.setScanStartDecorations(fileName, modules); - }); - emitter.on(VulnerabilityCountEvents.Scanned, (vulnerabilityCount: ModuleVulnerabilityCount) => { - this.editorDecorator.setScannedDecoration(vulnerabilityCount, true); - }); - - emitter.on(VulnerabilityCountEvents.Done, (modules: ModuleVulnerabilityCount[]) => { - this.editorDecorator.setScanDoneDecorations(fileName, modules); - this.updateDiagnostics(document, modules); - }); - - // Start - void this.getImportedModules(fileName, document.getText(), supportedLanguage, emitter); - return true; - } - - private updateDiagnostics(document: TextDocument, modules: ModuleVulnerabilityCount[]): void { - if (!this.diagnostics) { - return; - } - - const diagnostics: Diagnostic[] = []; - for (const module of modules) { - if (!module.hasCount || !module.range) { - continue; - } - - const diagnosticMessage = this.getDiagnosticMessage(module); - if (!diagnosticMessage.length) { - continue; - } - - const range = this.languages.createRange( - module.range.start.line - 1, - module.range.start.column, - module.range.end.line - 1, - module.range.end.column, - ); - - const diagnostic = this.languages.createDiagnostic(range, diagnosticMessage, 1); // Warning severity - diagnostics.push({ - ...diagnostic, - source: DIAGNOSTICS_OSS_COLLECTION_NAME, - code: module.mostSevereVulnerabilityId, - }); - } - - this.diagnostics.set(document.uri, diagnostics); - } - - private shouldProcessFile(fileName: string, language: Language): boolean { - if ([Language.TypeScript, Language.JavaScript, Language.PJSON].includes(language)) { - const ossResult = this.ossService.getResultArray(); - if (!ossResult) { - return false; - } - - for (const fileResult of ossResult) { - if (this.vulnerabilityCountProvider.isFilePartOfOssTest(fileName, fileResult)) { - return true; - } - } - - return false; - } - - return true; - } - - private async getImportedModules( - fileName: string, - content: string, - language: Language, - emitter: VulnerabilityCountEmitter, - ): Promise { - try { - const modules = this.getModules(fileName, content, language).filter(isValidModuleName); - emitter.startScanning(modules); - - const promises = modules - .map(module => this.vulnerabilityCountProvider.getVulnerabilityCount(fileName, module, language)) - .map(promise => - promise.then(module => { - emitter.scanned(module); - return module; - }), - ); - const testedModules = await Promise.all(promises); - emitter.done(testedModules); - } catch (e) { - emitter.error(e); - } - } - - private getModules(fileName: string, source: string, language: Language): ImportedModule[] { - const parser = ModuleParserProvider.getInstance(language, this.logger, this.configuration); - if (!parser) { - return []; - } - - return parser.getModules(fileName, source, language); - } - - private getDiagnosticMessage(module: ModuleVulnerabilityCount): string { - if (!module.count) { - return ''; - } - - let message = messages.diagnosticMessagePrefix(module); - message += this.getSeverityCountMessage( - [ - ModuleVulnerabilityCountSeverity.Critical, - ModuleVulnerabilityCountSeverity.High, - ModuleVulnerabilityCountSeverity.Medium, - ModuleVulnerabilityCountSeverity.Low, - ], - module, - ); - message += messages.decoratorMessage(module.count); - return message; - } - - private getSeverityCountMessage( - severities: ModuleVulnerabilityCountSeverity[], - module: ModuleVulnerabilityCount, - ): string { - if (!module.severityCounts) { - return module.count ? module.count : ''; - } - - const content: string[] = []; - for (const severity of severities) { - if (module.severityCounts[severity] > 0) { - content.push(`${module.severityCounts[severity]} ${severity}`); - } - } - - return content.join(', '); - } -} diff --git a/src/snyk/snykOss/services/vulnerabilityCount/vulnerabilityCountProvider.ts b/src/snyk/snykOss/services/vulnerabilityCount/vulnerabilityCountProvider.ts deleted file mode 100644 index 3c852ae33..000000000 --- a/src/snyk/snykOss/services/vulnerabilityCount/vulnerabilityCountProvider.ts +++ /dev/null @@ -1,161 +0,0 @@ -import { Language } from '../../../common/types'; -import { ILanguageClientAdapter } from '../../../common/vscode/languageClient'; -import { ITextDocumentAdapter } from '../../../common/vscode/textdocument'; -import { InlineValueText, LSPTextDocument } from '../../../common/vscode/types'; -import { IUriAdapter } from '../../../common/vscode/uri'; -import { OssFileResult, OssResultBody, OssVulnerability, isResultCliError } from '../../ossResult'; -import { OssService } from '../ossService'; -import { ImportedModule, ModuleVulnerabilityCount, SeverityCounts } from './importedModule'; - -export class ModuleVulnerabilityCountProvider { - constructor( - private readonly ossService: OssService, - private readonly lca: ILanguageClientAdapter, - private readonly uriAdapter: IUriAdapter, - private readonly textDocumentAdapter: ITextDocumentAdapter, - ) {} - - async getVulnerabilityCount( - fileName: string, - module: ImportedModule, - language: Language, - ): Promise { - const notCalculated = { - name: module.name, - fileName: module.fileName, - line: null, - range: null, - hasCount: false, - }; - - if ([Language.TypeScript, Language.JavaScript, Language.PJSON].includes(language)) { - // TODO use LS when OSS is moved to LS - const ossResult = this.ossService.getResultArray(); - if (!ossResult) { - return notCalculated; - } - - return this.mapOssResult(module, ossResult); - } else if (language == Language.HTML && module.loc) { - const uri = this.uriAdapter.file(fileName).toString(); - const doc: LSPTextDocument = this.textDocumentAdapter.create(uri, 'HTML', 1, ''); - const line = module.loc.start.line - 1; - const param = { - textDocument: { uri: doc.uri }, - range: { - start: { line: line, character: module.loc.start.column }, - end: { line: line, character: module.loc.end.column }, - }, - }; - const inlineValues: InlineValueText[] = await this.lca - .getLanguageClient() - .sendRequest('textDocument/inlineValue', param); - - if (inlineValues.length > 0) { - return { - name: module.name, - version: module.version, - fileName: module.fileName, - line: module.line, - range: module.loc, - count: inlineValues[0].text, - hasCount: true, - } as ModuleVulnerabilityCount; - } - } - - return notCalculated; - } - - isFilePartOfOssTest(filePath: string, ossFileResult: OssFileResult): boolean { - if (isResultCliError(ossFileResult)) { - return false; - } - - // File is considered to be part of OSS test if it has common root directory between OSS result path and filename path. - // This is since package.json always lies in the root directory folder of a project. - return filePath.startsWith(ossFileResult.path); - } - - private mapOssResult(module: ImportedModule, ossResult: ReadonlyArray): ModuleVulnerabilityCount { - const notCalculated = { - name: module.name, - fileName: module.fileName, - line: null, - hasCount: false, - range: null, - }; - - for (const fileResult of ossResult) { - if (!this.isFilePartOfOssTest(module.fileName, fileResult)) { - continue; - } - - const vulnerabilities = this.ossService.getUniqueVulnerabilities((fileResult as OssResultBody).vulnerabilities); - - // Sum up all vulnerabilities detected in first-level dependencies by OSS matching the imported module name. - // Ideally we want to use the same mechanism as NPM for determining the version used within users code. For now we stick with direct-vulnerability surfacing only. - const directVulnerabilities = vulnerabilities - .filter(v => v.name === module.name) - .filter(v => v.from.length == 2 && v.from[1].startsWith(module.name)); - const vulnerabilityCount = directVulnerabilities.length; - - // NPM allows declaration of the same direct dependency with multiple versions of it (e.g. {"dependencies": "webpack": "^4.44.1", "webpack": "^4.44.2",}). Thus, we should account for vulnerabilities that can be in different versions of the same package. - const hasSingleVersionVulnerability = directVulnerabilities.every( - vuln => vuln.version == directVulnerabilities[0].version, - ); - - let moduleVersion; - if (directVulnerabilities.length && hasSingleVersionVulnerability) { - moduleVersion = directVulnerabilities[0].version; - } - - const severityCounts = this.getSeverityCounts(directVulnerabilities); - const mostSevereVulnerability = this.getMostSevereVulnerability(directVulnerabilities); - - return { - name: module.name, - version: moduleVersion, - fileName: module.fileName, - count: `${vulnerabilityCount}`, - line: module.line, - range: module.loc, - hasCount: vulnerabilityCount > 0, - severityCounts, - mostSevereVulnerabilityId: mostSevereVulnerability?.id, - }; - } - - return notCalculated; - } - - private getSeverityCounts(directVulnerabilities: OssVulnerability[]): SeverityCounts { - return directVulnerabilities - .map(v => v.severity) - .reduce( - (arr, severity) => ({ - ...arr, - [severity]: directVulnerabilities.filter(v => v.severity == severity).length, - }), - {} as SeverityCounts, - ); - } - - private getMostSevereVulnerability(vulnerabilities: OssVulnerability[]): OssVulnerability | null { - return vulnerabilities.sort((a, b) => { - if (!a.cvssScore && !b.cvssScore) return 0; - if (!a.cvssScore) return 1; - if (!b.cvssScore) return -1; - - const cvssScore1 = parseFloat(a.cvssScore); - const cvssScore2 = parseFloat(b.cvssScore); - if (cvssScore1 > cvssScore2) { - return -1; - } else if (cvssScore1 < cvssScore2) { - return 1; - } - - return 0; - })?.[0]; - } -} From 16f6768a64c3fbb89104c99c895b7cedb4d7ec0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Scha=CC=88fer?= <101886095+PeterSchafer@users.noreply.github.com> Date: Mon, 6 Nov 2023 12:47:41 +0100 Subject: [PATCH 10/13] fix: duplicate diagnostics message --- .../vulnerabilityCount/ossVulnerabilityCountServiceLS.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/snyk/snykOss/services/vulnerabilityCount/ossVulnerabilityCountServiceLS.ts b/src/snyk/snykOss/services/vulnerabilityCount/ossVulnerabilityCountServiceLS.ts index ab4b58355..dfb8652ef 100644 --- a/src/snyk/snykOss/services/vulnerabilityCount/ossVulnerabilityCountServiceLS.ts +++ b/src/snyk/snykOss/services/vulnerabilityCount/ossVulnerabilityCountServiceLS.ts @@ -253,7 +253,7 @@ export class OssVulnerabilityCountServiceLS implements Disposable { ], module, ); - message += messages.decoratorMessage(module.count); + return message; } From 8d6a07eea54261bd8aa1d2c88bbf6965af90f123 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Scha=CC=88fer?= <101886095+PeterSchafer@users.noreply.github.com> Date: Mon, 6 Nov 2023 12:48:37 +0100 Subject: [PATCH 11/13] fix: use strict equal --- .../services/vulnerabilityCount/vulnerabilityCountProviderLS.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/snyk/snykOss/services/vulnerabilityCount/vulnerabilityCountProviderLS.ts b/src/snyk/snykOss/services/vulnerabilityCount/vulnerabilityCountProviderLS.ts index b1b20675a..0b49312a0 100644 --- a/src/snyk/snykOss/services/vulnerabilityCount/vulnerabilityCountProviderLS.ts +++ b/src/snyk/snykOss/services/vulnerabilityCount/vulnerabilityCountProviderLS.ts @@ -225,6 +225,6 @@ export class ModuleVulnerabilityCountProviderLS { }; public getUniqueVulnerabilities(vulnerabilities: OssVulnerability[]): OssVulnerability[] { - return vulnerabilities.filter((val, i, arr) => arr.findIndex(el => el.id === val.id) == i); + return vulnerabilities.filter((val, i, arr) => arr.findIndex(el => el.id === val.id) === i); } } From b5c51a299c34143e77a1a403df62d4bc784030d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Scha=CC=88fer?= <101886095+PeterSchafer@users.noreply.github.com> Date: Mon, 6 Nov 2023 13:18:34 +0100 Subject: [PATCH 12/13] chore: convert severities explicitely --- src/snyk/snykOss/ossResult.ts | 14 ++++++++++++++ .../vulnerabilityCountProviderLS.ts | 4 ++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/snyk/snykOss/ossResult.ts b/src/snyk/snykOss/ossResult.ts index 625019a49..b469695d8 100644 --- a/src/snyk/snykOss/ossResult.ts +++ b/src/snyk/snykOss/ossResult.ts @@ -1,5 +1,6 @@ import _ from 'lodash'; import { CliError } from '../cli/services/cliService'; +import { IssueSeverity } from '../common/languageServer/types'; export type OssResult = OssFileResult[] | OssFileResult; @@ -56,3 +57,16 @@ export function capitalizeOssSeverity(ossSeverity: OssSeverity): Capitalize Date: Mon, 6 Nov 2023 13:19:28 +0100 Subject: [PATCH 13/13] fix: typo --- .../services/vulnerabilityCount/vulnerabilityCountProviderLS.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/snyk/snykOss/services/vulnerabilityCount/vulnerabilityCountProviderLS.ts b/src/snyk/snykOss/services/vulnerabilityCount/vulnerabilityCountProviderLS.ts index 37c72712e..0f24e8304 100644 --- a/src/snyk/snykOss/services/vulnerabilityCount/vulnerabilityCountProviderLS.ts +++ b/src/snyk/snykOss/services/vulnerabilityCount/vulnerabilityCountProviderLS.ts @@ -218,7 +218,7 @@ export class ModuleVulnerabilityCountProviderLS { } } - // copy cached results to final result arra + // copy cached results to final result array resultCache.forEach(value => tempResultArray.push(value)); return tempResultArray;