diff --git a/package.json b/package.json index 25ab0537d..fb321914a 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,6 @@ "activationEvents": [ "onWebviewPanel:snyk.views.suggestion.code", "onWebviewPanel:snyk.views.suggestion.oss", - "onWebviewPanel:snyk.views.suggestion.oss.languageServer", "*" ], "main": "./out/extension.js", @@ -236,11 +235,6 @@ "name": "Snyk", "when": "!snyk:loggedIn || snyk:error || !snyk:workspaceFound" }, - { - "id": "snyk.views.analysis.oss.languageServer", - "name": "Open Source Security (LS)", - "when": "snyk:initialized && snyk:loggedIn && snyk:workspaceFound && !snyk:error" - }, { "id": "snyk.views.analysis.oss", "name": "Open Source Security", diff --git a/src/snyk/base/modules/baseSnykModule.ts b/src/snyk/base/modules/baseSnykModule.ts index 5681dafdf..14ed435b2 100644 --- a/src/snyk/base/modules/baseSnykModule.ts +++ b/src/snyk/base/modules/baseSnykModule.ts @@ -23,9 +23,8 @@ import { IMarkdownStringAdapter, MarkdownStringAdapter } from '../../common/vsco import { IWatcher } from '../../common/watchers/interfaces'; import { ICodeSettings } from '../../snykCode/codeSettings'; import SnykEditorsWatcher from '../../snykCode/watchers/editorsWatcher'; -import { OssServiceLanguageServer } from '../../snykOss/ossServiceLanguageServer'; -import { OssService } from '../../snykOss/services/ossService'; -import { OssVulnerabilityCountServiceLS } from '../../snykOss/services/vulnerabilityCount/ossVulnerabilityCountServiceLS'; +import { OssService } from '../../snykOss/ossService'; +import { OssVulnerabilityCountService } from '../../snykOss/services/vulnerabilityCount/ossVulnerabilityCountService'; import { IAuthenticationService } from '../services/authenticationService'; import { ScanModeService } from '../services/scanModeService'; import SnykStatusBarItem, { IStatusBarItem } from '../statusBarItem/statusBarItem'; @@ -46,11 +45,10 @@ export default abstract class BaseSnykModule implements IBaseSnykModule { protected authService: IAuthenticationService; protected downloadService: DownloadService; protected ossService?: OssService; - protected ossServiceLanguageServer?: OssServiceLanguageServer; protected advisorService?: AdvisorProvider; protected commandController: CommandController; protected scanModeService: ScanModeService; - protected ossVulnerabilityCountServiceLanguageServer: OssVulnerabilityCountServiceLS; + protected ossVulnerabilityCountService: OssVulnerabilityCountService; protected advisorScoreDisposable: AdvisorService; protected languageServer: ILanguageServer; @@ -87,6 +85,4 @@ export default abstract class BaseSnykModule implements IBaseSnykModule { } abstract runScan(): Promise; - - abstract runOssScan(): Promise; } diff --git a/src/snyk/base/modules/interfaces.ts b/src/snyk/base/modules/interfaces.ts index b1748810e..e43446fbb 100644 --- a/src/snyk/base/modules/interfaces.ts +++ b/src/snyk/base/modules/interfaces.ts @@ -17,7 +17,6 @@ export interface IBaseSnykModule { // Abstract methods runScan(): Promise; - runOssScan(manual?: boolean): Promise; } export interface ISnykLib { diff --git a/src/snyk/base/modules/snykLib.ts b/src/snyk/base/modules/snykLib.ts index 070350d5d..640b8e2fc 100644 --- a/src/snyk/base/modules/snykLib.ts +++ b/src/snyk/base/modules/snykLib.ts @@ -1,9 +1,7 @@ import * as _ from 'lodash'; -import { firstValueFrom } from 'rxjs'; -import { CliError } from '../../cli/services/cliService'; import { SupportedAnalysisProperties } from '../../common/analytics/itly'; import { configuration } from '../../common/configuration/instance'; -import { DEFAULT_SCAN_DEBOUNCE_INTERVAL, IDE_NAME, OSS_SCAN_DEBOUNCE_INTERVAL } from '../../common/constants/general'; +import { DEFAULT_SCAN_DEBOUNCE_INTERVAL, IDE_NAME } from '../../common/constants/general'; import { SNYK_CONTEXT } from '../../common/constants/views'; import { ErrorHandler } from '../../common/error/errorHandler'; import { Logger } from '../../common/logger/logger'; @@ -23,7 +21,6 @@ export default class SnykLib extends BaseSnykModule implements ISnykLib { return; } - // Only starts OSS scan. Code & IaC scans are managed by LS Logger.info('Starting full scan'); await this.contextService.setContext(SNYK_CONTEXT.AUTHENTICATING, false); @@ -39,7 +36,6 @@ export default class SnykLib extends BaseSnykModule implements ISnykLib { const workspacePaths = vsCodeWorkspace.getWorkspaceFolders(); if (workspacePaths.length) { this.logFullAnalysisIsTriggered(manual); - void this.startOssAnalysis(manual, false); } } catch (err) { await ErrorHandler.handleGlobal(err, Logger, this.contextService, this.loadingBadge); @@ -48,11 +44,8 @@ export default class SnykLib extends BaseSnykModule implements ISnykLib { // This function is called by commands, error handlers, etc. // We should avoid having duplicate parallel executions. - // Only starts OSS scan. Code & IaC scans are managed by LS public runScan = _.debounce(this.runFullScan_.bind(this), DEFAULT_SCAN_DEBOUNCE_INTERVAL, { leading: true }); - public runOssScan = _.debounce(this.startOssAnalysis.bind(this), OSS_SCAN_DEBOUNCE_INTERVAL, { leading: true }); - async enableCode(): Promise { Logger.info('Enabling Snyk Code'); const wasEnabled = await this.codeSettings.enable(); @@ -66,12 +59,6 @@ export default class SnykLib extends BaseSnykModule implements ISnykLib { } } - onDidChangeOssTreeVisibility(visible: boolean): void { - if (this.ossService) { - this.ossService.setVulnerabilityTreeVisibility(visible); - } - } - async checkAdvancedMode(): Promise { await this.contextService.setContext(SNYK_CONTEXT.ADVANCED, configuration.shouldShowAdvancedView); } @@ -81,25 +68,6 @@ export default class SnykLib extends BaseSnykModule implements ISnykLib { await this.contextService.setContext(SNYK_CONTEXT.WORKSPACE_FOUND, workspaceFound); } - private async startOssAnalysis(manual = false, reportTriggeredEvent = true): Promise { - if (!configuration.getFeaturesConfiguration()?.ossEnabled) return; - if (!this.ossService) throw new Error('OSS service is not initialized.'); - - // wait until Snyk Language Server is downloaded - await firstValueFrom(this.downloadService.downloadReady$); - - try { - const result = await this.ossService.test(manual, reportTriggeredEvent); - - if (result instanceof CliError || !result) { - return; - } - } catch (err) { - // catch unhandled error cases by reporting test failure - this.ossService.finalizeTest(new CliError(err)); - } - } - private isSnykCodeAutoscanSuspended(manual: boolean) { return !manual && !this.scanModeService.isCodeAutoScanAllowed(); } diff --git a/src/snyk/common/commands/commandController.ts b/src/snyk/common/commands/commandController.ts index f3c2642c1..60d9c1b24 100644 --- a/src/snyk/common/commands/commandController.ts +++ b/src/snyk/common/commands/commandController.ts @@ -7,7 +7,7 @@ import { createDCIgnore } from '../../snykCode/utils/ignoreFileUtils'; import { IssueUtils } from '../../snykCode/utils/issueUtils'; import { CodeIssueCommandArg } from '../../snykCode/views/interfaces'; import { IacIssueCommandArg } from '../../snykIac/views/interfaces'; -import { OssServiceLanguageServer } from '../../snykOss/ossServiceLanguageServer'; +import { OssService } from '../../snykOss/ossService'; import { IAnalytics } from '../analytics/itly'; import { SNYK_INITIATE_LOGIN_COMMAND, @@ -39,7 +39,7 @@ export class CommandController { private authService: IAuthenticationService, private snykCode: IProductService, private iacService: IProductService, - private ossService: OssServiceLanguageServer, + private ossService: OssService, private scanModeService: ScanModeService, private workspace: IVSCodeWorkspace, private commands: IVSCodeCommands, diff --git a/src/snyk/common/commands/types.ts b/src/snyk/common/commands/types.ts index e2638c20f..7e59c0206 100644 --- a/src/snyk/common/commands/types.ts +++ b/src/snyk/common/commands/types.ts @@ -2,7 +2,6 @@ import { completeFileSuggestionType } from '../../snykCode/interfaces'; import { CodeIssueCommandArg } from '../../snykCode/views/interfaces'; import { IacIssueCommandArg } from '../../snykIac/views/interfaces'; import { OssIssueCommandArgLanguageServer } from '../../snykOss/interfaces'; -import { OssIssueCommandArg } from '../../snykOss/views/ossVulnerabilityTreeProvider'; import { CodeIssueData, Issue } from '../languageServer/types'; export enum OpenCommandIssueType { @@ -12,20 +11,13 @@ export enum OpenCommandIssueType { } export type OpenIssueCommandArg = { - issue: CodeIssueCommandArg | OssIssueCommandArg | IacIssueCommandArg | OssIssueCommandArgLanguageServer; + issue: CodeIssueCommandArg | IacIssueCommandArg | OssIssueCommandArgLanguageServer; issueType: OpenCommandIssueType; }; export const isCodeIssue = ( - _issue: completeFileSuggestionType | Issue | OssIssueCommandArg | OssIssueCommandArgLanguageServer, + _issue: completeFileSuggestionType | Issue | OssIssueCommandArgLanguageServer, issueType: OpenCommandIssueType, ): _issue is Issue => { return issueType === OpenCommandIssueType.CodeIssue; }; - -export const isOssIssue = ( - _issue: completeFileSuggestionType | Issue | OssIssueCommandArg | OssIssueCommandArgLanguageServer, - issueType: OpenCommandIssueType, -): _issue is OssIssueCommandArg => { - return issueType === OpenCommandIssueType.OssVulnerability; -}; diff --git a/src/snyk/common/constants/general.ts b/src/snyk/common/constants/general.ts index d6e685c4e..17ac8fe7a 100644 --- a/src/snyk/common/constants/general.ts +++ b/src/snyk/common/constants/general.ts @@ -10,7 +10,6 @@ export const IDE_NAME_SHORT = 'vscode'; export const COMMAND_DEBOUNCE_INTERVAL = 200; // 200 milliseconds export const DEFAULT_SCAN_DEBOUNCE_INTERVAL = 1000; // 1 second export const DEFAULT_LS_DEBOUNCE_INTERVAL = 1000; // 1 second -export const OSS_SCAN_DEBOUNCE_INTERVAL = 10000; // 10 seconds export const EXECUTION_THROTTLING_INTERVAL = 1000 * 10; // * 60 * 30; // 30 minutes export const EXECUTION_PAUSE_INTERVAL = 1000 * 60 * 30; // 30 minutes export const REFRESH_VIEW_DEBOUNCE_INTERVAL = 200; // 200 milliseconds diff --git a/src/snyk/common/constants/views.ts b/src/snyk/common/constants/views.ts index 1b4317ba5..6616a1ef5 100644 --- a/src/snyk/common/constants/views.ts +++ b/src/snyk/common/constants/views.ts @@ -4,11 +4,9 @@ export const SNYK_VIEW_ANALYSIS_CODE_ENABLEMENT = 'snyk.views.analysis.code.enab export const SNYK_VIEW_ANALYSIS_CODE_SECURITY = 'snyk.views.analysis.code.security'; export const SNYK_VIEW_ANALYSIS_CODE_QUALITY = 'snyk.views.analysis.code.quality'; export const SNYK_VIEW_ANALYSIS_OSS = 'snyk.views.analysis.oss'; -export const SNYK_VIEW_ANALYSIS_OSS_LANGUAGE_SERVER = 'snyk.views.analysis.oss.languageServer'; export const SNYK_VIEW_SUPPORT = 'snyk.views.support'; export const SNYK_VIEW_SUGGESTION_CODE = 'snyk.views.suggestion.code'; export const SNYK_VIEW_SUGGESTION_OSS = 'snyk.views.suggestion.oss'; -export const SNYK_VIEW_SUGGESTION_OSS_LANGUAGE_SERVER = 'snyk.views.suggestion.oss.languageServer'; export const SNYK_VIEW_SUGGESTION_IAC = 'snyk.views.suggestion.iac'; export const SNYK_VIEW_ANALYSIS_IAC = 'snyk.views.analysis.configuration'; diff --git a/src/snyk/common/services/learnService.ts b/src/snyk/common/services/learnService.ts index df3bd9964..fde6fd070 100644 --- a/src/snyk/common/services/learnService.ts +++ b/src/snyk/common/services/learnService.ts @@ -1,4 +1,3 @@ -import { OssIssueCommandArg } from '../../snykOss/views/ossVulnerabilityTreeProvider'; import { SNYK_GET_LESSON_COMMAND } from '../constants/commands'; import { CodeIssueData, Issue } from '../languageServer/types'; import { IVSCodeCommands } from '../vscode/commands'; @@ -11,28 +10,6 @@ export type Lesson = { export class LearnService { constructor(private commandExecutor: IVSCodeCommands) {} - async getOssLesson(vulnerability: OssIssueCommandArg): Promise { - const cwe = vulnerability.identifiers?.CWE; - let cweElement = ''; - if (cwe && cwe.length > 0) { - cweElement = cwe[0]; - } - - const cve = vulnerability.identifiers?.CWE; - let cveElement = ''; - if (cve && cve.length > 0) { - cveElement = cve[0]; - } - return this.commandExecutor.executeCommand( - SNYK_GET_LESSON_COMMAND, - vulnerability.id, - vulnerability.packageManager, - cweElement, - cveElement, - 4, - ); - } - async getCodeLesson(issue: Issue): Promise { const ruleSplit = issue.additionalData.ruleId.split('/'); const rule = ruleSplit[ruleSplit.length - 1]; diff --git a/src/snyk/extension.ts b/src/snyk/extension.ts index 8bc849a01..c3608e68f 100644 --- a/src/snyk/extension.ts +++ b/src/snyk/extension.ts @@ -36,7 +36,6 @@ import { SNYK_VIEW_ANALYSIS_CODE_SECURITY, SNYK_VIEW_ANALYSIS_IAC, SNYK_VIEW_ANALYSIS_OSS, - SNYK_VIEW_ANALYSIS_OSS_LANGUAGE_SERVER, SNYK_VIEW_SUPPORT, SNYK_VIEW_WELCOME, } from './common/constants/views'; @@ -75,15 +74,11 @@ import { IacService } from './snykIac/iacService'; import IacIssueTreeProvider from './snykIac/views/iacIssueTreeProvider'; import { IacSuggestionWebviewProvider } from './snykIac/views/suggestion/iacSuggestionWebviewProvider'; import { EditorDecorator } from './snykOss/editor/editorDecorator'; -import { OssServiceLanguageServer } from './snykOss/ossServiceLanguageServer'; +import { OssService } from './snykOss/ossService'; import { OssDetailPanelProvider } from './snykOss/providers/ossDetailPanelProvider'; import OssIssueTreeProvider from './snykOss/providers/ossVulnerabilityTreeProvider'; -import { OssService } from './snykOss/services/ossService'; -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'; +import { ModuleVulnerabilityCountProvider } from './snykOss/providers/vulnerabilityCountProvider'; +import { OssVulnerabilityCountService } from './snykOss/services/vulnerabilityCount/ossVulnerabilityCountService'; class SnykExtension extends SnykLib implements IExtension { public async activate(vscodeContext: vscode.ExtensionContext): Promise { @@ -212,21 +207,6 @@ class SnykExtension extends SnykLib implements IExtension { this.analytics, ); - this.ossService = new OssService( - this.context, - Logger, - configuration, - new OssSuggestionWebviewProvider(this.context, vsCodeWindow, Logger, this.learnService), - vsCodeWorkspace, - this.viewManagerService, - this.downloadService, - new DailyScanJob(this), - this.notificationService, - this.analytics, - this.languageServer, - this.workspaceTrust, - ); - const ossSuggestionProvider = new OssDetailPanelProvider( vsCodeWindow, extensionContext, @@ -235,7 +215,7 @@ class SnykExtension extends SnykLib implements IExtension { vsCodeWorkspace, ); - this.ossServiceLanguageServer = new OssServiceLanguageServer( + this.ossService = new OssService( extensionContext, configuration, ossSuggestionProvider, @@ -276,7 +256,7 @@ class SnykExtension extends SnykLib implements IExtension { this.authService, this.snykCode, this.iacService, - this.ossServiceLanguageServer, + this.ossService, this.scanModeService, vsCodeWorkspace, vsCodeCommands, @@ -317,17 +297,7 @@ class SnykExtension extends SnykLib implements IExtension { codeQualityTree, ); - const ossVulnerabilityProvider = new OssVulnerabilityTreeProvider( - this.viewManagerService, - this.contextService, - this.ossService, - configuration, - ); - - vscodeContext.subscriptions.push( - vscode.window.registerTreeDataProvider(SNYK_VIEW_ANALYSIS_OSS, ossVulnerabilityProvider), - vscode.window.registerTreeDataProvider(SNYK_VIEW_SUPPORT, new SupportProvider()), - ); + vscodeContext.subscriptions.push(vscode.window.registerTreeDataProvider(SNYK_VIEW_SUPPORT, new SupportProvider())); const welcomeTree = vscode.window.createTreeView(SNYK_VIEW_WELCOME, { treeDataProvider: new EmptyTreeDataProvider(), @@ -336,12 +306,7 @@ class SnykExtension extends SnykLib implements IExtension { treeDataProvider: new EmptyTreeDataProvider(), }); - const ossTree = vscode.window.createTreeView(SNYK_VIEW_ANALYSIS_OSS, { - treeDataProvider: ossVulnerabilityProvider, - }); - vscodeContext.subscriptions.push( - ossTree.onDidChangeVisibility(e => this.onDidChangeOssTreeVisibility(e.visible)), welcomeTree.onDidChangeVisibility(e => this.onDidChangeWelcomeViewVisibility(e.visible)), codeEnablementTree, ); @@ -349,17 +314,17 @@ class SnykExtension extends SnykLib implements IExtension { const ossIssueProvider = new OssIssueTreeProvider( this.viewManagerService, this.contextService, - this.ossServiceLanguageServer, + this.ossService, configuration, vsCodeLanguages, ); - const ossSecurityTree = vscode.window.createTreeView(SNYK_VIEW_ANALYSIS_OSS_LANGUAGE_SERVER, { + const ossSecurityTree = vscode.window.createTreeView(SNYK_VIEW_ANALYSIS_OSS, { treeDataProvider: ossIssueProvider, }); vscodeContext.subscriptions.push( - vscode.window.registerTreeDataProvider(SNYK_VIEW_ANALYSIS_OSS_LANGUAGE_SERVER, ossIssueProvider), + vscode.window.registerTreeDataProvider(SNYK_VIEW_ANALYSIS_OSS, ossIssueProvider), ossSecurityTree, ); @@ -395,10 +360,8 @@ class SnykExtension extends SnykLib implements IExtension { this.editorsWatcher.activate(this); this.configurationWatcher.activate(this); this.snykCode.activateWebviewProviders(); - this.ossService.activateSuggestionProvider(); - this.ossService.activateManifestFileWatcher(this); this.iacService.activateWebviewProviders(); - this.ossServiceLanguageServer.activateWebviewProviders(); + this.ossService.activateWebviewProviders(); // noinspection ES6MissingAwait void this.notificationService.init(); @@ -412,23 +375,23 @@ class SnykExtension extends SnykLib implements IExtension { this.initDependencyDownload(); - this.ossVulnerabilityCountServiceLanguageServer = new OssVulnerabilityCountServiceLS( + this.ossVulnerabilityCountService = new OssVulnerabilityCountService( vsCodeWorkspace, vsCodeWindow, vsCodeLanguages, - new ModuleVulnerabilityCountProviderLS( - this.ossServiceLanguageServer, + new ModuleVulnerabilityCountProvider( + this.ossService, languageClientAdapter, new UriAdapter(), new TextDocumentAdapter(), ), - this.ossServiceLanguageServer, + this.ossService, Logger, new EditorDecorator(vsCodeWindow, vsCodeLanguages, new ThemeColorAdapter()), this.analytics, configuration, ); - this.ossVulnerabilityCountServiceLanguageServer.activate(); + this.ossVulnerabilityCountService.activate(); this.advisorScoreDisposable = new AdvisorService( vsCodeWindow, @@ -458,7 +421,7 @@ class SnykExtension extends SnykLib implements IExtension { } public async deactivate(): Promise { - this.ossVulnerabilityCountServiceLanguageServer.dispose(); + this.ossVulnerabilityCountService.dispose(); await this.languageServer.stop(); await this.analytics.flush(); await ErrorReporter.flush(); @@ -481,7 +444,6 @@ class SnykExtension extends SnykLib implements IExtension { private initDependencyDownload(): DownloadService { this.downloadService.downloadOrUpdate().catch(err => { Logger.error(`${messages.lsDownloadFailed} ${ErrorHandler.stringifyError(err)}`); - this.ossService?.handleLsDownloadFailure(); }); return this.downloadService; @@ -502,8 +464,6 @@ class SnykExtension extends SnykLib implements IExtension { ), vscode.commands.registerCommand(SNYK_START_COMMAND, async () => { await vscode.commands.executeCommand(SNYK_WORKSPACE_SCAN_COMMAND); - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - await this.commandController.executeCommand(SNYK_START_COMMAND, () => this.runScan(true)); // todo: remove once OSS scans replaced with LS }), vscode.commands.registerCommand(SNYK_SETTINGS_COMMAND, () => this.commandController.openSettings()), vscode.commands.registerCommand(SNYK_DCIGNORE_COMMAND, (custom: boolean, path?: string) => diff --git a/src/snyk/snykOss/messages.ts b/src/snyk/snykOss/constants/messages.ts similarity index 96% rename from src/snyk/snykOss/messages.ts rename to src/snyk/snykOss/constants/messages.ts index 1cbbec292..2537ade08 100644 --- a/src/snyk/snykOss/messages.ts +++ b/src/snyk/snykOss/constants/messages.ts @@ -1,4 +1,4 @@ -import { ModuleVulnerabilityCount } from './services/vulnerabilityCount/importedModule'; +import { ModuleVulnerabilityCount } from '../services/vulnerabilityCount/importedModule'; export const messages = { analysis: { diff --git a/src/snyk/snykOss/editor/editorDecorator.ts b/src/snyk/snykOss/editor/editorDecorator.ts index 398a5d888..0f5b26be8 100644 --- a/src/snyk/snykOss/editor/editorDecorator.ts +++ b/src/snyk/snykOss/editor/editorDecorator.ts @@ -4,7 +4,7 @@ import { IVSCodeLanguages } from '../../common/vscode/languages'; import { IThemeColorAdapter } from '../../common/vscode/theme'; import { TextEditorDecorationType } from '../../common/vscode/types'; import { IVSCodeWindow } from '../../common/vscode/window'; -import { messages } from '../messages/vulnerabilityCount'; +import { messages } from '../constants/messages'; import { ImportedModule, ModuleVulnerabilityCount } from '../services/vulnerabilityCount/importedModule'; export class EditorDecorator { @@ -58,7 +58,7 @@ export class EditorDecorator { module.line - 1, this.editorLastCharacterIndex, ), - renderOptions: getRenderOptions(messages.fetchingVulnerabilities, this.themeColorAdapter), + renderOptions: getRenderOptions(messages.vulnerabilityCount.fetchingVulnerabilities, this.themeColorAdapter), }; } @@ -92,7 +92,7 @@ export class EditorDecorator { this.fileDecorationMap.set(filePath, lineDecorations); // set map, if no decoration was set before } - const text = vulnerabilityCount.count ? messages.decoratorMessage(vulnerabilityCount.count) : ''; + const text = vulnerabilityCount.count ? messages.vulnerabilityCount.decoratorMessage(vulnerabilityCount.count) : ''; lineDecorations[vulnerabilityCount.line] = { range: this.languages.createRange( diff --git a/src/snyk/snykOss/interfaces.ts b/src/snyk/snykOss/interfaces.ts index c206b56a1..907ba23a0 100644 --- a/src/snyk/snykOss/interfaces.ts +++ b/src/snyk/snykOss/interfaces.ts @@ -1,6 +1,7 @@ -import * as vscode from 'vscode'; -import { Issue, OssIssueData } from '../common/languageServer/types'; +import _ from 'lodash'; +import { Issue, IssueSeverity, OssIssueData } from '../common/languageServer/types'; import { IWebViewProvider } from '../common/views/webviewProvider'; +import { CliError } from '../cli/services/cliService'; export interface IOssSuggestionWebviewProvider extends IWebViewProvider> { openIssueId: string | undefined; @@ -10,3 +11,103 @@ export type OssIssueCommandArgLanguageServer = Issue & { matchingIdVulnerabilities: Issue[]; overviewHtml: string; }; + +export type OssResult = OssFileResult[] | OssFileResult; + +export type OssFileResult = OssResultBody | CliError; + +export type OssResultBody = { + vulnerabilities: OssVulnerability[]; + projectName: string; + displayTargetFile: string; + packageManager: string; + path: string; +}; + +export type OssVulnerability = { + id: string; + license?: string; + identifiers?: Identifiers; + title: string; + description: string; + language: string; + packageManager: string; + packageName: string; + severity: OssSeverity; + name: string; + version: string; + exploit?: string; + + CVSSv3?: string; + cvssScore?: string; + + fixedIn?: Array; + from: Array; + upgradePath: Array; + isPatchable: boolean; + isUpgradable: boolean; +}; + +export type Identifiers = { + CWE: string[]; + CVE: string[]; +}; + +export enum OssSeverity { + Low = 'low', + Medium = 'medium', + High = 'high', + Critical = 'critical', +} + +export function capitalizeOssSeverity(ossSeverity: OssSeverity): Capitalize { + return _.capitalize(ossSeverity) as Capitalize; +} + +export function isResultCliError(fileResult: OssFileResult): fileResult is CliError { + return (fileResult as CliError).error !== undefined; +} + +export function convertSeverity(severity: IssueSeverity): OssSeverity { + switch (severity) { + case IssueSeverity.Low: + return OssSeverity.Low; + case IssueSeverity.Medium: + return OssSeverity.Medium; + case IssueSeverity.High: + return OssSeverity.High; + default: + return OssSeverity.Critical; + } +} + +export function convertIssue(issue: Issue): OssVulnerability { + const tempVuln: OssVulnerability = { + id: issue.id, + identifiers: issue.additionalData.identifiers, + title: issue.title, + description: issue.additionalData.description, + language: issue.additionalData.language, + packageManager: issue.additionalData.packageManager, + packageName: issue.additionalData.packageName, + severity: convertSeverity(issue.severity), + name: issue.additionalData.name, + version: issue.additionalData.version, + exploit: issue.additionalData.exploit, + + CVSSv3: issue.additionalData.CVSSv3, + cvssScore: issue.additionalData.cvssScore, + + fixedIn: issue.additionalData.fixedIn === undefined ? [] : issue.additionalData.fixedIn, + from: issue.additionalData.from, + upgradePath: issue.additionalData.upgradePath, + isPatchable: issue.additionalData.isPatchable, + isUpgradable: issue.additionalData.isUpgradable, + }; + + if (issue.additionalData.license !== undefined) { + tempVuln.license = issue.additionalData.license; + } + + return tempVuln; +} diff --git a/src/snyk/snykOss/messages/error.ts b/src/snyk/snykOss/messages/error.ts deleted file mode 100644 index f8f215e17..000000000 --- a/src/snyk/snykOss/messages/error.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const messages = { - suggestionViewShowFailed: 'Failed to show Snyk OSS suggestion view', -}; diff --git a/src/snyk/snykOss/messages/test.ts b/src/snyk/snykOss/messages/test.ts deleted file mode 100644 index d41d6d05e..000000000 --- a/src/snyk/snykOss/messages/test.ts +++ /dev/null @@ -1,9 +0,0 @@ -export const messages = { - testFailed: 'Open Source Security test failed.', - testStarted: 'Open Source Security test started.', - viewResults: 'View results', - hide: "Don't show again", - - testFailedForPath: (path: string): string => `Open Source Security test failed for "${path}".`, - testFinished: (projectName: string): string => `Open Source Security test finished for "${projectName}".`, -}; diff --git a/src/snyk/snykOss/messages/treeView.ts b/src/snyk/snykOss/messages/treeView.ts deleted file mode 100644 index e01a041bc..000000000 --- a/src/snyk/snykOss/messages/treeView.ts +++ /dev/null @@ -1,11 +0,0 @@ -export const messages = { - cookingDependencies: 'Scanning...', - - runTest: 'Run scan for Open Source security vulnerabilities.', - noVulnerabilitiesFound: ' ✅ Congrats! Snyk found no vulnerabilities.', - singleVulnerabilityFound: 'Snyk found 1 vulnerability', - vulnerability: 'vulnerability', - vulnerabilities: 'vulnerabilities', - - multipleVulnerabilitiesFound: (issueCount: number): string => `Snyk found ${issueCount} vulnerabilities`, -}; diff --git a/src/snyk/snykOss/messages/vulnerabilityCount.ts b/src/snyk/snykOss/messages/vulnerabilityCount.ts deleted file mode 100644 index f328ebd22..000000000 --- a/src/snyk/snykOss/messages/vulnerabilityCount.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { ModuleVulnerabilityCount } from '../services/vulnerabilityCount/importedModule'; - -export const messages = { - fetchingVulnerabilities: 'Fetching vulnerabilities...', - vulnerability: 'vulnerability', - vulnerabilities: 'vulnerabilities', - showMostSevereVulnerability: 'Show the most severe vulnerability (Snyk)', - - decoratorMessage: (vulnerabilityCount: string): string => { - const vulnerabilityCountNumber = Number.parseInt(vulnerabilityCount, 10); - if (isNaN(vulnerabilityCountNumber)) { - return vulnerabilityCount; - } - return `${vulnerabilityCountNumber} ${vulnerabilityCountNumber > 1 ? 'vulnerabilities' : 'vulnerability'}`; - }, - - diagnosticMessagePrefix: (module: ModuleVulnerabilityCount): string => - `Dependency ${module.name}${module.version ? `@${module.version}` : ''} has `, -}; diff --git a/src/snyk/snykOss/ossResult.ts b/src/snyk/snykOss/ossResult.ts deleted file mode 100644 index aabe8b873..000000000 --- a/src/snyk/snykOss/ossResult.ts +++ /dev/null @@ -1,103 +0,0 @@ -import _ from 'lodash'; -import { CliError } from '../cli/services/cliService'; -import { Issue, IssueSeverity, OssIssueData } from '../common/languageServer/types'; - -export type OssResult = OssFileResult[] | OssFileResult; - -export type OssFileResult = OssResultBody | CliError; - -export type OssResultBody = { - vulnerabilities: OssVulnerability[]; - projectName: string; - displayTargetFile: string; - packageManager: string; - path: string; -}; - -export type OssVulnerability = { - id: string; - license?: string; - identifiers?: Identifiers; - title: string; - description: string; - language: string; - packageManager: string; - packageName: string; - severity: OssSeverity; - name: string; - version: string; - exploit?: string; - - CVSSv3?: string; - cvssScore?: string; - - fixedIn?: Array; - from: Array; - upgradePath: Array; - isPatchable: boolean; - isUpgradable: boolean; -}; - -export type Identifiers = { - CWE: string[]; - CVE: string[]; -}; - -export enum OssSeverity { - Low = 'low', - Medium = 'medium', - High = 'high', - Critical = 'critical', -} - -export function capitalizeOssSeverity(ossSeverity: OssSeverity): Capitalize { - return _.capitalize(ossSeverity) as Capitalize; -} - -export function isResultCliError(fileResult: OssFileResult): fileResult is CliError { - return (fileResult as CliError).error !== undefined; -} - -export function convertSeverity(severity: IssueSeverity): OssSeverity { - switch (severity) { - case IssueSeverity.Low: - return OssSeverity.Low; - case IssueSeverity.Medium: - return OssSeverity.Medium; - case IssueSeverity.High: - return OssSeverity.High; - default: - return OssSeverity.Critical; - } -} - -export function convertIssue(issue: Issue): OssVulnerability { - const tempVuln: OssVulnerability = { - id: issue.id, - identifiers: issue.additionalData.identifiers, - title: issue.title, - description: issue.additionalData.description, - language: issue.additionalData.language, - packageManager: issue.additionalData.packageManager, - packageName: issue.additionalData.packageName, - severity: convertSeverity(issue.severity), - name: issue.additionalData.name, - version: issue.additionalData.version, - exploit: issue.additionalData.exploit, - - CVSSv3: issue.additionalData.CVSSv3, - cvssScore: issue.additionalData.cvssScore, - - fixedIn: issue.additionalData.fixedIn === undefined ? [] : issue.additionalData.fixedIn, - from: issue.additionalData.from, - upgradePath: issue.additionalData.upgradePath, - isPatchable: issue.additionalData.isPatchable, - isUpgradable: issue.additionalData.isUpgradable, - }; - - if (issue.additionalData.license !== undefined) { - tempVuln.license = issue.additionalData.license; - } - - return tempVuln; -} diff --git a/src/snyk/snykOss/ossServiceLanguageServer.ts b/src/snyk/snykOss/ossService.ts similarity index 95% rename from src/snyk/snykOss/ossServiceLanguageServer.ts rename to src/snyk/snykOss/ossService.ts index e1d5f0610..b2fa759bb 100644 --- a/src/snyk/snykOss/ossServiceLanguageServer.ts +++ b/src/snyk/snykOss/ossService.ts @@ -12,7 +12,7 @@ import { IVSCodeLanguages } from '../common/vscode/languages'; import { IVSCodeWorkspace } from '../common/vscode/workspace'; import { IOssSuggestionWebviewProvider } from './interfaces'; -export class OssServiceLanguageServer extends ProductService { +export class OssService extends ProductService { constructor( extensionContext: ExtensionContext, config: IConfiguration, diff --git a/src/snyk/snykOss/providers/ossDetailPanelProvider.ts b/src/snyk/snykOss/providers/ossDetailPanelProvider.ts index 75661b088..1b5946a06 100644 --- a/src/snyk/snykOss/providers/ossDetailPanelProvider.ts +++ b/src/snyk/snykOss/providers/ossDetailPanelProvider.ts @@ -1,5 +1,5 @@ import * as vscode from 'vscode'; -import { SNYK_VIEW_SUGGESTION_OSS_LANGUAGE_SERVER } from '../../common/constants/views'; +import { SNYK_VIEW_SUGGESTION_OSS } from '../../common/constants/views'; import { ErrorHandler } from '../../common/error/errorHandler'; import { Issue, OssIssueData } from '../../common/languageServer/types'; import { ILog } from '../../common/logger/interfaces'; @@ -10,7 +10,7 @@ import { ExtensionContext } from '../../common/vscode/extensionContext'; import { IVSCodeLanguages } from '../../common/vscode/languages'; import { IVSCodeWindow } from '../../common/vscode/window'; import { IVSCodeWorkspace } from '../../common/vscode/workspace'; -import { messages as errorMessages } from '../messages/error'; +import { messages } from '../constants/messages'; export class OssDetailPanelProvider extends WebviewProvider> @@ -36,10 +36,7 @@ export class OssDetailPanelProvider activate(): void { this.context.addDisposables( - this.window.registerWebviewPanelSerializer( - SNYK_VIEW_SUGGESTION_OSS_LANGUAGE_SERVER, - new WebviewPanelSerializer(this), - ), + this.window.registerWebviewPanelSerializer(SNYK_VIEW_SUGGESTION_OSS, new WebviewPanelSerializer(this)), ); } @@ -55,7 +52,7 @@ export class OssDetailPanelProvider this.panel.reveal(vscode.ViewColumn.Two, true); } else { this.panel = vscode.window.createWebviewPanel( - SNYK_VIEW_SUGGESTION_OSS_LANGUAGE_SERVER, + SNYK_VIEW_SUGGESTION_OSS, 'Snyk OSS Vulnerability', { viewColumn: vscode.ViewColumn.Two, @@ -103,7 +100,7 @@ export class OssDetailPanelProvider this.issue = issue; } catch (e) { - ErrorHandler.handle(e, this.logger, errorMessages.suggestionViewShowFailed); + ErrorHandler.handle(e, this.logger, messages.errors.suggestionViewShowFailed); } } diff --git a/src/snyk/snykOss/providers/ossVulnerabilityTreeProvider.ts b/src/snyk/snykOss/providers/ossVulnerabilityTreeProvider.ts index d64997b34..f89fd927a 100644 --- a/src/snyk/snykOss/providers/ossVulnerabilityTreeProvider.ts +++ b/src/snyk/snykOss/providers/ossVulnerabilityTreeProvider.ts @@ -13,8 +13,8 @@ import { IViewManagerService } from '../../common/services/viewManagerService'; import { ProductIssueTreeProvider } from '../../common/views/issueTreeProvider'; import { TreeNode } from '../../common/views/treeNode'; import { IVSCodeLanguages } from '../../common/vscode/languages'; +import { messages } from '../constants/messages'; import { OssIssueCommandArgLanguageServer } from '../interfaces'; -import { messages } from '../messages'; export default class OssIssueTreeProvider extends ProductIssueTreeProvider { constructor( diff --git a/src/snyk/snykOss/services/vulnerabilityCount/vulnerabilityCountProviderLS.ts b/src/snyk/snykOss/providers/vulnerabilityCountProvider.ts similarity index 89% rename from src/snyk/snykOss/services/vulnerabilityCount/vulnerabilityCountProviderLS.ts rename to src/snyk/snykOss/providers/vulnerabilityCountProvider.ts index 01729c876..29b3ab467 100644 --- a/src/snyk/snykOss/services/vulnerabilityCount/vulnerabilityCountProviderLS.ts +++ b/src/snyk/snykOss/providers/vulnerabilityCountProvider.ts @@ -1,16 +1,20 @@ -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, OssVulnerability, convertIssue, isResultCliError } from '../../ossResult'; -import { OssServiceLanguageServer } from '../../ossServiceLanguageServer'; -import { ImportedModule, ModuleVulnerabilityCount, SeverityCounts } from './importedModule'; - -export class ModuleVulnerabilityCountProviderLS { +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 { convertIssue, isResultCliError, OssFileResult, OssResultBody, OssVulnerability } from '../interfaces'; +import { OssService } from '../ossService'; +import { + ImportedModule, + ModuleVulnerabilityCount, + SeverityCounts, +} from '../services/vulnerabilityCount/importedModule'; + +export class ModuleVulnerabilityCountProvider { constructor( - private readonly ossService: OssServiceLanguageServer, + private readonly ossService: OssService, private readonly lca: ILanguageClientAdapter, private readonly uriAdapter: IUriAdapter, private readonly textDocumentAdapter: ITextDocumentAdapter, diff --git a/src/snyk/snykOss/services/ossService.ts b/src/snyk/snykOss/services/ossService.ts deleted file mode 100644 index 547785b01..000000000 --- a/src/snyk/snykOss/services/ossService.ts +++ /dev/null @@ -1,224 +0,0 @@ -import * as marked from 'marked'; -import { Subject } from 'rxjs'; -import { IExtension } from '../../base/modules/interfaces'; -import { CliError, CliService } from '../../cli/services/cliService'; -import { IAnalytics } from '../../common/analytics/itly'; -import { IConfiguration } from '../../common/configuration/configuration'; -import { IWorkspaceTrust } from '../../common/configuration/trustedFolders'; -import { IDE_NAME } from '../../common/constants/general'; -import { ILanguageServer } from '../../common/languageServer/languageServer'; -import { ILog } from '../../common/logger/interfaces'; -import { DownloadService } from '../../common/services/downloadService'; -import { INotificationService } from '../../common/services/notificationService'; -import { IViewManagerService } from '../../common/services/viewManagerService'; -import { IWebViewProvider } from '../../common/views/webviewProvider'; -import { ExtensionContext } from '../../common/vscode/extensionContext'; -import { IVSCodeWorkspace } from '../../common/vscode/workspace'; -import { messages } from '../messages/test'; -import { OssFileResult, OssResult, OssSeverity, OssVulnerability, isResultCliError } from '../ossResult'; -import { OssIssueCommandArg } from '../views/ossVulnerabilityTreeProvider'; -import { DailyScanJob } from '../watchers/dailyScanJob'; -import createManifestFileWatcher from '../watchers/manifestFileWatcher'; - -export class OssService extends CliService { - protected readonly command: string[] = ['test']; - - private isVulnerabilityTreeVisible = false; - - readonly scanFinished$ = new Subject(); - - constructor( - protected readonly extensionContext: ExtensionContext, - protected readonly logger: ILog, - protected readonly config: IConfiguration, - private readonly suggestionProvider: IWebViewProvider, - protected readonly workspace: IVSCodeWorkspace, - private readonly viewManagerService: IViewManagerService, - protected readonly downloadService: DownloadService, - private readonly dailyScanJob: DailyScanJob, - private readonly notificationService: INotificationService, - private readonly analytics: IAnalytics, - protected readonly languageServer: ILanguageServer, - protected readonly workspaceTrust: IWorkspaceTrust, - ) { - super(extensionContext, logger, config, workspace, downloadService, languageServer, workspaceTrust); - } - - public getResultArray = (): ReadonlyArray | undefined => { - if (!this.result) { - return undefined; - } - - return Array.isArray(this.result) ? this.result : [this.result]; - }; - - protected mapToResultType(rawCliResult: string): OssResult { - if (rawCliResult.length == 0) { - throw new Error('CLI returned empty output result.'); - } - - let result: OssResult; - try { - result = JSON.parse(rawCliResult) as OssResult; - } catch (err) { - throw new Error(`Failed to parse JSON result. Unparsed: ${rawCliResult}`); - } - - return result; - } - - protected ensureDependencies(): void { - this.viewManagerService.refreshOssView(); - this.logger.info('Waiting for Open Source scan CLI readiness'); - } - - protected beforeTest(manualTrigger: boolean, reportTriggeredEvent: boolean): void { - this.logger.info(messages.testStarted); - this.viewManagerService.refreshOssView(); - - if (reportTriggeredEvent) { - this.analytics.logAnalysisIsTriggered({ - analysisType: ['Snyk Open Source'], - ide: IDE_NAME, - triggeredByUser: manualTrigger, - }); - } - } - - protected afterTest(result: OssResult | CliError): void { - if (result instanceof CliError) { - this.logger.error(`${messages.testFailed} ${result.error}`); - this.logAnalysisIsReady('Error'); - } else { - this.logOssResult(result); - - if (this.config.shouldAutoScanOss) { - this.dailyScanJob.schedule(); - } - } - - this.scanFinished$.next(); - this.viewManagerService.refreshOssView(); - } - - override handleLsDownloadFailure(): void { - super.handleLsDownloadFailure(); - this.viewManagerService.refreshOssView(); - } - - override handleNoTrustedFolders(): void { - super.handleNoTrustedFolders(); - this.viewManagerService.refreshOssView(); - } - - activateSuggestionProvider(): void { - this.suggestionProvider.activate(); - } - - showSuggestionProvider(vulnerability: OssIssueCommandArg): Promise { - return this.suggestionProvider.showPanel(vulnerability); - } - - activateManifestFileWatcher(extension: IExtension): void { - const manifestWatcher = createManifestFileWatcher(extension, this.workspace, this.config); - this.extensionContext.addDisposables(manifestWatcher); - } - - setVulnerabilityTreeVisibility(visible: boolean): void { - this.isVulnerabilityTreeVisible = visible; - } - - getUniqueVulnerabilities(vulnerabilities: OssVulnerability[]): OssVulnerability[] { - return vulnerabilities.filter((val, i, arr) => arr.findIndex(el => el.id === val.id) == i); - } - - getNewCriticalVulnerabilitiesCount(currentResult: OssResult, otherResult: OssResult): number { - if (Array.isArray(currentResult) && Array.isArray(otherResult)) { - let newVulnerabilityCount = 0; - for (let i = 0; i < otherResult.length; i++) { - newVulnerabilityCount += this.getNewCriticalVulnerabilitiesCount(currentResult[i], otherResult[i]); - } - - return newVulnerabilityCount; - } - - // if only one of results is an array, no count possible - if (Array.isArray(currentResult) || Array.isArray(otherResult)) { - throw new Error('Result types mismatch for new vulnerabilities calculation.'); - } - - if (!currentResult || isResultCliError(currentResult)) { - return 0; - } - - const currentVulnSet = this.getUniqueVulnerabilities(currentResult.vulnerabilities).filter( - v => v.severity === OssSeverity.Critical, - ); - - if (isResultCliError(otherResult)) { - return currentVulnSet.length; - } - - const otherVulnSet = this.getUniqueVulnerabilities(otherResult.vulnerabilities).filter( - v => v.severity === OssSeverity.Critical, - ); - - if (currentVulnSet.length > otherVulnSet.length) { - return currentVulnSet.length - otherVulnSet.length; - } - - return 0; - } - - 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, - }); - }); - }); - } - - private logOssResult(result: OssResult) { - const fileResults = Array.isArray(result) ? result : [result]; - - for (const fileResult of fileResults) { - if (isResultCliError(fileResult)) { - this.logger.error(this.getTestErrorMessage(fileResult)); - this.logAnalysisIsReady('Error'); - } else { - this.logger.info(messages.testFinished(fileResult.projectName)); - this.logAnalysisIsReady('Success'); - } - } - } - - private getTestErrorMessage(fileResult: CliError): string { - let errorMessage: string; - if (fileResult.path) { - errorMessage = `${messages.testFailedForPath(fileResult.path)} ${fileResult.error}`; - } else { - errorMessage = `${messages.testFailed} ${fileResult.error}`; - } - return errorMessage; - } - - private logAnalysisIsReady(result: 'Error' | 'Success'): void { - this.analytics.logAnalysisIsReady({ - ide: IDE_NAME, - analysisType: 'Snyk Open Source', - result, - }); - } -} diff --git a/src/snyk/snykOss/services/vulnerabilityCount/ossVulnerabilityCountServiceLS.ts b/src/snyk/snykOss/services/vulnerabilityCount/ossVulnerabilityCountService.ts similarity index 94% rename from src/snyk/snykOss/services/vulnerabilityCount/ossVulnerabilityCountServiceLS.ts rename to src/snyk/snykOss/services/vulnerabilityCount/ossVulnerabilityCountService.ts index 090021f9a..f41d519d8 100644 --- a/src/snyk/snykOss/services/vulnerabilityCount/ossVulnerabilityCountServiceLS.ts +++ b/src/snyk/snykOss/services/vulnerabilityCount/ossVulnerabilityCountService.ts @@ -10,12 +10,12 @@ 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 { messages } from '../../constants/messages'; import { EditorDecorator } from '../../editor/editorDecorator'; -import { messages } from '../../messages/vulnerabilityCount'; -import { OssServiceLanguageServer } from '../../ossServiceLanguageServer'; -import { VulnerabilityCountEmitter, VulnerabilityCountEvents } from '../../vulnerabilityCountEmitter'; +import { OssService } from '../../ossService'; +import { ModuleVulnerabilityCountProvider } from '../../providers/vulnerabilityCountProvider'; import { ImportedModule, ModuleVulnerabilityCount, ModuleVulnerabilityCountSeverity } from './importedModule'; -import { ModuleVulnerabilityCountProviderLS } from './vulnerabilityCountProviderLS'; +import { VulnerabilityCountEmitter, VulnerabilityCountEvents } from './vulnerabilityCountEmitter'; export enum SupportedLanguage { TypeScript, @@ -24,7 +24,7 @@ export enum SupportedLanguage { PJSON, } -export class OssVulnerabilityCountServiceLS implements Disposable { +export class OssVulnerabilityCountService implements Disposable { protected disposables: Disposable[] = []; protected ossScanFinishedSubscription: Subscription; @@ -35,8 +35,8 @@ export class OssVulnerabilityCountServiceLS implements Disposable { private readonly workspace: IVSCodeWorkspace, private readonly window: IVSCodeWindow, private readonly languages: IVSCodeLanguages, - private readonly vulnerabilityCountProvider: ModuleVulnerabilityCountProviderLS, - private readonly ossService: OssServiceLanguageServer, + private readonly vulnerabilityCountProvider: ModuleVulnerabilityCountProvider, + private readonly ossService: OssService, private readonly logger: ILog, private readonly editorDecorator: EditorDecorator, private readonly analytics: IAnalytics, @@ -222,7 +222,7 @@ export class OssVulnerabilityCountServiceLS implements Disposable { return ''; } - let message = messages.diagnosticMessagePrefix(module); + let message = messages.vulnerabilityCount.diagnosticMessagePrefix(module); message += this.getSeverityCountMessage( [ ModuleVulnerabilityCountSeverity.Critical, diff --git a/src/snyk/snykOss/vulnerabilityCountEmitter.ts b/src/snyk/snykOss/services/vulnerabilityCount/vulnerabilityCountEmitter.ts similarity index 88% rename from src/snyk/snykOss/vulnerabilityCountEmitter.ts rename to src/snyk/snykOss/services/vulnerabilityCount/vulnerabilityCountEmitter.ts index 4564f0742..dcde88813 100644 --- a/src/snyk/snykOss/vulnerabilityCountEmitter.ts +++ b/src/snyk/snykOss/services/vulnerabilityCount/vulnerabilityCountEmitter.ts @@ -1,5 +1,5 @@ import EventEmitter from 'events'; -import { ImportedModule, ModuleVulnerabilityCount } from './services/vulnerabilityCount/importedModule'; +import { ImportedModule, ModuleVulnerabilityCount } from './importedModule'; export enum VulnerabilityCountEvents { PackageJsonFound = 'packageJsonFound', diff --git a/src/snyk/snykOss/views/ossVulnerabilityTreeProvider.ts b/src/snyk/snykOss/views/ossVulnerabilityTreeProvider.ts deleted file mode 100644 index 17b9037b2..000000000 --- a/src/snyk/snykOss/views/ossVulnerabilityTreeProvider.ts +++ /dev/null @@ -1,230 +0,0 @@ -import { OpenCommandIssueType, OpenIssueCommandArg } from '../../common/commands/types'; -import { IConfiguration } from '../../common/configuration/configuration'; -import { SNYK_OPEN_ISSUE_COMMAND } from '../../common/constants/commands'; -import { SNYK_ANALYSIS_STATUS } from '../../common/constants/views'; -import { messages as commonMessages } from '../../common/messages/analysisMessages'; -import { IContextService } from '../../common/services/contextService'; -import { IViewManagerService } from '../../common/services/viewManagerService'; -import { AnalysisTreeNodeProvider } from '../../common/views/analysisTreeNodeProvider'; -import { INodeIcon, NODE_ICONS, TreeNode } from '../../common/views/treeNode'; -import { messages } from '../messages/treeView'; -import { isResultCliError, OssFileResult, OssSeverity, OssVulnerability } from '../ossResult'; -import { OssService } from '../services/ossService'; - -type ISeverityCounts = { - [key in OssSeverity]: number; -}; - -export type OssIssueCommandArg = OssVulnerability & { - matchingIdVulnerabilities: OssVulnerability[]; - overviewHtml: string; -}; - -export class OssVulnerabilityTreeProvider extends AnalysisTreeNodeProvider { - constructor( - protected readonly viewManagerService: IViewManagerService, - protected readonly contextService: IContextService, - protected readonly ossService: OssService, - protected readonly configuration: IConfiguration, - ) { - super(configuration, ossService); - } - - async getRootChildren(): Promise { - if (!this.configuration.getFeaturesConfiguration()?.ossEnabled) { - return [ - new TreeNode({ - text: SNYK_ANALYSIS_STATUS.OSS_DISABLED, - }), - ]; - } - - if (!this.contextService.shouldShowOssAnalysis) return []; - - if (!this.ossService.isLsDownloadSuccessful) { - return [this.getErrorEncounteredTreeNode()]; - } - - if (!this.ossService.isCliReady) { - return [ - new TreeNode({ - text: messages.cookingDependencies, - }), - ]; - } else if (!this.ossService.isAnyWorkspaceFolderTrusted) { - return [this.getNoWorkspaceTrustTreeNode()]; - } - - if (this.ossService.isAnalysisRunning) { - return [ - new TreeNode({ - text: commonMessages.scanRunning, - }), - ]; - } - - const ossResults = this.ossService.getResultArray(); - if (!ossResults) { - return [ - new TreeNode({ - text: messages.runTest, - }), - ]; - } - - const nodes: TreeNode[] = []; - const [resultNodes, totalVulnCount] = await this.getResultNodes(ossResults); - nodes.push(...resultNodes); - - if (ossResults.length == 1 && isResultCliError(ossResults[0])) { - return nodes; - } - - nodes.sort(this.compareNodes); - - const topNodes = [ - new TreeNode({ - text: this.getIssueFoundText(totalVulnCount), - }), - this.getDurationTreeNode(), - this.getNoSeverityFiltersSelectedTreeNode(), - ]; - nodes.unshift(...topNodes.filter((n): n is TreeNode => n !== null)); - - return nodes; - } - - protected getIssueFoundText(nIssues: number): string { - switch (nIssues) { - case 0: - return messages.noVulnerabilitiesFound; - case 1: - return messages.singleVulnerabilityFound; - default: - return messages.multipleVulnerabilitiesFound(nIssues); - } - } - - protected getIssueDescriptionText( - dir: string | undefined, - vulnerabilities: readonly OssVulnerability[], - ): string | undefined { - return `${dir} - ${vulnerabilities.length} ${ - vulnerabilities.length === 1 ? messages.vulnerability : messages.vulnerabilities - }`; - } - - static getSeverityIcon(severity: OssSeverity | string): INodeIcon { - return ( - { - [OssSeverity.Critical]: NODE_ICONS.critical, - [OssSeverity.High]: NODE_ICONS.high, - [OssSeverity.Medium]: NODE_ICONS.medium, - [OssSeverity.Low]: NODE_ICONS.low, - }[severity] || NODE_ICONS.low - ); - } - - static getFileSeverity(counts: ISeverityCounts): OssSeverity { - for (const s of [OssSeverity.Critical, OssSeverity.High, OssSeverity.Medium, OssSeverity.Low]) { - if (counts[s]) return s; - } - - return OssSeverity.Low; - } - - /** Returns severity significance index. The higher, the more significant severity is. */ - static getSeverityComparatorIndex(severity: OssSeverity): number { - return Object.values(OssSeverity).indexOf(severity); - } - - onDidChangeTreeData = this.viewManagerService.refreshOssViewEmitter.event; - - private initFileSeverityCounts(): ISeverityCounts { - return { - [OssSeverity.Critical]: 0, - [OssSeverity.High]: 0, - [OssSeverity.Medium]: 0, - [OssSeverity.Low]: 0, - }; - } - - protected getFilteredIssues(uniqueVulnerabilities: OssVulnerability[]): OssVulnerability[] { - return uniqueVulnerabilities.filter(vuln => { - switch (vuln.severity.toLowerCase()) { - case OssSeverity.Critical: - return this.configuration.severityFilter.critical; - case OssSeverity.High: - return this.configuration.severityFilter.high; - case OssSeverity.Medium: - return this.configuration.severityFilter.medium; - case OssSeverity.Low: - return this.configuration.severityFilter.low; - default: - return true; - } - }); - } - - private async getResultNodes(ossResults: ReadonlyArray): Promise<[TreeNode[], number]> { - const nodes: TreeNode[] = []; - let totalVulnCount = 0; - - for (const fileResult of ossResults) { - if (isResultCliError(fileResult)) { - nodes.push(this.getErrorEncounteredTreeNode(fileResult.path)); - continue; - } - - const counts: ISeverityCounts = this.initFileSeverityCounts(); - const vulnerabilityNodes: TreeNode[] = []; - - const uniqueVulns = this.ossService.getUniqueVulnerabilities(fileResult.vulnerabilities); - totalVulnCount += uniqueVulns.length; - - const fileVulnerabilities = this.getFilteredIssues(uniqueVulns); - if (fileVulnerabilities.length == 0) continue; - - for (const vuln of fileVulnerabilities) { - counts[vuln.severity]++; - vulnerabilityNodes.push( - new TreeNode({ - text: `${vuln.packageName}@${vuln.version} - ${vuln.title}`, - icon: OssVulnerabilityTreeProvider.getSeverityIcon(vuln.severity), - internal: { - severity: OssVulnerabilityTreeProvider.getSeverityComparatorIndex(vuln.severity), - }, - command: { - command: SNYK_OPEN_ISSUE_COMMAND, - title: '', - arguments: [ - { - issueType: OpenCommandIssueType.OssVulnerability, - // eslint-disable-next-line no-await-in-loop - issue: await this.ossService.getOssIssueCommandArg(vuln, fileResult.vulnerabilities), - } as OpenIssueCommandArg, - ], - }, - }), - ); - } - - vulnerabilityNodes.sort(this.compareNodes); - const fileSeverity = OssVulnerabilityTreeProvider.getFileSeverity(counts); - - const fileNode = new TreeNode({ - text: fileResult.displayTargetFile, - description: this.getIssueDescriptionText(fileResult.projectName, fileVulnerabilities), - icon: OssVulnerabilityTreeProvider.getSeverityIcon(fileSeverity), - children: vulnerabilityNodes, - internal: { - nIssues: vulnerabilityNodes.length, - severity: OssVulnerabilityTreeProvider.getSeverityComparatorIndex(fileSeverity), - }, - }); - nodes.push(fileNode); - } - - return [nodes, totalVulnCount]; - } -} diff --git a/src/snyk/snykOss/views/suggestion/ossSuggestionWebviewProvider.ts b/src/snyk/snykOss/views/suggestion/ossSuggestionWebviewProvider.ts deleted file mode 100644 index 8d9ad8b86..000000000 --- a/src/snyk/snykOss/views/suggestion/ossSuggestionWebviewProvider.ts +++ /dev/null @@ -1,210 +0,0 @@ -import * as vscode from 'vscode'; -import { SNYK_OPEN_BROWSER_COMMAND } from '../../../common/constants/commands'; -import { SNYK_VIEW_SUGGESTION_OSS } from '../../../common/constants/views'; -import { ErrorHandler } from '../../../common/error/errorHandler'; -import { ILog } from '../../../common/logger/interfaces'; -import { messages as learnMessages } from '../../../common/messages/learn'; -import { LearnService } from '../../../common/services/learnService'; -import { getNonce } from '../../../common/views/nonce'; -import { WebviewPanelSerializer } from '../../../common/views/webviewPanelSerializer'; -import { WebviewProvider } from '../../../common/views/webviewProvider'; -import { ExtensionContext } from '../../../common/vscode/extensionContext'; -import { IVSCodeWindow } from '../../../common/vscode/window'; -import { messages as errorMessages } from '../../messages/error'; -import { OssIssueCommandArg } from '../ossVulnerabilityTreeProvider'; - -enum OssSuggestionsViewEventMessageType { - OpenBrowser = 'openBrowser', -} - -type OssSuggestionViewEventMessage = { - type: OssSuggestionsViewEventMessageType; - value: unknown; -}; - -export class OssSuggestionWebviewProvider extends WebviewProvider { - constructor( - protected readonly context: ExtensionContext, - private readonly window: IVSCodeWindow, - protected readonly logger: ILog, - private readonly learnService: LearnService, - ) { - super(context, logger); - } - - activate(): void { - this.context.addDisposables( - this.window.registerWebviewPanelSerializer(SNYK_VIEW_SUGGESTION_OSS, new WebviewPanelSerializer(this)), - ); - } - - async postLearnLessonMessage(vulnerability: OssIssueCommandArg): Promise { - try { - if (this.panel) { - const lesson = await this.learnService.getOssLesson(vulnerability); - if (lesson) { - void this.panel.webview.postMessage({ - type: 'setLesson', - args: { url: lesson.url, title: learnMessages.lessonButtonTitle }, - }); - } else { - void this.panel.webview.postMessage({ - type: 'setLesson', - args: null, - }); - } - } - } catch (e) { - ErrorHandler.handle(e, this.logger, learnMessages.getLessonError); - } - } - - async showPanel(vulnerability: OssIssueCommandArg): Promise { - try { - await this.focusSecondEditorGroup(); - - if (this.panel) { - this.panel.reveal(vscode.ViewColumn.Two, true); - } else { - this.panel = vscode.window.createWebviewPanel( - SNYK_VIEW_SUGGESTION_OSS, - 'Snyk OSS Vulnerability', - { - viewColumn: vscode.ViewColumn.Two, - preserveFocus: true, - }, - this.getWebviewOptions(), - ); - this.registerListeners(); - } - - this.panel.webview.html = this.getHtmlForWebview(this.panel.webview); - this.panel.iconPath = vscode.Uri.joinPath( - vscode.Uri.file(this.context.extensionPath), - 'media', - 'images', - 'snyk-oss.svg', - ); - - void this.panel.webview.postMessage({ type: 'set', args: vulnerability }); - void this.postLearnLessonMessage(vulnerability); - } catch (e) { - ErrorHandler.handle(e, this.logger, errorMessages.suggestionViewShowFailed); - } - } - - protected registerListeners(): void { - if (!this.panel) return; - - this.panel.onDidDispose(() => this.onPanelDispose(), null, this.disposables); - this.panel.webview.onDidReceiveMessage( - (data: OssSuggestionViewEventMessage) => { - switch (data.type) { - case OssSuggestionsViewEventMessageType.OpenBrowser: - void vscode.commands.executeCommand(SNYK_OPEN_BROWSER_COMMAND, data.value); - break; - default: - break; - } - }, - null, - this.disposables, - ); - this.panel.onDidChangeViewState(() => this.checkVisibility(), null, this.disposables); - } - - protected getHtmlForWebview(webview: vscode.Webview): string { - const images: Record = [ - ['icon-code', 'svg'], - ['dark-critical-severity', 'svg'], - ['dark-high-severity', 'svg'], - ['dark-medium-severity', 'svg'], - ['dark-low-severity', 'svg'], - ['learn-icon', 'svg'], - ].reduce((accumulator: Record, [name, ext]) => { - const uri = this.getWebViewUri('media', 'images', `${name}.${ext}`); - if (!uri) throw new Error('Image missing.'); - accumulator[name] = uri.toString(); - return accumulator; - }, {}); - - const scriptUri = this.getWebViewUri( - 'out', - 'snyk', - 'snykOss', - 'views', - 'suggestion', - 'ossSuggestionWebviewScript.js', - ); - const styleUri = this.getWebViewUri('media', 'views', 'oss', 'suggestion', 'suggestion.css'); - const learnStyleUri = this.getWebViewUri('media', 'views', 'common', 'learn.css'); - - const nonce = getNonce(); - - return ` - - - - - - - - - - - - -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
-
-
Vulnerable module
-
-
-
-
Introduced through
-
-
-
-
Fixed in
-
-
-
-
Exploit maturity
-
-
-
-
-

Detailed paths

-
-
-
-
-
-
- - - `; - } -} diff --git a/src/snyk/snykOss/views/suggestion/ossSuggestionWebviewScript.ts b/src/snyk/snykOss/views/suggestion/ossSuggestionWebviewScript.ts deleted file mode 100644 index 5dce4e32d..000000000 --- a/src/snyk/snykOss/views/suggestion/ossSuggestionWebviewScript.ts +++ /dev/null @@ -1,293 +0,0 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/no-unsafe-call */ -/// -// This script will be run within the webview itself -// It cannot access the main VS Code APIs directly. -(function () { - // TODO: Redefine types until bundling is introduced into extension - // https://stackoverflow.com/a/56938089/1713082 - type Vulnerability = { - id: string; - license?: string; - identifiers?: Identifiers; - title: string; - description: string; - language: string; - packageManager: string; - packageName: string; - severity: string; - name: string; - version: string; - exploit?: string; - - CVSSv3?: string; - cvssScore?: string; - - fixedIn?: Array; - from: Array; - upgradePath: Array; - isPatchable: boolean; - isUpgradable: boolean; - - matchingIdVulnerabilities: Vulnerability[]; - overviewHtml: string; - }; - - type Lesson = { - url: string; - title: string; - }; - - type Identifiers = { - CWE: string[]; - CVE: string[]; - }; - - const vscode = acquireVsCodeApi(); - let vulnerability = {} as Vulnerability; - - let lesson: Lesson | null; - - function navigateToUrl(url: string) { - vscode.postMessage({ - type: 'openBrowser', - value: url, - }); - } - - function fillLearnLink() { - const learnWrapper = document.querySelector('.learn')!; - learnWrapper.className = 'learn'; - - if (lesson) { - const learnLink = document.querySelector('.learn--link')!; - learnLink.innerText = lesson.title; - const lessonUrl = lesson.url; - learnLink.onclick = () => navigateToUrl(lessonUrl); - learnWrapper.className = 'learn show'; - } - } - - function showCurrentSuggestion() { - const severity = document.querySelector('.severity')!; - const title = document.querySelector('.suggestion .suggestion-text')!; - - // Set title - title.innerHTML = vulnerability.title; - - // Set severity icon - setSeverityIcon(); - - // Fill identifiers line - fillIdentifiers(); - - // Fill summary - fillSummary(); - - // Fill detailed paths - fillDetailedPaths(); - - // Fill overview - fillOverview(); - - function setSeverityIcon() { - if (vulnerability.severity) { - severity.querySelectorAll('img').forEach(n => { - if (n.id.slice(-1) === 'l') { - if (n.id.includes(vulnerability.severity)) n.className = 'icon light-only'; - else n.className = 'icon light-only hidden'; - } else { - if (n.id.includes(vulnerability.severity)) n.className = 'icon dark-only'; - else n.className = 'icon dark-only hidden'; - } - }); - } else { - severity.querySelectorAll('img').forEach(n => (n.className = 'icon hidden')); - } - } - - function fillIdentifiers() { - const identifiers = document.querySelector('.identifiers')!; - identifiers.innerHTML = ''; // reset node - const type = vulnerability.license ? 'License' : 'Vulnerability'; - const typeNode = document.createTextNode(type); - identifiers.appendChild(typeNode); - - vulnerability.identifiers?.CVE.forEach(cve => - appendIdentifierSpan(identifiers, cve, `https://cve.mitre.org/cgi-bin/cvename.cgi?name=${cve}`), - ); - vulnerability.identifiers?.CWE.forEach(cwe => appendIdentifierSpan(identifiers, cwe, getCweLink(cwe))); - if (vulnerability.cvssScore) appendIdentifierSpan(identifiers, `CVSS ${vulnerability.cvssScore}`); - appendIdentifierSpan(identifiers, vulnerability.id.toUpperCase(), `https://snyk.io/vuln/${vulnerability.id}`); - } - - function fillSummary() { - const module = document.querySelector('.module > .content')!; - module.textContent = vulnerability.name; - - const maturity = document.querySelector('.maturity > .content')!; - if (!vulnerability.exploit) { - maturity.classList.add('hidden'); - } else { - maturity.textContent = vulnerability.exploit; - } - - const introducedThrough = document.querySelector('.introduced-through > .content')!; - introducedThrough.innerHTML = ''; // reset node - if (vulnerability.from.length == 0) { - introducedThrough.classList.add('hidden'); - } else { - let modules = vulnerability.matchingIdVulnerabilities - .filter(vuln => vuln.from.length > 1) - .map(vuln => vuln.from[1]); - modules = [...new Set(modules)]; // obtain distinct only - - modules.forEach((module, i, arr) => { - appendIntroducedThroughSpan(introducedThrough, module, vulnerability.packageManager); - if (i != arr.length - 1) introducedThrough.append(document.createTextNode(', ')); - }); - } - - const fixedIn = document.querySelector('.fixed-in > .content')!; - fixedIn.innerHTML = ''; // reset node - if (!vulnerability.fixedIn || vulnerability.fixedIn.length == 0) { - fixedIn.append('Not fixed'); - } else { - fixedIn.append(vulnerability.name + '@'); - vulnerability.fixedIn.forEach((version, i, arr) => { - let versionStr = version; - if (i != arr.length - 1) versionStr = versionStr + ', '; - fixedIn.append(versionStr); - }); - } - } - - function fillDetailedPaths() { - const paths = document.querySelector('.detailed-paths')!; - paths.innerHTML = ''; // reset node - - vulnerability.matchingIdVulnerabilities.forEach(vuln => { - const introducedThrough = vuln.from.join(' > '); - - const isOutdated = vuln.upgradePath && vuln.upgradePath[1] === vuln.from[1]; - - // The logic as in registry - // https://github.com/snyk/registry/blob/5fe141a3c5eeb6b2c5e62cfa2b5a8643df29403d/frontend/src/components/IssueCardVulnerablePath/IssueCardVulnerablePath.vue#L109 - let remediationAdvice: string; - const upgradeMessage = `Upgrade to ${vuln.upgradePath[1]}`; - - if (vuln.isUpgradable || vuln.isPatchable) { - if (isOutdated) { - remediationAdvice = vuln.isPatchable ? upgradeMessage : getOutdatedDependencyMessage(vuln); - } else { - remediationAdvice = upgradeMessage; - } - } else { - remediationAdvice = 'none'; - } - - const html = ` -
-
Introduced through
-
${introducedThrough}
-
-
-
Remediation
-
${remediationAdvice}
-
`; - - const path = document.createElement('div'); - path.className = 'detailed-path'; - path.innerHTML = html; - paths.append(path); - }); - } - - function fillOverview() { - const overview = document.getElementById('overview')!; - overview.innerHTML = vulnerability.overviewHtml; - } - } - - function getCweLink(cwe: string) { - const id = cwe.toUpperCase().replace('CWE-', ''); - return `https://cwe.mitre.org/data/definitions/${id}.html`; - } - - function appendIdentifierSpan(identifiers: Element, id: string, link?: string) { - const delimiter = document.createElement('span'); - // delimiter.innerText = ' | '; - delimiter.className = 'delimiter'; - identifiers.appendChild(delimiter); - - let cveNode: HTMLElement; - if (link) { - cveNode = document.createElement('a'); - cveNode.onclick = () => navigateToUrl(link); - } else { - cveNode = document.createElement('span'); - } - - cveNode.innerText = id; - - identifiers.appendChild(cveNode); - } - - function appendIntroducedThroughSpan(introducedThrough: Element, module: string, packageManager: string) { - const supportedPackageManagers = ['npm']; - - let node: HTMLElement; - // replicate app.snyk.io linking logic from https://github.com/snyk/registry/blob/c78f0ae84dc20f25146880b3d3d5661f3d3e4db2/frontend/src/lib/issue-utils.ts#L547 - if (supportedPackageManagers.includes(packageManager.toLowerCase())) { - node = document.createElement('a'); - node.onclick = () => navigateToUrl(`https://app.snyk.io/test/${packageManager}/${module}`); - } else { - node = document.createElement('span'); - } - - node.innerText = module; - introducedThrough.appendChild(node); - } - - function getOutdatedDependencyMessage(vulnerability: Vulnerability) { - return `Your dependencies are out of date, otherwise you would be using a newer ${vulnerability.name} than ${ - vulnerability.name - }@${vulnerability.version}. - ${ - ['npm', 'yarn', 'yarn-workspace'].includes(vulnerability.packageManager) - ? `Try relocking your lockfile or deleting node_modules and reinstalling your dependencies. If the problem persists, one of your dependencies may be bundling outdated modules.` - : 'Try reinstalling your dependencies. If the problem persists, one of your dependencies may be bundling outdated modules.' - }`; - } - - window.addEventListener('message', event => { - const { type, args } = event.data; - switch (type) { - case 'set': { - vulnerability = args; - vscode.setState({ ...vscode.getState(), vulnerability }); - showCurrentSuggestion(); - break; - } - case 'get': { - vulnerability = vscode.getState()?.vulnerability || {}; - showCurrentSuggestion(); - break; - } - case 'setLesson': { - lesson = args; - vscode.setState({ ...vscode.getState(), lesson }); - fillLearnLink(); - break; - } - case 'getLesson': { - lesson = vscode.getState()?.lesson || null; - fillLearnLink(); - break; - } - } - }); -})(); diff --git a/src/snyk/snykOss/watchers/dailyScanJob.ts b/src/snyk/snykOss/watchers/dailyScanJob.ts deleted file mode 100644 index 6b21793b8..000000000 --- a/src/snyk/snykOss/watchers/dailyScanJob.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { IExtension } from '../../base/modules/interfaces'; - -export class DailyScanJob { - private readonly dayInMs = 86400000; - private job: NodeJS.Timeout; - - constructor(private readonly extension: IExtension) {} - - schedule(): void { - if (this.job) { - this.job.refresh(); - return; - } - - this.job = setTimeout(() => { - void this.extension.runOssScan(false); - }, this.dayInMs); - } -} diff --git a/src/snyk/snykOss/watchers/manifestFileWatcher.ts b/src/snyk/snykOss/watchers/manifestFileWatcher.ts index 9a13c05c1..d022125c3 100644 --- a/src/snyk/snykOss/watchers/manifestFileWatcher.ts +++ b/src/snyk/snykOss/watchers/manifestFileWatcher.ts @@ -45,13 +45,13 @@ export default function createManifestFileWatcher( const globPattern = `**/{${Object.values(SUPPORTED_MANIFEST_FILES).join(',')}}`; const watcher = workspace.createFileSystemWatcher(globPattern); - watcher.onDidChange(() => runOssScan()); - watcher.onDidDelete(() => runOssScan()); - watcher.onDidCreate(() => runOssScan()); + watcher.onDidChange(() => runScan()); + watcher.onDidDelete(() => runScan()); + watcher.onDidCreate(() => runScan()); - function runOssScan() { + function runScan() { if (configuration.shouldAutoScanOss) { - void extension.runOssScan(); + void extension.runScan(); } } diff --git a/src/test/unit/common/commands/commandController.test.ts b/src/test/unit/common/commands/commandController.test.ts index c77e022d0..4a13ede58 100644 --- a/src/test/unit/common/commands/commandController.test.ts +++ b/src/test/unit/common/commands/commandController.test.ts @@ -10,7 +10,7 @@ import { IOpenerService } from '../../../../snyk/common/services/openerService'; import { IProductService } from '../../../../snyk/common/services/productService'; import { IVSCodeCommands } from '../../../../snyk/common/vscode/commands'; import { IVSCodeWorkspace } from '../../../../snyk/common/vscode/workspace'; -import { OssServiceLanguageServer } from '../../../../snyk/snykOss/ossServiceLanguageServer'; +import { OssService } from '../../../../snyk/snykOss/ossService'; import { LanguageServerMock } from '../../mocks/languageServer.mock'; import { LoggerMock } from '../../mocks/logger.mock'; import { windowMock } from '../../mocks/window.mock'; @@ -26,7 +26,7 @@ suite('CommandController', () => { {} as IAuthenticationService, {} as IProductService, {} as IProductService, - {} as OssServiceLanguageServer, + {} as OssService, {} as ScanModeService, {} as IVSCodeWorkspace, {} as IVSCodeCommands, diff --git a/src/test/unit/common/services/learnService.test.ts b/src/test/unit/common/services/learnService.test.ts index e5715a87d..60804dcd2 100644 --- a/src/test/unit/common/services/learnService.test.ts +++ b/src/test/unit/common/services/learnService.test.ts @@ -1,8 +1,7 @@ -import { strictEqual } from 'assert'; import sinon from 'sinon'; +import { strictEqual } from 'assert'; import { IVSCodeCommands } from '../../../../snyk/common/vscode/commands'; import { LearnService } from '../../../../snyk/common/services/learnService'; -import { OssIssueCommandArg } from '../../../../snyk/snykOss/views/ossVulnerabilityTreeProvider'; import { CodeIssueData, Issue, IssueSeverity } from '../../../../snyk/common/languageServer/types'; import { SNYK_GET_LESSON_COMMAND } from '../../../../snyk/common/constants/commands'; @@ -20,21 +19,6 @@ suite('LearnService', () => { sinon.restore(); }); - test('getOssLesson executes correct command', async () => { - const learnService = new LearnService(commands); - - const issue: OssIssueCommandArg = { - id: 'id', - packageManager: 'packageManager', - } as OssIssueCommandArg; - - await learnService.getOssLesson(issue); - strictEqual(executeCommandFake.calledOnce, true); - strictEqual( - executeCommandFake.calledWith(SNYK_GET_LESSON_COMMAND, issue.id, issue.packageManager, '', '', 4), - true, - ); - }); test('getCodeLesson executes correct command', async () => { const learnService = new LearnService(commands); const issue: Issue = { diff --git a/src/test/unit/snykOss/ossServiceLanguageServer.test.ts b/src/test/unit/snykOss/ossService.test.ts similarity index 91% rename from src/test/unit/snykOss/ossServiceLanguageServer.test.ts rename to src/test/unit/snykOss/ossService.test.ts index b1e997c6d..1dc50a3fe 100644 --- a/src/test/unit/snykOss/ossServiceLanguageServer.test.ts +++ b/src/test/unit/snykOss/ossService.test.ts @@ -7,11 +7,10 @@ import { ILanguageServer } from '../../../snyk/common/languageServer/languageSer import { OssIssueData, ScanProduct, ScanStatus } from '../../../snyk/common/languageServer/types'; import { IProductService } from '../../../snyk/common/services/productService'; import { IViewManagerService } from '../../../snyk/common/services/viewManagerService'; -import { ICodeActionAdapter, ICodeActionKindAdapter } from '../../../snyk/common/vscode/codeAction'; import { ExtensionContext } from '../../../snyk/common/vscode/extensionContext'; import { IVSCodeLanguages } from '../../../snyk/common/vscode/languages'; import { IVSCodeWorkspace } from '../../../snyk/common/vscode/workspace'; -import { OssServiceLanguageServer } from '../../../snyk/snykOss/ossServiceLanguageServer'; +import { OssService } from '../../../snyk/snykOss/ossService'; import { OssDetailPanelProvider } from '../../../snyk/snykOss/providers/ossDetailPanelProvider'; import { LanguageServerMock } from '../mocks/languageServer.mock'; import { LoggerMock } from '../mocks/logger.mock'; @@ -29,7 +28,7 @@ suite('OSS Service', () => { refreshOssView: refreshViewFake, } as unknown as IViewManagerService; - service = new OssServiceLanguageServer( + service = new OssService( {} as ExtensionContext, {} as IConfiguration, {} as OssDetailPanelProvider, diff --git a/src/test/unit/snykOss/services/vulnerabilityCount/vulnerabilityCountProvider.test.ts b/src/test/unit/snykOss/providers/vulnerabilityCountProvider.test.ts similarity index 88% rename from src/test/unit/snykOss/services/vulnerabilityCount/vulnerabilityCountProvider.test.ts rename to src/test/unit/snykOss/providers/vulnerabilityCountProvider.test.ts index 79c933516..96765d53f 100644 --- a/src/test/unit/snykOss/services/vulnerabilityCount/vulnerabilityCountProvider.test.ts +++ b/src/test/unit/snykOss/providers/vulnerabilityCountProvider.test.ts @@ -1,18 +1,18 @@ import { deepStrictEqual, strictEqual } from 'assert'; import sinon from 'sinon'; -import { CliError } from '../../../../../snyk/cli/services/cliService'; -import { Language } from '../../../../../snyk/common/types'; -import { ILanguageClientAdapter } from '../../../../../snyk/common/vscode/languageClient'; -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'; +import { CliError } from '../../../../snyk/cli/services/cliService'; +import { Language } from '../../../../snyk/common/types'; +import { ILanguageClientAdapter } from '../../../../snyk/common/vscode/languageClient'; +import { ITextDocumentAdapter } from '../../../../snyk/common/vscode/textdocument'; +import { IUriAdapter } from '../../../../snyk/common/vscode/uri'; +import { OssResultBody, OssVulnerability } from '../../../../snyk/snykOss/interfaces'; +import { OssService } from '../../../../snyk/snykOss/ossService'; +import { ModuleVulnerabilityCountProvider } from '../../../../snyk/snykOss/providers/vulnerabilityCountProvider'; +import { ImportedModule } from '../../../../snyk/snykOss/services/vulnerabilityCount/importedModule'; suite('OSS ModuleVulnerabilityCountProvider', () => { - let ossService: OssServiceLanguageServer; - let vulnerabilityCountProvider: ModuleVulnerabilityCountProviderLS; + let ossService: OssService; + let vulnerabilityCountProvider: ModuleVulnerabilityCountProvider; const sampleFilePath = 'C:\\git\\project\\test.js'; const sampleModuleName = 'mongo-express'; @@ -54,8 +54,8 @@ suite('OSS ModuleVulnerabilityCountProvider', () => { ]; setup(() => { - ossService = {} as OssServiceLanguageServer; - vulnerabilityCountProvider = new ModuleVulnerabilityCountProviderLS( + ossService = {} as OssService; + vulnerabilityCountProvider = new ModuleVulnerabilityCountProvider( ossService, {} as ILanguageClientAdapter, {} as IUriAdapter, diff --git a/src/test/unit/snykOss/services/ossService.test.ts b/src/test/unit/snykOss/services/ossService.test.ts deleted file mode 100644 index de0817279..000000000 --- a/src/test/unit/snykOss/services/ossService.test.ts +++ /dev/null @@ -1,197 +0,0 @@ -import { deepStrictEqual, rejects, strictEqual } from 'assert'; -import * as fs from 'fs/promises'; -import _ from 'lodash'; -import sinon from 'sinon'; -import { CliProcess } from '../../../../snyk/cli/process'; -import { IAnalytics } from '../../../../snyk/common/analytics/itly'; -import { IConfiguration } from '../../../../snyk/common/configuration/configuration'; -import { WorkspaceTrust } from '../../../../snyk/common/configuration/trustedFolders'; -import { ILog } from '../../../../snyk/common/logger/interfaces'; -import { DownloadService } from '../../../../snyk/common/services/downloadService'; -import { INotificationService } from '../../../../snyk/common/services/notificationService'; -import { IViewManagerService } from '../../../../snyk/common/services/viewManagerService'; -import { IWebViewProvider } from '../../../../snyk/common/views/webviewProvider'; -import { ExtensionContext } from '../../../../snyk/common/vscode/extensionContext'; -import { IVSCodeWorkspace } from '../../../../snyk/common/vscode/workspace'; -import { OssFileResult, OssResult, OssSeverity } from '../../../../snyk/snykOss/ossResult'; -import { OssService } from '../../../../snyk/snykOss/services/ossService'; -import { OssIssueCommandArg } from '../../../../snyk/snykOss/views/ossVulnerabilityTreeProvider'; -import { DailyScanJob } from '../../../../snyk/snykOss/watchers/dailyScanJob'; -import { LanguageServerMock } from '../../mocks/languageServer.mock'; -import { LoggerMock } from '../../mocks/logger.mock'; - -suite('OssService', () => { - const extensionPath = 'test/path'; - let logger: ILog; - let ossService: OssService; - - setup(() => { - logger = new LoggerMock(); - - const ls = new LanguageServerMock(); - ls.cliReady$.next(''); - - const testFolderPath = ''; - ossService = new OssService( - { - extensionPath, - } as ExtensionContext, - logger, - { - getAdditionalCliParameters: () => '', - getCliPath: () => undefined, - isAutomaticDependencyManagementEnabled: () => true, - getTrustedFolders: () => [testFolderPath], - } as unknown as IConfiguration, - {} as IWebViewProvider, - { - getWorkspaceFolders: () => [testFolderPath], - } as IVSCodeWorkspace, - { - refreshOssView: () => undefined, - } as IViewManagerService, - {} as DownloadService, - { - schedule: sinon.fake(), - } as unknown as DailyScanJob, - {} as INotificationService, - { - logAnalysisIsReady: sinon.fake(), - } as unknown as IAnalytics, - ls, - new WorkspaceTrust(), - ); - }); - - teardown(() => { - sinon.restore(); - }); - - test('Maps single project result correctly', async () => { - const cliOutput = await fs.readFile('mocked_data/snykOss/single-project-vulnerabilities.json', 'utf-8'); - sinon.stub(CliProcess.prototype, 'spawn').resolves(cliOutput); - - const result = await ossService.test(false, false); - const expected = JSON.parse(cliOutput) as OssResult; - deepStrictEqual(result, expected); - }); - - test('Maps multiple project results correctly', async () => { - const cliOutput = await fs.readFile('mocked_data/snykOss/multi-project-vulnerabilities.json', 'utf-8'); - sinon.stub(CliProcess.prototype, 'spawn').resolves(cliOutput); - - const result = await ossService.test(false, false); - const expected = JSON.parse(cliOutput) as OssResult; - deepStrictEqual(result, expected); - }); - - test('Empty result output throws an error', async () => { - sinon.stub(CliProcess.prototype, 'spawn').resolves(''); - await rejects(async () => await ossService.test(false, false)); - }); - - test('Invalid JSON output throws an error', async () => { - sinon.stub(CliProcess.prototype, 'spawn').resolves('{'); - await rejects(async () => await ossService.test(false, false)); - }); - - test('Gets new critical vulns count correctly for single project', () => { - const oldOssResult = { - vulnerabilities: [ - { - id: '1', - severity: OssSeverity.Critical, - }, - { - id: '2', - severity: OssSeverity.Medium, - }, - ], - displayTargetFile: '', - packageManager: '', - projectName: '', - } as OssFileResult; - - // Assert: latest result has same vulnerability count - strictEqual(ossService.getNewCriticalVulnerabilitiesCount(oldOssResult, oldOssResult), 0); - - const newOssResult = { - ..._.clone(oldOssResult), - vulnerabilities: [ - { - id: '1', - severity: OssSeverity.Critical, - }, - { - id: '2', - severity: OssSeverity.Medium, - }, - { - id: '3', - severity: OssSeverity.Critical, - }, - { - id: '4', - severity: OssSeverity.Medium, - }, - ], - } as OssFileResult; - - // Assert: latest result has more vulnerabilities - strictEqual(ossService.getNewCriticalVulnerabilitiesCount(newOssResult, oldOssResult), 1); - - // Assert: latest result has less vulnerabilities - strictEqual(ossService.getNewCriticalVulnerabilitiesCount(oldOssResult, newOssResult), 0); - }); - - test('Gets new critical vulns count correctly for multiple projects', () => { - const oldOssResult = { - vulnerabilities: [ - { - id: '1', - severity: OssSeverity.Critical, - }, - { - id: '2', - severity: OssSeverity.Medium, - }, - ], - displayTargetFile: '', - packageManager: '', - projectName: '', - } as OssFileResult; - const oldOssResults = [oldOssResult, oldOssResult]; - - // Assert: latest result has same vulnerability count - strictEqual(ossService.getNewCriticalVulnerabilitiesCount(oldOssResults, oldOssResults), 0); - - const newOssResult = { - ..._.clone(oldOssResult), - vulnerabilities: [ - { - id: '1', - severity: OssSeverity.Critical, - }, - { - id: '2', - severity: OssSeverity.Medium, - }, - { - id: '3', - severity: OssSeverity.Critical, - }, - { - id: '4', - severity: OssSeverity.Medium, - }, - ], - } as OssFileResult; - const newOssResults = [newOssResult, newOssResult]; - - // Assert: latest result has more vulnerabilities - strictEqual(ossService.getNewCriticalVulnerabilitiesCount(newOssResults, oldOssResults), 2); - - // Assert: latest result has less vulnerabilities - strictEqual(ossService.getNewCriticalVulnerabilitiesCount(oldOssResults, newOssResults), 0); - }); -}); diff --git a/src/test/unit/snykOss/services/vulnerabilityCount/ossVulnerabilityCountService.test.ts b/src/test/unit/snykOss/services/vulnerabilityCount/ossVulnerabilityCountService.test.ts index b5d547667..cace82ca2 100644 --- a/src/test/unit/snykOss/services/vulnerabilityCount/ossVulnerabilityCountService.test.ts +++ b/src/test/unit/snykOss/services/vulnerabilityCount/ossVulnerabilityCountService.test.ts @@ -12,26 +12,26 @@ 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 { OssServiceLanguageServer } from '../../../../../snyk/snykOss/ossServiceLanguageServer'; -import { OssVulnerabilityCountServiceLS } from '../../../../../snyk/snykOss/services/vulnerabilityCount/ossVulnerabilityCountServiceLS'; -import { ModuleVulnerabilityCountProviderLS } from '../../../../../snyk/snykOss/services/vulnerabilityCount/vulnerabilityCountProviderLS'; +import { OssFileResult } from '../../../../../snyk/snykOss/interfaces'; +import { OssService } from '../../../../../snyk/snykOss/ossService'; +import { ModuleVulnerabilityCountProvider } from '../../../../../snyk/snykOss/providers/vulnerabilityCountProvider'; +import { OssVulnerabilityCountService } from '../../../../../snyk/snykOss/services/vulnerabilityCount/ossVulnerabilityCountService'; import { LoggerMock } from '../../../mocks/logger.mock'; suite('OSS VulnerabilityCountService', () => { let workspace: IVSCodeWorkspace; let window: IVSCodeWindow; let languages: IVSCodeLanguages; - let ossVulnerabilityCountService: OssVulnerabilityCountServiceLS; - let ossService: OssServiceLanguageServer; - let vulnerabilityCountProvider: ModuleVulnerabilityCountProviderLS; + let ossVulnerabilityCountService: OssVulnerabilityCountService; + let ossService: OssService; + let vulnerabilityCountProvider: ModuleVulnerabilityCountProvider; setup(() => { const logger = new LoggerMock(); ossService = { scanFinished$: EMPTY, newResultAvailable$: EMPTY, - } as unknown as OssServiceLanguageServer; + } as unknown as OssService; workspace = {} as IVSCodeWorkspace; window = { createTextEditorDecorationType: sinon.fake(), @@ -41,7 +41,7 @@ suite('OSS VulnerabilityCountService', () => { registerCodeActionsProvider: sinon.fake(), registerHoverProvider: sinon.fake(), } as unknown as IVSCodeLanguages; - vulnerabilityCountProvider = new ModuleVulnerabilityCountProviderLS( + vulnerabilityCountProvider = new ModuleVulnerabilityCountProvider( ossService, {} as ILanguageClientAdapter, {} as IUriAdapter, @@ -52,7 +52,7 @@ suite('OSS VulnerabilityCountService', () => { const analytics = {} as IAnalytics; const configuration = {} as IConfiguration; - ossVulnerabilityCountService = new OssVulnerabilityCountServiceLS( + ossVulnerabilityCountService = new OssVulnerabilityCountService( workspace, window, languages, diff --git a/src/test/unit/snykOss/services/watchers/dailyScanJob.test.ts b/src/test/unit/snykOss/services/watchers/dailyScanJob.test.ts deleted file mode 100644 index a9de4a5eb..000000000 --- a/src/test/unit/snykOss/services/watchers/dailyScanJob.test.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { strictEqual } from 'assert'; -import sinon from 'sinon'; -import { IExtension } from '../../../../../snyk/base/modules/interfaces'; -import { DailyScanJob } from '../../../../../snyk/snykOss/watchers/dailyScanJob'; - -suite('OSS DailyScanJob', () => { - let extension: IExtension; - let clock: sinon.SinonFakeTimers; - let ossScanSpy: sinon.SinonSpy; - - setup(() => { - ossScanSpy = sinon.fake(); - extension = { - runOssScan: ossScanSpy, - } as unknown as IExtension; - clock = sinon.useFakeTimers(); - }); - - teardown(() => { - sinon.restore(); - clock; - }); - - test('Runs a scan after 24 hours have passed', () => { - const job = new DailyScanJob(extension); - job.schedule(); - - clock.tick(86400000); - strictEqual(ossScanSpy.calledOnce, true); - }); - - test("Doesn't run scan before 24 hours have passed", () => { - const job = new DailyScanJob(extension); - job.schedule(); - - clock.tick(86399999); - strictEqual(ossScanSpy.calledOnce, false); - }); - - test('24h timer is reset when new schedule happens', () => { - const job = new DailyScanJob(extension); - job.schedule(); - - clock.tick(86399999); - strictEqual(ossScanSpy.called, false); - - job.schedule(); - - clock.tick(86399999); - strictEqual(ossScanSpy.called, false); - - clock.tick(1); - strictEqual(ossScanSpy.calledOnce, true); - }); -});