Skip to content

Commit

Permalink
Feat/add iac treeview (#346)
Browse files Browse the repository at this point in the history
* feat: add IaC tree view

* feat: hide tree behind a feature flag

* test: add server test

* chore: add scan, settings and watcher

* chore: update CHANGELOG.md

---------

Co-authored-by: michelkaporin <[email protected]>
  • Loading branch information
Avishagp and michelkaporin authored Mar 10, 2023
1 parent 699f642 commit e15b649
Show file tree
Hide file tree
Showing 21 changed files with 726 additions and 20 deletions.
12 changes: 4 additions & 8 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
# Snyk Security - Code and Open Source Dependencies Changelog

## [1.16.2]
## [1.17.0]

### Added

- Snyk IaC: UI Feature flag

## [1.16.1]

### Added

- Snyk IaC: Added IaC issue data type definitions
- Snyk IaC: Added tree view.
- Snyk IaC: Added IaC issue data type definitions.
- Snyk IaC: UI Feature flag.

## [1.15.5]

Expand Down
9 changes: 7 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,11 @@
"name": "Code Security",
"when": "snyk:lsCodePreview && snyk:loggedIn && snyk:featuresSelected && snyk:codeEnabled && !snyk:codeLocalEngineEnabled && snyk:workspaceFound && !snyk:error"
},
{
"id": "snyk.views.analysis.configuration",
"name": "Configuration",
"when": "snyk:iacUiVisible && snyk:loggedIn && snyk:featuresSelected && snyk:workspaceFound && !snyk:error"
},
{
"id": "snyk.views.analysis.code.quality",
"name": "Code Quality",
Expand Down Expand Up @@ -325,12 +330,12 @@
"view/title": [
{
"command": "snyk.start",
"when": "view == 'snyk.views.analysis.code.security.old' || view == 'snyk.views.analysis.code.security' || view == 'snyk.views.analysis.code.quality.old' || view == 'snyk.views.analysis.code.quality' || view == 'snyk.views.analysis.oss'",
"when": "view == 'snyk.views.analysis.code.security.old' || view == 'snyk.views.analysis.code.security' || view == 'snyk.views.analysis.code.quality.old' || view == 'snyk.views.analysis.code.quality' || view == 'snyk.views.analysis.oss' || view == 'snyk.views.analysis.configuration'",
"group": "navigation"
},
{
"command": "snyk.settings",
"when": "view == 'snyk.views.analysis.code.security.old' || view == 'snyk.views.analysis.code.security' || view == 'snyk.views.analysis.code.quality.old' || view == 'snyk.views.analysis.code.quality' || view == 'snyk.views.analysis.oss' || view == 'snyk.views.welcome'",
"when": "view == 'snyk.views.analysis.code.security.old' || view == 'snyk.views.analysis.code.security' || view == 'snyk.views.analysis.code.quality.old' || view == 'snyk.views.analysis.code.quality' || view == 'snyk.views.analysis.oss' || view == 'snyk.views.welcome' || view == 'snyk.views.analysis.configuration'",
"group": "navigation"
}
],
Expand Down
3 changes: 3 additions & 0 deletions src/snyk/base/modules/baseSnykModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { CodeSettings, ICodeSettings } from '../../snykCode/codeSettings';
import { ISnykCodeErrorHandler, SnykCodeErrorHandler } from '../../snykCode/error/snykCodeErrorHandler';
import { FalsePositiveApi, IFalsePositiveApi } from '../../snykCode/falsePositive/api/falsePositiveApi';
import SnykEditorsWatcher from '../../snykCode/watchers/editorsWatcher';
import { ISnykIacService } from '../../snykIaC/iacService';
import { OssService } from '../../snykOss/services/ossService';
import { OssVulnerabilityCountService } from '../../snykOss/services/vulnerabilityCount/ossVulnerabilityCountService';
import { IAuthenticationService } from '../services/authenticationService';
Expand Down Expand Up @@ -69,6 +70,8 @@ export default abstract class BaseSnykModule implements IBaseSnykModule {
protected codeSettings: ICodeSettings;
protected codeScanOrchestrator: CodeScanOrchestrator;

iacService: ISnykIacService;

readonly loadingBadge: ILoadingBadge;
protected user: User;
protected experimentService: ExperimentService;
Expand Down
3 changes: 3 additions & 0 deletions src/snyk/common/constants/views.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const SNYK_VIEW_SUGGESTION_CODE = 'snyk.views.suggestion.code';
export const SNYK_VIEW_SUGGESTION_CODE_OLD = 'snyk.views.suggestion.code.old';
export const SNYK_VIEW_FALSE_POSITIVE_CODE = 'snyk.views.suggestion.code.falsePositive';
export const SNYK_VIEW_SUGGESTION_OSS = 'snyk.views.suggestion.oss';
export const SNYK_VIEW_ANALYSIS_IAC = 'snyk.views.analysis.configuration';

// Having multiple boolean contexts instead of a single context
// with multiple values helps us to avoid flickering UI.
Expand All @@ -20,6 +21,7 @@ export const SNYK_CONTEXT = {
FEATURES_SELECTED: 'featuresSelected',
CODE_ENABLED: 'codeEnabled',
CODE_LOCAL_ENGINE_ENABLED: 'codeLocalEngineEnabled',
IAC_UI_VISIBLE: 'iacUiVisible',
LS_CODE_PREVIEW: 'lsCodePreview',
WORKSPACE_FOUND: 'workspaceFound',
ERROR: 'error',
Expand All @@ -39,4 +41,5 @@ export const SNYK_ANALYSIS_STATUS = {
OSS_DISABLED: 'Snyk Open Source Security is disabled. Enable it in settings to use it.',
CODE_SECURITY_DISABLED: 'Snyk Code Security is disabled. Enable it in settings to use it.',
CODE_QUALITY_DISABLED: 'Snyk Code Quality is disabled. Enable it in settings to use it.',
IAC_DISABLED: 'Snyk Configuration is disabled. Enable it in settings to use it.',
};
8 changes: 4 additions & 4 deletions src/snyk/common/languageServer/languageServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,21 @@ import { IVSCodeWorkspace } from '../vscode/workspace';
import { LsExecutable } from './lsExecutable';
import { LanguageClientMiddleware } from './middleware';
import { InitializationOptions, LanguageServerSettings } from './settings';
import { CodeIssueData, OssIssueData, Scan } from './types';
import { CodeIssueData, IacIssueData, OssIssueData, Scan } from './types';

export interface ILanguageServer {
start(): Promise<void>;
stop(): Promise<void>;
showOutputChannel(): void;

cliReady$: ReplaySubject<string>;
scan$: Subject<Scan<CodeIssueData | OssIssueData>>;
scan$: Subject<Scan<CodeIssueData | OssIssueData | IacIssueData>>;
}

export class LanguageServer implements ILanguageServer {
private client: LanguageClient;
readonly cliReady$ = new ReplaySubject<string>(1);
readonly scan$ = new Subject<Scan<CodeIssueData | OssIssueData>>();
readonly scan$ = new Subject<Scan<CodeIssueData | OssIssueData | IacIssueData>>();

constructor(
private user: User,
Expand Down Expand Up @@ -156,7 +156,7 @@ export class LanguageServer implements ILanguageServer {
});
});

client.onNotification(SNYK_SCAN, (scan: Scan<CodeIssueData | OssIssueData>) => {
client.onNotification(SNYK_SCAN, (scan: Scan<CodeIssueData | OssIssueData | IacIssueData>) => {
this.logger.info(`${_.capitalize(scan.product)} scan for ${scan.folderPath}: ${scan.status}.`);
this.scan$.next(scan);
});
Expand Down
5 changes: 5 additions & 0 deletions src/snyk/common/services/contextService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export interface IContextService {
readonly viewContext: { [key: string]: unknown };
shouldShowCodeAnalysis: boolean;
shouldShowOssAnalysis: boolean;
shouldShowIacAnalysis: boolean;
isCodeInLsPreview: boolean;

setContext(key: string, value: unknown): Promise<void>;
Expand Down Expand Up @@ -36,6 +37,10 @@ export class ContextService implements IContextService {
return this.shouldShowAnalysis;
}

get shouldShowIacAnalysis(): boolean {
return this.shouldShowAnalysis && !!this.viewContext[SNYK_CONTEXT.IAC_UI_VISIBLE];
}

private get shouldShowAnalysis(): boolean {
return !this.viewContext[SNYK_CONTEXT.ERROR] && [SNYK_CONTEXT.LOGGEDIN].every(c => !!this.viewContext[c]);
}
Expand Down
11 changes: 11 additions & 0 deletions src/snyk/common/services/viewManagerService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export interface IViewManagerService {
readonly refreshCodeSecurityViewEmitter: EventEmitter<void>;
readonly refreshCodeQualityViewEmitter: EventEmitter<void>;
readonly refreshOssViewEmitter: EventEmitter<void>;
readonly refreshIacViewEmitter: EventEmitter<void>;

refreshAllViews(): void;
refreshAllCodeAnalysisViews(): void;
Expand All @@ -36,6 +37,7 @@ export interface IViewManagerService {
refreshCodeSecurityView(): void;
refreshCodeQualityView(): void;
refreshOssView(): void;
refreshIacView(): void;
}

export class ViewManagerService implements IViewManagerService {
Expand All @@ -48,6 +50,8 @@ export class ViewManagerService implements IViewManagerService {

readonly refreshOssViewEmitter: EventEmitter<void>;

readonly refreshIacViewEmitter: EventEmitter<void>;

constructor() {
this.refreshOldCodeSecurityViewEmitter = new EventEmitter<void>();
this.refreshOldCodeQualityViewEmitter = new EventEmitter<void>();
Expand All @@ -56,11 +60,14 @@ export class ViewManagerService implements IViewManagerService {

this.refreshOssViewEmitter = new EventEmitter<void>();
this.viewContainer = new ViewContainer();

this.refreshIacViewEmitter = new EventEmitter<void>();
}

refreshAllViews(): void {
this.refreshOssView();
this.refreshAllOldCodeAnalysisViews();
this.refreshIacView();
}

refreshAllOldCodeAnalysisViews(): void {
Expand Down Expand Up @@ -125,4 +132,8 @@ export class ViewManagerService implements IViewManagerService {
refreshOssView = _.throttle((): void => this.refreshOssViewEmitter.fire(), REFRESH_VIEW_DEBOUNCE_INTERVAL, {
leading: true,
});

refreshIacView = _.throttle((): void => this.refreshIacViewEmitter.fire(), REFRESH_VIEW_DEBOUNCE_INTERVAL, {
leading: true,
});
}
7 changes: 5 additions & 2 deletions src/snyk/common/watchers/configurationWatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
ADVANCED_CUSTOM_LS_PATH,
CODE_QUALITY_ENABLED_SETTING,
CODE_SECURITY_ENABLED_SETTING,
IAC_ENABLED_SETTING,
OSS_ENABLED_SETTING,
SEVERITY_FILTER_SETTING,
TRUSTED_FOLDERS,
Expand All @@ -36,6 +37,8 @@ class ConfigurationWatcher implements IWatcher {
extension.snykCodeOld.analyzer.refreshDiagnostics();
// If two settings are changed simultaneously, only one will be applied, thus refresh all views
return extension.viewManagerService.refreshAllOldCodeAnalysisViews();
} else if (key === IAC_ENABLED_SETTING) {
return extension.viewManagerService.refreshIacView();
} else if (key === SEVERITY_FILTER_SETTING) {
extension.snykCodeOld.analyzer.refreshDiagnostics();
return extension.viewManagerService.refreshAllViews();
Expand All @@ -46,8 +49,7 @@ class ConfigurationWatcher implements IWatcher {
return _.debounce(() => extension.restartLanguageServer(), DEFAULT_LS_DEBOUNCE_INTERVAL)();
} else if (key === TRUSTED_FOLDERS) {
extension.workspaceTrust.resetTrustedFoldersCache();
extension.viewManagerService.refreshAllCodeAnalysisViews();
// extension.viewManagerService.refreshAllViews(); // todo: when oss results delivered via LS
extension.viewManagerService.refreshAllViews();
}

const extensionConfig = vscode.workspace.getConfiguration('snyk');
Expand All @@ -70,6 +72,7 @@ class ConfigurationWatcher implements IWatcher {
OSS_ENABLED_SETTING,
CODE_SECURITY_ENABLED_SETTING,
CODE_QUALITY_ENABLED_SETTING,
IAC_ENABLED_SETTING,
SEVERITY_FILTER_SETTING,
ADVANCED_CUSTOM_ENDPOINT,
ADVANCED_CUSTOM_LS_PATH,
Expand Down
50 changes: 49 additions & 1 deletion src/snyk/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
SNYK_VIEW_ANALYSIS_CODE_QUALITY_OLD,
SNYK_VIEW_ANALYSIS_CODE_SECURITY,
SNYK_VIEW_ANALYSIS_CODE_SECURITY_OLD,
SNYK_VIEW_ANALYSIS_IAC,
SNYK_VIEW_ANALYSIS_OSS,
SNYK_VIEW_FEATURES,
SNYK_VIEW_SUPPORT,
Expand All @@ -58,7 +59,7 @@ import { vsCodeEnv } from './common/vscode/env';
import { extensionContext } from './common/vscode/extensionContext';
import { HoverAdapter } from './common/vscode/hover';
import { LanguageClientAdapter } from './common/vscode/languageClient';
import { vsCodeLanguages, VSCodeLanguages } from './common/vscode/languages';
import { VSCodeLanguages, vsCodeLanguages } from './common/vscode/languages';
import SecretStorageAdapter from './common/vscode/secretStorage';
import { ThemeColorAdapter } from './common/vscode/theme';
import { Range, Uri } from './common/vscode/types';
Expand All @@ -74,6 +75,9 @@ import { CodeQualityIssueTreeProviderOld } from './snykCode/views/qualityIssueTr
import CodeSecurityIssueTreeProvider from './snykCode/views/securityIssueTreeProvider';
import { CodeSecurityIssueTreeProviderOld } from './snykCode/views/securityIssueTreeProviderOld';
import { CodeSuggestionWebviewProvider } from './snykCode/views/suggestion/codeSuggestionWebviewProvider';
import { SnykIacService } from './snykIaC/iacService';
import IacIssueTreeProvider from './snykIaC/views/iacIssueTreeProvider';
//import { IacSuggestionWebviewProvider } from './snykIaC/views/suggestion/iacSuggestionWebviewProvider';
import { NpmTestApi } from './snykOss/api/npmTestApi';
import { EditorDecorator } from './snykOss/editor/editorDecorator';
import { OssService } from './snykOss/services/ossService';
Expand Down Expand Up @@ -228,6 +232,31 @@ class SnykExtension extends SnykLib implements IExtension {
this.workspaceTrust,
);

// const iacSuggestionProvider = new IacSuggestionWebviewProvider(
// vsCodeWindow,
// extensionContext,
// Logger,
// vsCodeLanguages,
// vsCodeWorkspace,
// this.learnService,
// );

this.iacService = new SnykIacService(
this.context,
configuration,
//iacSuggestionProvider,
// new CodeActionAdapter(),
// this.codeActionKindAdapter,
this.viewManagerService,
vsCodeWorkspace,
this.workspaceTrust,
this.languageServer,
// vsCodeWindow,
// vsCodeLanguages,
Logger,
// this.analytics,
);

this.commandController = new CommandController(
this.openerService,
this.authService,
Expand Down Expand Up @@ -327,6 +356,23 @@ class SnykExtension extends SnykLib implements IExtension {
codeEnablementTree,
);

const iacIssueProvider = new IacIssueTreeProvider(
this.viewManagerService,
this.contextService,
this.iacService,
configuration,
vsCodeLanguages,
);

const iacSecurityTree = vscode.window.createTreeView(SNYK_VIEW_ANALYSIS_IAC, {
treeDataProvider: iacIssueProvider,
});

vscodeContext.subscriptions.push(
vscode.window.registerTreeDataProvider(SNYK_VIEW_ANALYSIS_IAC, iacIssueProvider),
iacSecurityTree,
);

// Fill the view container to expose views for tests
const viewContainer = this.viewManagerService.viewContainer;
viewContainer.set(SNYK_VIEW_WELCOME, welcomeTree);
Expand Down Expand Up @@ -402,6 +448,8 @@ class SnykExtension extends SnykLib implements IExtension {
Logger.info('Code scans are not using Language Server.');
}

await this.contextService.setContext(SNYK_CONTEXT.IAC_UI_VISIBLE, !!configuration.getPreviewFeatures().iacUi);

await this.languageServer.start();

this.codeScanOrchestrator = new CodeScanOrchestrator(
Expand Down
5 changes: 5 additions & 0 deletions src/snyk/snykCode/codeService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export interface ISnykCodeService extends AnalysisStatusProvider, Disposable {
getIssueById(issueId: string): Issue<CodeIssueData> | undefined;
isAnyWorkspaceFolderTrusted: boolean;
resetResult(folderPath: string): void;
isAnyResultAvailable(): boolean;

activateWebviewProviders(): void;
showSuggestionProvider(folderPath: string, issueId: string): void;
Expand Down Expand Up @@ -106,6 +107,10 @@ export class SnykCodeService extends AnalysisStatusProvider implements ISnykCode
this.viewManagerService.refreshAllCodeAnalysisViews();
}

public isAnyResultAvailable(): boolean {
return this._result.size > 0;
}

activateWebviewProviders(): void {
this.suggestionProvider.activate();
}
Expand Down
2 changes: 1 addition & 1 deletion src/snyk/snykCode/views/issueTreeProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export class IssueTreeProvider extends AnalysisTreeNodeProvider {
];
}

if (this.codeService.result.size <= 0) {
if (!this.codeService.isAnyResultAvailable()) {
return [
new TreeNode({
text: messages.runTest,
Expand Down
4 changes: 4 additions & 0 deletions src/snyk/snykIaC/iacResult.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { IacIssueData, Issue } from '../common/languageServer/types';

export type IacWorkspaceFolderResult = Issue<IacIssueData>[] | Error;
export type IacResult = Map<string, IacWorkspaceFolderResult>; // map of a workspace folder to results array or an error occurred in this folder
Loading

0 comments on commit e15b649

Please sign in to comment.