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

[pull] main from snyk:main #180

Merged
merged 3 commits into from
Nov 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
3 changes: 2 additions & 1 deletion .github/workflows/release-preview.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ name: Build and Release "Preview"
on:
push:
branches:
- main
# TODO: revert back to main once this feature is released
- feat/HEAD-78_oss_via_ls
jobs:
build:
uses: snyk/vscode-extension/.github/workflows/ci.yaml@main
Expand Down
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
# Snyk Security - Code and Open Source Dependencies Changelog

## [1.26.1]

### Fixed

- Expanded the server settings returned by `LanguageClientMiddleware` to include necessary attributes for consistent initialization across the application.

### Added

- Introduced the `defaultToTrue` utility function within `LanguageServerSettings` to treat undefined feature flags as enabled by default.

### Changed

- Enhanced the `ServerSettings` type to include user-specific attributes such as `integrationName`, `integrationVersion`, `automaticAuthentication`, and `deviceId`. This unification simplifies the configuration management.

### BREAKING CHANGES

- The `fromConfiguration` method in `LanguageServerSettings` now requires a `User` object to initialize server settings, impacting all areas of the application where server settings are consumed.
- `LanguageClientMiddleware` instantiation now requires a `User` object, aligning with new server settings structure. Consumers must now pass a `User` object upon middleware creation.

## [1.25.1]

