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: OSS via LS [HEAD-78] #393

Merged
merged 16 commits into from
Nov 29, 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: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: CI
on:
pull_request:
branches:
- main
- '**'
workflow_call:
secrets:
ITERATIVELY_KEY:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/release-preview.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ jobs:
- name: Verify analytics events
run: npm run ampli:verify -- -t ${{ secrets.ITERATIVELY_KEY }}

# Naming convention for the preview version means we can only release one preview per hour
- name: Patch to preview version
run: npm run patch-preview
env:
Expand Down
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
# Snyk Security - Code and Open Source Dependencies Changelog

## [2.1.0]

### Added

- Snyk LS: Snyk Open Source Security features now use Language Server backend
- Snyk OSS: Squiggly warning underlines for direct and transitive vulnerabilities
- Snyk OSS: Squiggly underlines colour coded based on severity
- Snyk OSS: Vulnerability count text includes transitive vulnerabilities
- Snyk OSS: Vulnerability count text includes breakdown of vulnerabilities by severity
- Snyk OSS: Hovers lists vulnerabilities and shows summary (without typo)
- Snyk OSS: Hovers show information from security.snyk.io/vuln database
- Snyk OSS: CodeActions shows actions available for all vulnerabilities

## [1.26.1]

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## Run extension and debug

Clone the repository, then run `npm install` in the directory.
Clone the repository, then run `npm install && npm run build` in the directory.

- Open repository directory in VS Code and press `F5` to run extension in a new VS Code window.
- This allows extension debugging within VS Code.
Expand Down
30 changes: 21 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,7 @@
"@types/babel__traverse": "^7.12.2",
"@types/find-package-json": "^1.2.2",
"@types/glob": "^7.1.3",
"@types/he": "^1.2.3",
"@types/lodash": "^4.14.161",
"@types/marked": "^3.0.0",
"@types/mocha": "^8.0.3",
Expand Down Expand Up @@ -441,6 +442,7 @@
"analytics-node": "^4.0.1",
"axios": "^0.27.2",
"glob": "^7.2.0",
"he": "^1.2.0",
"htmlparser2": "^7.2.0",
"http-proxy-agent": "^5.0.0",
"https-proxy-agent": "^5.0.0",
Expand Down
4 changes: 1 addition & 3 deletions src/snyk/base/modules/baseSnykModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +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 { OssService } from '../../snykOss/services/ossService';
import { OssService } from '../../snykOss/ossService';
import { OssVulnerabilityCountService } from '../../snykOss/services/vulnerabilityCount/ossVulnerabilityCountService';
import { IAuthenticationService } from '../services/authenticationService';
import { ScanModeService } from '../services/scanModeService';
Expand Down Expand Up @@ -85,6 +85,4 @@ export default abstract class BaseSnykModule implements IBaseSnykModule {
}

abstract runScan(): Promise<void>;

abstract runOssScan(): Promise<void>;
}
1 change: 0 additions & 1 deletion src/snyk/base/modules/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ export interface IBaseSnykModule {

// Abstract methods
runScan(): Promise<void>;
runOssScan(manual?: boolean): Promise<void>;
}

