Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: create new ossService #385

Merged
merged 5 commits into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/snyk/base/modules/baseSnykModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ 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 { OssVulnerabilityCountService } from '../../snykOss/services/vulnerabilityCount/ossVulnerabilityCountService';
import { IAuthenticationService } from '../services/authenticationService';
Expand All @@ -45,6 +46,7 @@ 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;
Expand Down
4 changes: 3 additions & 1 deletion src/snyk/common/languageServer/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ export class LanguageServerSettings {
static async fromConfiguration(configuration: IConfiguration): Promise<ServerSettings> {
const featuresConfiguration = configuration.getFeaturesConfiguration();

const ossEnabled = _.isUndefined(featuresConfiguration.ossEnabled) ? true : featuresConfiguration.ossEnabled;

const iacEnabled = _.isUndefined(featuresConfiguration.iacEnabled) ? true : featuresConfiguration.iacEnabled;
const codeSecurityEnabled = _.isUndefined(featuresConfiguration.codeSecurityEnabled)
? true
Expand All @@ -44,7 +46,7 @@ export class LanguageServerSettings {
return {
activateSnykCodeSecurity: `${codeSecurityEnabled}`,
activateSnykCodeQuality: `${codeQualityEnabled}`,
activateSnykOpenSource: 'false',
activateSnykOpenSource: `${ossEnabled}`,
activateSnykIac: `${iacEnabled}`,
enableTelemetry: `${configuration.shouldReportEvents}`,
sendErrorReports: `${configuration.shouldReportErrors}`,
Expand Down
27 changes: 26 additions & 1 deletion src/snyk/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ import {
SNYK_OPEN_BROWSER_COMMAND,
SNYK_OPEN_ISSUE_COMMAND,
SNYK_OPEN_LOCAL_COMMAND,
SNYK_SET_TOKEN_COMMAND,
SNYK_SETTINGS_COMMAND,
SNYK_SET_TOKEN_COMMAND,
SNYK_SHOW_LS_OUTPUT_COMMAND,
SNYK_SHOW_OUTPUT_COMMAND,
SNYK_START_COMMAND,
Expand Down Expand Up @@ -74,11 +74,13 @@ 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/services/ossService';
import { OssVulnerabilityCountService } from './snykOss/services/vulnerabilityCount/ossVulnerabilityCountService';
import { ModuleVulnerabilityCountProvider } from './snykOss/services/vulnerabilityCount/vulnerabilityCountProvider';
import { OssVulnerabilityTreeProvider } from './snykOss/views/ossVulnerabilityTreeProvider';
import { OssSuggestionWebviewProvider } from './snykOss/views/suggestion/ossSuggestionWebviewProvider';
import { OssSuggestionWebviewProviderLanguageServer } from './snykOss/views/suggestion/ossSuggestionWebviewProviderLanguageServer';
import { DailyScanJob } from './snykOss/watchers/dailyScanJob';

class SnykExtension extends SnykLib implements IExtension {
Expand Down Expand Up @@ -223,6 +225,29 @@ class SnykExtension extends SnykLib implements IExtension {
this.workspaceTrust,
);

const ossSuggestionProvider = new OssSuggestionWebviewProviderLanguageServer(
vsCodeWindow,
extensionContext,
Logger,
vsCodeLanguages,
vsCodeWorkspace,
);

this.ossServiceLanguageServer = new OssServiceLanguageServer(
extensionContext,
configuration,
ossSuggestionProvider,
new CodeActionAdapter(),
this.codeActionKindAdapter,
this.viewManagerService,
vsCodeWorkspace,
this.workspaceTrust,
this.languageServer,
vsCodeLanguages,
Logger,
this.analytics,
);

const iacSuggestionProvider = new IacSuggestionWebviewProvider(
vsCodeWindow,
extensionContext,
Expand Down
61 changes: 61 additions & 0 deletions src/snyk/snykOss/ossServiceLanguageServer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { Subscription } from 'rxjs';
import { IAnalytics } from '../common/analytics/itly';
import { IConfiguration } from '../common/configuration/configuration';
import { IWorkspaceTrust } from '../common/configuration/trustedFolders';
import { ILanguageServer } from '../common/languageServer/languageServer';
import { OssIssueData, Scan, ScanProduct } from '../common/languageServer/types';
import { ILog } from '../common/logger/interfaces';
import { ProductService } from '../common/services/productService';
import { IViewManagerService } from '../common/services/viewManagerService';
import { ICodeActionAdapter, ICodeActionKindAdapter } from '../common/vscode/codeAction';
import { ExtensionContext } from '../common/vscode/extensionContext';
import { IVSCodeLanguages } from '../common/vscode/languages';
import { IVSCodeWorkspace } from '../common/vscode/workspace';
import { IOssSuggestionWebviewProvider } from './views/interfaces';

export class OssServiceLanguageServer extends ProductService<OssIssueData> {
constructor(
extensionContext: ExtensionContext,
config: IConfiguration,
suggestionProvider: IOssSuggestionWebviewProvider,
readonly codeActionAdapter: ICodeActionAdapter,
readonly codeActionKindAdapter: ICodeActionKindAdapter,
viewManagerService: IViewManagerService,
workspace: IVSCodeWorkspace,
workspaceTrust: IWorkspaceTrust,
languageServer: ILanguageServer,
languages: IVSCodeLanguages,
logger: ILog,
readonly analytics: IAnalytics,
) {
super(
extensionContext,
config,
suggestionProvider,
viewManagerService,
workspace,
workspaceTrust,
languageServer,
languages,
logger,
);

// this.registerCodeActionsProvider(
// new OssCodeActionsProvider(this.result, codeActionAdapter, codeActionKindAdapter, languages, analytics),
// );
}

subscribeToLsScanMessages(): Subscription {
return this.languageServer.scan$.subscribe((scan: Scan<OssIssueData>) => {
if (scan.product !== ScanProduct.OpenSource) {
return;
}

super.handleLsScanMessage(scan);
});
}

refreshTreeView() {
this.viewManagerService.refreshOssView();
}
}
11 changes: 11 additions & 0 deletions src/snyk/snykOss/views/interfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Issue, OssIssueData } from '../../common/languageServer/types';
import { IWebViewProvider } from '../../common/views/webviewProvider';

export interface IOssSuggestionWebviewProvider extends IWebViewProvider<Issue<OssIssueData>> {
openIssueId: string | undefined;
}

export type OssIssueCommandArgLanguageServer = OssIssueData & {
matchingIdVulnerabilities: OssIssueData[];
overviewHtml: string;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
import * as vscode from 'vscode';
import { SNYK_OPEN_BROWSER_COMMAND } from '../../../common/constants/commands';
import { SNYK_VIEW_SUGGESTION_IAC, SNYK_VIEW_SUGGESTION_OSS } from '../../../common/constants/views';
import { ErrorHandler } from '../../../common/error/errorHandler';
import { IacIssueData, Issue, OssIssueData } from '../../../common/languageServer/types';
import { ILog } from '../../../common/logger/interfaces';
import { getNonce } from '../../../common/views/nonce';
import { WebviewPanelSerializer } from '../../../common/views/webviewPanelSerializer';
import { IWebViewProvider, WebviewProvider } from '../../../common/views/webviewProvider';
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 { getAbsoluteMarkerFilePath } from '../../utils/analysisUtils';
// import { IssueUtils } from '../../utils/issueUtils';
// import { ICodeSuggestionWebviewProvider } from '../interfaces';

export class OssSuggestionWebviewProviderLanguageServer
extends WebviewProvider<Issue<OssIssueData>>
implements IWebViewProvider<Issue<OssIssueData>>
{
// For consistency reasons, the single source of truth for the current suggestion is the
// panel state. The following field is only used in
private issue: Issue<OssIssueData> | undefined;

constructor(
private readonly window: IVSCodeWindow,
protected readonly context: ExtensionContext,
protected readonly logger: ILog,
private readonly languages: IVSCodeLanguages,
private readonly workspace: IVSCodeWorkspace,
) {
super(context, logger);
}

activate(): void {
this.context.addDisposables(
this.window.registerWebviewPanelSerializer(SNYK_VIEW_SUGGESTION_OSS, new WebviewPanelSerializer(this)),
);
}

get openIssueId(): string | undefined {
return this.issue?.id;
}

async showPanel(issue: Issue<OssIssueData>): Promise<void> {
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-iac.svg', // TODO: USE OSS ICON
);

await this.panel.webview.postMessage({ type: 'set', args: issue });

this.issue = issue;
} 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.onDidChangeViewState(() => this.checkVisibility(), undefined, this.disposables);
// Handle messages from the webview
this.panel.webview.onDidReceiveMessage(msg => this.handleMessage(msg), undefined, this.disposables);
}

disposePanel(): void {
super.disposePanel();
}

protected onPanelDispose(): void {
super.onPanelDispose();
}

private async handleMessage(message: any) {
try {
const { type, value } = message;
switch (type) {
case 'openBrowser': {
await vscode.commands.executeCommand(SNYK_OPEN_BROWSER_COMMAND, value);
break;
}
default: {
throw new Error('Unknown message type');
}
}
} catch (e) {
ErrorHandler.handle(e, this.logger, errorMessages.suggestionViewShowFailed);
}
}

protected getHtmlForWebview(webview: vscode.Webview): string {
const images: Record<string, string> = [
['dark-critical-severity', 'svg'],
['dark-high-severity', 'svg'],
['dark-medium-severity', 'svg'],
['dark-low-severity', 'svg'],
].reduce((accumulator: Record<string, string>, [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',
'snykIac',
'views',
'suggestion',
'iacSuggestionWebviewScript.js',
);
const styleUri = this.getWebViewUri('media', 'views', 'oss', 'suggestion', 'suggestion.css'); // make it common

const nonce = getNonce();

return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">

<!--
Use a content security policy to only allow loading images from https or from our extension directory,
and only allow scripts that have a specific nonce.
-->
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src ${webview.cspSource}; img-src ${webview.cspSource} https:; script-src 'nonce-${nonce}';">

<link href="${styleUri}" rel="stylesheet">
</head>
<body>
<div class="suggestion">
<section class="suggestion--header">
<div class="severity">
<img id="lowl" class="icon light-only hidden" src="${images['dark-low-severity']}" />
<img id="lowd" class="icon dark-only hidden" src="${images['dark-low-severity']}" />
<img id="mediuml" class="icon light-only hidden" src="${images['dark-medium-severity']}" />
<img id="mediumd" class="icon dark-only hidden" src="${images['dark-medium-severity']}" />
<img id="highl" class="icon light-only hidden" src="${images['dark-high-severity']}" />
<img id="highd" class="icon dark-only hidden" src="${images['dark-high-severity']}" />
<img id="criticall" class="icon light-only hidden" src="${images['dark-critical-severity']}" />
<img id="criticald" class="icon dark-only hidden" src="${images['dark-critical-severity']}" />
<span id="severity-text"></span>
</div>
<div class="suggestion-text"></div>
<div class="identifiers"></div>
</section>
<section class="delimiter-top summary">
<div class="summary-item description">
<div class="label font-light">Description</div>
<div class="content"></div>
</div>
<div class="summary-item impact">
<div class="label font-light">Impact</div>
<div class="content"></div>
</div>
<div class="summary-item path">
<div class="label font-light">Path</div>
<div class="content">
<code></code>
</div>
</div>
</section>
</div>
<section class="delimiter-top">
<h2>Remediation</h2>
<div class="remediation" class="font-light"></div>
</section>
<section class="delimiter-top hidden references">
<h2>References</h2>
<div class="reference-links" class="font-light"></div>
</section>
</div>
<script nonce="${nonce}" src="${scriptUri}"></script>
</body>
</html>`;
}
}
Loading
Loading