### Changed
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@
},
"snyk.advanced.organization": {
"type": "string",
"markdownDescription": "Specifies an organization slug name to run tests for that organization. \n\nNote: the value must match the URL slug as displayed in the URL of your org in the Snyk UI: `https://app.snyk.io/org/[orgslugname]`. If not specified, preferred organization as defined in your [web account settings](https://app.snyk.io/account) is used to run tests.",
"markdownDescription": "Specifies an organization slug name to run tests for that organization. \n\nNote: The slug name can be extracted from the URL of your organization in the Snyk UI: `https://app.snyk.io/org/[orgslugname]`. If not specified, preferred organization as defined in your [web account settings](https://app.snyk.io/account) is used to run tests.",
"scope": "window"
},
"snyk.advanced.tokenStorage": {
Expand Down
21 changes: 6 additions & 15 deletions src/snyk/common/languageServer/languageServer.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import _ from 'lodash';
import { firstValueFrom, ReplaySubject, Subject } from 'rxjs';
import { IAuthenticationService } from '../../base/services/authenticationService';
import { CLI_INTEGRATION_NAME } from '../../cli/contants/integration';
import { Configuration, IConfiguration } from '../configuration/configuration';
import { IConfiguration } from '../configuration/configuration';
import {
SNYK_ADD_TRUSTED_FOLDERS,
SNYK_CLI_PATH,
Expand All @@ -22,9 +21,8 @@ import { IVSCodeWindow } from '../vscode/window';
import { IVSCodeWorkspace } from '../vscode/workspace';
import { LsExecutable } from './lsExecutable';
import { LanguageClientMiddleware } from './middleware';
import { InitializationOptions, LanguageServerSettings } from './settings';
import { LanguageServerSettings, ServerSettings } from './settings';
import { CodeIssueData, IacIssueData, OssIssueData, Scan } from './types';
import * as fs from 'fs';

export interface ILanguageServer {
start(): Promise<void>;
Expand Down Expand Up @@ -111,7 +109,7 @@ export class LanguageServer implements ILanguageServer {
synchronize: {
configurationSection: CONFIGURATION_IDENTIFIER,
},
middleware: new LanguageClientMiddleware(this.configuration),
middleware: new LanguageClientMiddleware(this.configuration, this.user),
/**
* We reuse the output channel here as it's not properly disposed of by the language client ([email protected])
* See: https://github.com/microsoft/vscode-languageserver-node/blob/cdf4d6fdaefe329ce417621cf0f8b14e0b9bb39d/client/src/common/client.ts#L2789
Expand Down Expand Up @@ -178,16 +176,9 @@ export class LanguageServer implements ILanguageServer {

// Initialization options are not semantically equal to server settings, thus separated here
// https://github.com/microsoft/language-server-protocol/issues/567
async getInitializationOptions(): Promise<InitializationOptions> {
const settings = await LanguageServerSettings.fromConfiguration(this.configuration);

return {
...settings,
integrationName: CLI_INTEGRATION_NAME,
integrationVersion: await Configuration.getVersion(),
deviceId: this.user.anonymousId,
automaticAuthentication: 'false',
};
async getInitializationOptions(): Promise<ServerSettings> {
const settings = await LanguageServerSettings.fromConfiguration(this.configuration, this.user);
return settings;
}

showOutputChannel(): void {
Expand Down
9 changes: 5 additions & 4 deletions src/snyk/common/languageServer/middleware.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { IConfiguration } from '../configuration/configuration';
import {
import { IConfiguration } from '../../common/configuration/configuration';
import { User } from '../user';
import type {
CancellationToken,
ConfigurationParams,
ConfigurationRequestHandlerSignature,
Expand All @@ -18,7 +19,7 @@ export type LanguageClientWorkspaceMiddleware = Partial<WorkspaceMiddleware> & {
};

export class LanguageClientMiddleware implements Middleware {
constructor(private configuration: IConfiguration) {}
constructor(private configuration: IConfiguration, private user: User) {}

workspace: LanguageClientWorkspaceMiddleware = {
configuration: async (
Expand All @@ -39,7 +40,7 @@ export class LanguageClientMiddleware implements Middleware {
return [];
}

const serverSettings = await LanguageServerSettings.fromConfiguration(this.configuration);
const serverSettings = await LanguageServerSettings.fromConfiguration(this.configuration, this.user);
return [serverSettings];
},
};
Expand Down
94 changes: 62 additions & 32 deletions src/snyk/common/languageServer/settings.ts
Original file line number Diff line number Diff line change
@@ -1,64 +1,94 @@
import _ from 'lodash';
import { IConfiguration, SeverityFilter } from '../configuration/configuration';

export type InitializationOptions = ServerSettings & {
integrationName?: string;
integrationVersion?: string;
automaticAuthentication?: string;
deviceId?: string;
};
import { CLI_INTEGRATION_NAME } from '../../cli/contants/integration';
import { Configuration, IConfiguration, SeverityFilter } from '../configuration/configuration';
import { User } from '../user';

export type ServerSettings = {
// Feature toggles
activateSnykCodeSecurity?: string;
activateSnykCodeQuality?: string;
activateSnykOpenSource?: string;
activateSnykIac?: string;

// Endpoint path, and organization
path?: string;
cliPath?: string;
endpoint?: string;
organization?: string;

// Authentication and parameters
token?: string;
automaticAuthentication?: string;
additionalParams?: string;
path?: string;
manageBinariesAutomatically?: string;

// Reporting and telemetry
sendErrorReports?: string;
organization?: string;
enableTelemetry?: string;
manageBinariesAutomatically?: string;
cliPath?: string;
token?: string;

// Security and scanning settings
filterSeverity?: SeverityFilter;
scanningMode?: string;
insecure?: string;

// Trusted folders feature
enableTrustedFoldersFeature?: string;
trustedFolders?: string[];
insecure?: string;
scanningMode?: string;

// Snyk integration settings
integrationName?: string;
integrationVersion?: string;
deviceId?: string;
};

/**
* Transforms a boolean or undefined value into a string representation.
* It guarantees that undefined values are represented as 'true'.
* This utility is used to ensure feature flags are enabled by default
* when not explicitly set to false.
*
* @param {boolean | undefined} value - The value to transform.
* @returns {string} - The string 'true' if the value is undefined or truthy, 'false' if the value is false.
*/
export const defaultToTrue = (value: boolean | undefined): string => {
return `${value !== undefined ? value : true}`;
};

export class LanguageServerSettings {
static async fromConfiguration(configuration: IConfiguration): Promise<ServerSettings> {
static async fromConfiguration(configuration: IConfiguration, user: User): Promise<ServerSettings> {
const featuresConfiguration = configuration.getFeaturesConfiguration();

const iacEnabled = _.isUndefined(featuresConfiguration.iacEnabled) ? true : featuresConfiguration.iacEnabled;
const codeSecurityEnabled = _.isUndefined(featuresConfiguration.codeSecurityEnabled)
? true
: featuresConfiguration.codeSecurityEnabled;
const codeQualityEnabled = _.isUndefined(featuresConfiguration.codeQualityEnabled)
? true
: featuresConfiguration.codeQualityEnabled;
const iacEnabled = defaultToTrue(featuresConfiguration.iacEnabled);
const codeSecurityEnabled = defaultToTrue(featuresConfiguration.codeSecurityEnabled);
const codeQualityEnabled = defaultToTrue(featuresConfiguration.codeQualityEnabled);

return {
activateSnykCodeSecurity: `${codeSecurityEnabled}`,
activateSnykCodeQuality: `${codeQualityEnabled}`,
activateSnykCodeSecurity: codeSecurityEnabled,
activateSnykCodeQuality: codeQualityEnabled,
activateSnykOpenSource: 'false',
activateSnykIac: `${iacEnabled}`,
enableTelemetry: `${configuration.shouldReportEvents}`,
sendErrorReports: `${configuration.shouldReportErrors}`,
activateSnykIac: iacEnabled,

cliPath: configuration.getCliPath(),
endpoint: configuration.snykOssApiEndpoint,
additionalParams: configuration.getAdditionalCliParameters(),
organization: configuration.organization,

token: await configuration.getToken(),
automaticAuthentication: 'false',
additionalParams: configuration.getAdditionalCliParameters(),
manageBinariesAutomatically: `${configuration.isAutomaticDependencyManagementEnabled()}`,

sendErrorReports: `${configuration.shouldReportErrors}`,
enableTelemetry: `${configuration.shouldReportEvents}`,

filterSeverity: configuration.severityFilter,
scanningMode: configuration.scanningMode,
insecure: `${configuration.getInsecure()}`,

enableTrustedFoldersFeature: 'true',
trustedFolders: configuration.getTrustedFolders(),
insecure: `${configuration.getInsecure()}`,
scanningMode: configuration.scanningMode,

integrationName: CLI_INTEGRATION_NAME,
integrationVersion: await Configuration.getVersion(),
deviceId: user.anonymousId,
};
}
}
4 changes: 2 additions & 2 deletions src/test/unit/common/languageServer/languageServer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { v4 } from 'uuid';
import { IAuthenticationService } from '../../../../snyk/base/services/authenticationService';
import { IConfiguration } from '../../../../snyk/common/configuration/configuration';
import { LanguageServer } from '../../../../snyk/common/languageServer/languageServer';
import { InitializationOptions } from '../../../../snyk/common/languageServer/settings';
import { ServerSettings } from '../../../../snyk/common/languageServer/settings';
import { DownloadService } from '../../../../snyk/common/services/downloadService';
import { User } from '../../../../snyk/common/user';
import { ILanguageClientAdapter } from '../../../../snyk/common/vscode/languageClient';
Expand Down Expand Up @@ -198,7 +198,7 @@ suite('Language Server', () => {
});

test('LanguageServer should provide correct initialization options', async () => {
const expectedInitializationOptions: InitializationOptions = {
const expectedInitializationOptions: ServerSettings = {
activateSnykCodeSecurity: 'true',
activateSnykCodeQuality: 'true',
activateSnykOpenSource: 'false',
Expand Down
10 changes: 7 additions & 3 deletions src/test/unit/common/languageServer/middleware.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import { CliExecutable } from '../../../../snyk/cli/cliExecutable';
import { IConfiguration } from '../../../../snyk/common/configuration/configuration';
import { LanguageClientMiddleware } from '../../../../snyk/common/languageServer/middleware';
import { ServerSettings } from '../../../../snyk/common/languageServer/settings';
import {
import { User } from '../../../../snyk/common/user';
import type {
CancellationToken,
ConfigurationParams,
ConfigurationRequestHandlerSignature,
Expand All @@ -15,7 +16,10 @@ import { extensionContextMock } from '../../mocks/extensionContext.mock';

suite('Language Server: Middleware', () => {
let configuration: IConfiguration;
let user: User;

setup(() => {
user = { anonymousId: 'anonymous-id' } as User;
configuration = {
shouldReportEvents: false,
shouldReportErrors: false,
Expand Down Expand Up @@ -51,7 +55,7 @@ suite('Language Server: Middleware', () => {
});

test('Configuration request should translate settings', async () => {
const middleware = new LanguageClientMiddleware(configuration);
const middleware = new LanguageClientMiddleware(configuration, user);
const params: ConfigurationParams = {
items: [
{
Expand Down Expand Up @@ -96,7 +100,7 @@ suite('Language Server: Middleware', () => {
});

test('Configuration request should return an error', async () => {
const middleware = new LanguageClientMiddleware(configuration);
const middleware = new LanguageClientMiddleware(configuration, user);
const params: ConfigurationParams = {
items: [
{
Expand Down
55 changes: 55 additions & 0 deletions src/test/unit/common/languageServer/settings.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import assert from 'assert';
import { IConfiguration } from '../../../../snyk/common/configuration/configuration';
import { LanguageServerSettings, defaultToTrue } from '../../../../snyk/common/languageServer/settings';
import { User } from '../../../../snyk/common/user';

suite('LanguageServerSettings', () => {
suite('defaultToTrue', () => {
test('should return "true" for undefined values', () => {
assert.strictEqual(defaultToTrue(undefined), 'true');
});

test('should return "true" for truthy values', () => {
assert.strictEqual(defaultToTrue(true), 'true');
});

test('should return "false" for false values', () => {
assert.strictEqual(defaultToTrue(false), 'false');
});
});

suite('fromConfiguration', () => {
test('should generate server settings with default true values for undefined feature toggles', async () => {
const mockUser = { anonymousId: 'anonymous-id' } as User;
const mockConfiguration: IConfiguration = {
shouldReportEvents: true,
shouldReportErrors: false,
snykOssApiEndpoint: 'https://dev.snyk.io/api',
organization: 'my-org',
// eslint-disable-next-line @typescript-eslint/require-await
getToken: async () => 'snyk-token',
getFeaturesConfiguration: () => ({}), // iacEnabled, codeSecurityEnabled, codeQualityEnabled are undefined
getCliPath: () => '/path/to/cli',
getAdditionalCliParameters: () => '--all-projects -d',
getTrustedFolders: () => ['/trusted/path'],
getInsecure: () => false,
isAutomaticDependencyManagementEnabled: () => true,
severityFilter: { critical: true, high: true, medium: true, low: false },
scanningMode: 'scan-mode',
} as IConfiguration;

const serverSettings = await LanguageServerSettings.fromConfiguration(mockConfiguration, mockUser);

assert.strictEqual(serverSettings.activateSnykCodeSecurity, 'true');
assert.strictEqual(serverSettings.activateSnykCodeQuality, 'true');
assert.strictEqual(serverSettings.activateSnykIac, 'true');
assert.strictEqual(serverSettings.deviceId, 'anonymous-id');

assert.strictEqual(serverSettings.enableTelemetry, 'true');
assert.strictEqual(serverSettings.sendErrorReports, 'false');
assert.strictEqual(serverSettings.cliPath, '/path/to/cli');

assert.strictEqual(serverSettings.token, 'snyk-token');
});
});
});
Loading