export interface ISnykLib {
Expand Down
34 changes: 1 addition & 33 deletions src/snyk/base/modules/snykLib.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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<void> {
Logger.info('Enabling Snyk Code');
const wasEnabled = await this.codeSettings.enable();
Expand All @@ -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<void> {
await this.contextService.setContext(SNYK_CONTEXT.ADVANCED, configuration.shouldShowAdvancedView);
}
Expand All @@ -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<void> {
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();
}
Expand Down
3 changes: 2 additions & 1 deletion src/snyk/cli/process.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { ChildProcessWithoutNullStreams, spawn } from 'child_process';
import { OAuthToken } from '../base/services/authenticationService';
import { Configuration, IConfiguration } from '../common/configuration/configuration';
import { ILog } from '../common/logger/interfaces';
import { getVsCodeProxy } from '../common/proxy';
import { IVSCodeWorkspace } from '../common/vscode/workspace';
import { CLI_INTEGRATION_NAME } from './contants/integration';
import { CliError } from './services/cliService';
import { OAuthToken } from '../base/services/authenticationService';

export class CliProcess {
private runningProcess: ChildProcessWithoutNullStreams | null;
Expand All @@ -25,6 +25,7 @@ export class CliProcess {
return new Promise((resolve, reject) => {
let output = '';

// file deepcode ignore ArrayMethodOnNonArray: readonly string[] is an array of strings
this.logger.info(`Running "${cliPath} ${args.join(' ')}".`);

this.runningProcess = spawn(cliPath, args, { env: { ...process.env, ...processEnv }, cwd });
Expand Down
30 changes: 21 additions & 9 deletions src/snyk/common/commands/commandController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@
import _ from 'lodash';
import { IAuthenticationService } from '../../base/services/authenticationService';
import { ScanModeService } from '../../base/services/scanModeService';
import { createDCIgnore } from '../../snykCode/utils/ignoreFileUtils';
import { createDCIgnore as createDCIgnoreUtil } from '../../snykCode/utils/ignoreFileUtils';
import { IssueUtils } from '../../snykCode/utils/issueUtils';
import { CodeIssueCommandArg } from '../../snykCode/views/interfaces';
import { IacIssueCommandArg } from '../../snykIac/views/interfaces';
import { capitalizeOssSeverity } from '../../snykOss/ossResult';
import { OssService } from '../../snykOss/services/ossService';
import { OssIssueCommandArg } from '../../snykOss/views/ossVulnerabilityTreeProvider';
import { OssService } from '../../snykOss/ossService';
import { IAnalytics } from '../analytics/itly';
import {
SNYK_INITIATE_LOGIN_COMMAND,
Expand Down Expand Up @@ -90,11 +88,11 @@ export class CommandController {
const paths = this.workspace.getWorkspaceFolders();
const promises = [];
for (const p of paths) {
promises.push(createDCIgnore(p, custom, this.workspace, this.window, uriAdapter));
promises.push(createDCIgnoreUtil(p, custom, this.workspace, this.window, uriAdapter));
}
await Promise.all(promises);
} else {
await createDCIgnore(path, custom, this.workspace, this.window, uriAdapter);
await createDCIgnoreUtil(path, custom, this.workspace, this.window, uriAdapter);
}
}

Expand Down Expand Up @@ -122,14 +120,28 @@ export class CommandController {
severity: IssueUtils.issueSeverityAsText(issue.severity),
});
} else if (arg.issueType == OpenCommandIssueType.OssVulnerability) {
const issue = arg.issue as OssIssueCommandArg;
void this.ossService.showSuggestionProvider(issue);
const issueArgs = arg.issue as CodeIssueCommandArg;
const folderPath = issueArgs.folderPath;
const issue = this.ossService.getIssue(folderPath, issueArgs.id);

if (!issue) {
this.logger.warn(`Failed to find the issue ${issueArgs.id}.`);
return;
}

await this.openLocalFile(issue.filePath, issueArgs.range);

try {
await this.ossService.showSuggestionProvider(folderPath, issueArgs.id);
} catch (e) {
ErrorHandler.handle(e, this.logger);
}

this.analytics.logIssueInTreeIsClicked({
ide: IDE_NAME,
issueId: issue.id,
issueType: 'Open Source Vulnerability',
severity: capitalizeOssSeverity(issue.severity),
severity: IssueUtils.issueSeverityAsText(issue.severity),
});
} else if (arg.issueType == OpenCommandIssueType.IacIssue) {
const issueArgs = arg.issue as IacIssueCommandArg;
Expand Down
11 changes: 2 additions & 9 deletions src/snyk/common/commands/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { completeFileSuggestionType } from '../../snykCode/interfaces';
import { CodeIssueCommandArg } from '../../snykCode/views/interfaces';
import { IacIssueCommandArg } from '../../snykIac/views/interfaces';
import { OssIssueCommandArg } from '../../snykOss/views/ossVulnerabilityTreeProvider';
import { OssIssueCommandArg } from '../../snykOss/interfaces';
import { CodeIssueData, Issue } from '../languageServer/types';

export enum OpenCommandIssueType {
Expand All @@ -11,7 +11,7 @@ export enum OpenCommandIssueType {
}

export type OpenIssueCommandArg = {
issue: CodeIssueCommandArg | OssIssueCommandArg | IacIssueCommandArg;
issue: CodeIssueCommandArg | IacIssueCommandArg | OssIssueCommandArg;
issueType: OpenCommandIssueType;
};

Expand All @@ -21,10 +21,3 @@ export const isCodeIssue = (
): _issue is Issue<CodeIssueData> => {
return issueType === OpenCommandIssueType.CodeIssue;
};

export const isOssIssue = (
_issue: completeFileSuggestionType | Issue<CodeIssueData> | OssIssueCommandArg,
issueType: OpenCommandIssueType,
): _issue is OssIssueCommandArg => {
return issueType === OpenCommandIssueType.OssVulnerability;
};
1 change: 0 additions & 1 deletion src/snyk/common/constants/general.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading
Loading