Skip to content

Commit

Permalink
feat: add detection for standalone/cli-embedded based on size, add lo…
Browse files Browse the repository at this point in the history
…gging args if given `-d` or env var

- SNYK_LOG_DEVEL overrules all log level settings now
- `-d` or `--debug` in additional parameters now puts language server in debug mode

Signed-off-by: Bastian Doetsch <[email protected]>
  • Loading branch information
bastiandoetsch committed Sep 8, 2023
1 parent 3f47e04 commit 779bbd2
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 9 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# Snyk Security - Code and Open Source Dependencies Changelog

## [1.21.6]
## [1.23.0]
- add detection for standalone/cli embedded language server based on binary size

## [1.22.0]

### Added

Expand Down
27 changes: 25 additions & 2 deletions src/snyk/common/languageServer/languageServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,13 @@ import { LsExecutable } from './lsExecutable';
import { LanguageClientMiddleware } from './middleware';
import { InitializationOptions, LanguageServerSettings } from './settings';
import { CodeIssueData, IacIssueData, OssIssueData, Scan } from './types';
import * as fs from 'fs';

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

stop(): Promise<void>;

showOutputChannel(): void;

cliReady$: ReplaySubject<string>;
Expand Down Expand Up @@ -77,11 +80,31 @@ export class LanguageServer implements ILanguageServer {

const lsBinaryPath = LsExecutable.getPath(this.configuration.getSnykLanguageServerPath());

this.logger.info(`Snyk Language Server path: ${lsBinaryPath}`);
// log level is set to info by default
let logLevel = 'info';
const additionalCliParameters = this.configuration.getAdditionalCliParameters();
if (
additionalCliParameters != null &&
additionalCliParameters.length > 0 &&
(additionalCliParameters.includes('-d') || additionalCliParameters.includes('--debug'))
) {
logLevel = 'debug';
}
logLevel = process.env.SNYK_LOG_LEVEL ?? logLevel;

// check size of file at lsBinaryPath to determine if cli or ls
let args = ['-l', logLevel];
const lsBinarySize = fs.statSync(lsBinaryPath).size;
const fiftyMB = 50 * 1024 * 1024;
if (lsBinarySize > fiftyMB) {
args = ['language-server', ...args];
}

this.logger.info(`Snyk Language Server - path: ${lsBinaryPath}`);
this.logger.info(`Snyk Language Server - args: ${args}`);
const serverOptions: ServerOptions = {
command: lsBinaryPath,
args: ['-l', 'info'],
args: args,
options: {
env: processEnv,
},
Expand Down
134 changes: 128 additions & 6 deletions src/test/unit/common/languageServer/languageServer.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
import assert, { deepStrictEqual, strictEqual } from 'assert';
/* eslint-disable @typescript-eslint/no-empty-function */
import assert, { deepStrictEqual, fail, strictEqual } from 'assert';
import { ReplaySubject } from 'rxjs';
import sinon from 'sinon';
import { v4 } from 'uuid';
Expand All @@ -16,6 +16,7 @@ import { defaultFeaturesConfigurationStub } from '../../mocks/configuration.mock
import { LoggerMock } from '../../mocks/logger.mock';
import { windowMock } from '../../mocks/window.mock';
import { stubWorkspaceConfiguration } from '../../mocks/workspace.mock';
import * as fs from 'fs';

suite('Language Server', () => {
const authServiceMock = {} as IAuthenticationService;
Expand All @@ -24,24 +25,39 @@ suite('Language Server', () => {
let configurationMock: IConfiguration;
let languageServer: LanguageServer;
let downloadServiceMock: DownloadService;
const path = 'testPath';
const logger = {
info(_msg: string) {},
warn(_msg: string) {},
log(_msg: string) {},
error(msg: string) {
fail(msg);
},
} as unknown as LoggerMock;

setup(() => {
configurationMock = {
getInsecure(): boolean {
return true;
},
getCliPath(): string | undefined {
return 'testPath';
return path;
},
getToken(): Promise<string | undefined> {
return Promise.resolve('testToken');
},
shouldReportEvents: true,
shouldReportErrors: true,
getSnykLanguageServerPath(): string {
return 'testPath';
try {
fs.statSync(path);
} catch (_) {
fs.writeFileSync(path, 'huhu');
}
return path;
},
getAdditionalCliParameters() {
return '--all-projects';
return '--all-projects -d';
},
isAutomaticDependencyManagementEnabled() {
return true;
Expand Down Expand Up @@ -73,9 +89,114 @@ suite('Language Server', () => {
});

teardown(() => {
fs.rmSync(path, { force: true });
sinon.restore();
});

test('LanguageServer (standalone) starts with correct args', async () => {
const lca = sinon.spy({
create(
_id: string,
_name: string,
serverOptions: ServerOptions,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_clientOptions: LanguageClientOptions,
): LanguageClient {
return {
start(): Promise<void> {
assert.strictEqual('args' in serverOptions ? serverOptions?.args?.[0] : '', '-l');
assert.strictEqual('args' in serverOptions ? serverOptions?.args?.[1] : '', 'debug');
return Promise.resolve();
},
onNotification(): void {
return;
},
onReady(): Promise<void> {
return Promise.resolve();
},
} as unknown as LanguageClient;
},
});

languageServer = new LanguageServer(
user,
configurationMock,
lca as unknown as ILanguageClientAdapter,
stubWorkspaceConfiguration('snyk.loglevel', 'trace'),
windowMock,
authServiceMock,
{
info(_msg: string) {},
warn(_msg: string) {},
log(_msg: string) {},
error(msg: string) {
fail(msg);
},
} as unknown as LoggerMock,
downloadServiceMock,
);
downloadServiceMock.downloadReady$.next();

await languageServer.start();
sinon.assert.called(lca.create);
sinon.verify();
});

test('LanguageServer (embedded) starts with language-server arg', async () => {
configurationMock.getSnykLanguageServerPath = () => {
try {
fs.statSync(path);
} catch (_) {
// write >50MB of data to simulate a CLI sized binary
let data = '';
const size = 55 * 128 * 1024; // 128 * 8 = 1024 bytes, 55 * 1024 * 1024 = 55MB
for (let i = 0; i < size; i++) {
data += '01234567';
}
fs.writeFileSync(path, data);
}
return path;
};
const lca = sinon.spy({
create(
_id: string,
_name: string,
serverOptions: ServerOptions,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_clientOptions: LanguageClientOptions,
): LanguageClient {
return {
start(): Promise<void> {
assert.strictEqual('args' in serverOptions ? serverOptions?.args?.[0] : '', 'language-server');
return Promise.resolve();
},
onNotification(): void {
return;
},
onReady(): Promise<void> {
return Promise.resolve();
},
} as unknown as LanguageClient;
},
});

languageServer = new LanguageServer(
user,
configurationMock,
lca as unknown as ILanguageClientAdapter,
stubWorkspaceConfiguration('snyk.loglevel', 'trace'),
windowMock,
authServiceMock,
logger,
downloadServiceMock,
);
downloadServiceMock.downloadReady$.next();

await languageServer.start();
sinon.assert.called(lca.create);
sinon.verify();
});

test('LanguageServer adds proxy settings to env of started binary', async () => {

Check failure on line 200 in src/test/unit/common/languageServer/languageServer.test.ts

View workflow job for this annotation

GitHub Actions / Build and Test (ubuntu-latest)

Async arrow function has no 'await' expression
const expectedProxy = 'http://localhost:8080';
const lca = sinon.spy({
Expand All @@ -90,9 +211,11 @@ suite('Language Server', () => {
assert.strictEqual(id, 'Snyk LS');
assert.strictEqual(name, 'Snyk Language Server');
assert.strictEqual(
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
'options' in serverOptions ? serverOptions?.options?.env?.http_proxy : undefined,
expectedProxy,
);
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
assert.strictEqual(clientOptions.initializationOptions.token, 'testToken');
return Promise.resolve();
},
Expand All @@ -117,7 +240,6 @@ suite('Language Server', () => {
downloadServiceMock,
);
downloadServiceMock.downloadReady$.next();
await languageServer.start();
sinon.assert.called(lca.create);
sinon.verify();
});
Expand Down

0 comments on commit 779bbd2

Please sign in to comment.