Skip to content

Commit e8491ac

Browse files
snjezafbricon
authored andcommitted
Warn about sensible java preferences in project settings
Signed-off-by: Snjezana Peco <[email protected]>
1 parent 1fff007 commit e8491ac

File tree

4 files changed

+126
-23
lines changed

4 files changed

+126
-23
lines changed

src/extension.ts

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import * as path from 'path';
44
import * as os from 'os';
55
import * as fs from 'fs';
66
import { workspace, extensions, ExtensionContext, window, StatusBarAlignment, commands, ViewColumn, Uri, CancellationToken, TextDocumentContentProvider, TextEditor, WorkspaceConfiguration, languages, IndentAction, ProgressLocation, InputBoxOptions, Selection, Position, EventEmitter, OutputChannel } from 'vscode';
7-
import { ExecuteCommandParams, ExecuteCommandRequest, LanguageClient, LanguageClientOptions, RevealOutputChannelOn, Position as LSPosition, Location as LSLocation, StreamInfo, VersionedTextDocumentIdentifier, ErrorHandler, Message, ErrorAction, CloseAction, InitializationFailedHandler } from 'vscode-languageclient';
7+
import { ExecuteCommandParams, ExecuteCommandRequest, LanguageClient, LanguageClientOptions, RevealOutputChannelOn, Position as LSPosition, Location as LSLocation, StreamInfo, VersionedTextDocumentIdentifier, ErrorHandler, Message, ErrorAction, CloseAction, InitializationFailedHandler, DidChangeConfigurationNotification } from 'vscode-languageclient';
88
import { onExtensionChange, collectJavaExtensions } from './plugin';
99
import { prepareExecutable, awaitServerConnection } from './javaServerStarter';
1010
import { getDocumentSymbolsCommand, getDocumentSymbolsProvider } from './documentSymbols';
@@ -22,7 +22,7 @@ import * as refactorAction from './refactorAction';
2222
import * as pasteAction from './pasteAction';
2323
import * as net from 'net';
2424
import { getJavaConfiguration } from './utils';
25-
import { onConfigurationChange, excludeProjectSettingsFiles } from './settings';
25+
import { onConfigurationChange, excludeProjectSettingsFiles, getKey, IS_WORKSPACE_JDK_ALLOWED } from './settings';
2626
import { logger, initializeLogFile } from './log';
2727
import glob = require('glob');
2828
import { SnippetCompletionProvider } from './snippetCompletionProvider';
@@ -126,7 +126,7 @@ export function activate(context: ExtensionContext): Promise<ExtensionAPI> {
126126

127127
enableJavadocSymbols();
128128

129-
return requirements.resolveRequirements().catch(error => {
129+
return requirements.resolveRequirements(context).catch(error => {
130130
// show error
131131
window.showErrorMessage(error.message, error.label).then((selection) => {
132132
if (error.label && error.label === selection && error.command) {
@@ -138,7 +138,6 @@ export function activate(context: ExtensionContext): Promise<ExtensionAPI> {
138138
}).then(requirements => {
139139
return new Promise((resolve, reject) => {
140140
const workspacePath = path.resolve(storagePath + '/jdt_ws');
141-
142141
// Options to control the language client
143142
const clientOptions: LanguageClientOptions = {
144143
// Register the server for java
@@ -153,7 +152,7 @@ export function activate(context: ExtensionContext): Promise<ExtensionAPI> {
153152
initializationOptions: {
154153
bundles: collectJavaExtensions(extensions.all),
155154
workspaceFolders: workspace.workspaceFolders ? workspace.workspaceFolders.map(f => f.uri.toString()) : null,
156-
settings: { java: getJavaConfiguration() },
155+
settings: { java: getJavaConfig(requirements.java_home) },
157156
extendedClientCapabilities: {
158157
progressReportProvider: getJavaConfiguration().get('progressReports.enabled'),
159158
classFileContentsSupport: true,
@@ -170,6 +169,14 @@ export function activate(context: ExtensionContext): Promise<ExtensionAPI> {
170169
},
171170
triggerFiles: getTriggerFiles()
172171
},
172+
middleware: {
173+
workspace: {
174+
didChangeConfiguration: () => {
175+
languageClient.sendNotification(DidChangeConfigurationNotification.type, { settings: getJavaConfig(requirements.java_home) });
176+
onConfigurationChange(languageClient, context);
177+
}
178+
}
179+
},
173180
revealOutputChannelOn: RevealOutputChannelOn.Never,
174181
errorHandler: new ClientErrorHandler(extensionName),
175182
initializationFailedHandler: error => {
@@ -203,7 +210,7 @@ export function activate(context: ExtensionContext): Promise<ExtensionAPI> {
203210
if (!port) {
204211
const lsPort = process.env['JDTLS_CLIENT_PORT'];
205212
if (!lsPort) {
206-
serverOptions = prepareExecutable(requirements, workspacePath, getJavaConfiguration());
213+
serverOptions = prepareExecutable(requirements, workspacePath, getJavaConfig(requirements.java_home), context);
207214
} else {
208215
serverOptions = () => {
209216
const socket = net.connect(lsPort);
@@ -450,6 +457,13 @@ export function activate(context: ExtensionContext): Promise<ExtensionAPI> {
450457
});
451458
}
452459

460+
function getJavaConfig(javaHome: string) {
461+
const origConfig = getJavaConfiguration();
462+
const javaConfig = JSON.parse(JSON.stringify(origConfig));
463+
javaConfig.home = javaHome;
464+
return javaConfig;
465+
}
466+
453467
export function deactivate(): Thenable<void> {
454468
if (!languageClient) {
455469
return undefined;

src/javaServerStarter.ts

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,22 @@ import * as glob from 'glob';
55
import * as os from 'os';
66
import { StreamInfo, Executable, ExecutableOptions } from 'vscode-languageclient';
77
import { RequirementsData } from './requirements';
8-
import { getJavaEncoding } from './settings';
8+
import { getJavaEncoding, IS_WORKSPACE_VMARGS_ALLOWED, getKey, getJavaagentFlag } from './settings';
99
import { logger } from './log';
10+
import { getJavaConfiguration } from './utils';
11+
import { workspace, ExtensionContext } from 'vscode';
1012

1113
declare var v8debug;
1214
const DEBUG = (typeof v8debug === 'object') || startedInDebugMode();
1315

14-
export function prepareExecutable(requirements: RequirementsData, workspacePath, javaConfig): Executable {
16+
export function prepareExecutable(requirements: RequirementsData, workspacePath, javaConfig, context: ExtensionContext): Executable {
1517
const executable: Executable = Object.create(null);
1618
const options: ExecutableOptions = Object.create(null);
1719
options.env = process.env;
1820
options.stdio = 'pipe';
1921
executable.options = options;
2022
executable.command = path.resolve(requirements.java_home + '/bin/java');
21-
executable.args = prepareParams(requirements, javaConfig, workspacePath);
23+
executable.args = prepareParams(requirements, javaConfig, workspacePath, context);
2224
logger.info(`Starting Java server with: ${executable.command} ${executable.args.join(' ')}`);
2325
return executable;
2426
}
@@ -39,7 +41,7 @@ export function awaitServerConnection(port): Thenable<StreamInfo> {
3941
});
4042
}
4143

42-
function prepareParams(requirements: RequirementsData, javaConfiguration, workspacePath): string[] {
44+
function prepareParams(requirements: RequirementsData, javaConfiguration, workspacePath, context: ExtensionContext): string[] {
4345
const params: string[] = [];
4446
if (DEBUG) {
4547
params.push('-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=1044,quiet=y');
@@ -60,8 +62,23 @@ function prepareParams(requirements: RequirementsData, javaConfiguration, worksp
6062
if (DEBUG) {
6163
params.push('-Dlog.level=ALL');
6264
}
63-
64-
const vmargs = javaConfiguration.get('jdt.ls.vmargs', '');
65+
let vmargsCheck = workspace.getConfiguration().inspect('java.jdt.ls.vmargs').workspaceValue;
66+
if (vmargsCheck !== undefined) {
67+
const agentFlag = getJavaagentFlag(vmargsCheck);
68+
if (agentFlag !== null) {
69+
const keyVmargs = getKey(IS_WORKSPACE_VMARGS_ALLOWED, context.storagePath, vmargsCheck);
70+
const key = context.globalState.get(keyVmargs);
71+
if (key !== true) {
72+
vmargsCheck = workspace.getConfiguration().inspect('java.jdt.ls.vmargs').globalValue;
73+
}
74+
}
75+
}
76+
let vmargs;
77+
if (vmargsCheck !== undefined) {
78+
vmargs = vmargsCheck + '';
79+
} else {
80+
vmargs = '';
81+
}
6582
const encodingKey = '-Dfile.encoding=';
6683
if (vmargs.indexOf(encodingKey) < 0) {
6784
params.push(encodingKey + getJavaEncoding());

src/requirements.ts

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
'use strict';
22

3-
import { workspace, Uri, env } from 'vscode';
3+
import { workspace, Uri, env, window, ConfigurationTarget, commands, ExtensionContext } from 'vscode';
44
import * as cp from 'child_process';
55
import * as path from 'path';
66
import * as pathExists from 'path-exists';
77
import * as expandHomeDir from 'expand-home-dir';
88
import findJavaHome = require("find-java-home");
99
import { Commands } from './commands';
10+
import { checkJavaPreferences } from './settings';
1011

1112
const isWindows = process.platform.indexOf('win') === 0;
1213
const JAVAC_FILENAME = 'javac' + (isWindows ? '.exe' : '');
@@ -29,16 +30,16 @@ interface ErrorData {
2930
* if any of the requirements fails to resolve.
3031
*
3132
*/
32-
export async function resolveRequirements(): Promise<RequirementsData> {
33-
const javaHome = await checkJavaRuntime();
33+
export async function resolveRequirements(context: ExtensionContext): Promise<RequirementsData> {
34+
const javaHome = await checkJavaRuntime(context);
3435
const javaVersion = await checkJavaVersion(javaHome);
3536
return Promise.resolve({ java_home: javaHome, java_version: javaVersion });
3637
}
3738

38-
function checkJavaRuntime(): Promise<string> {
39-
return new Promise((resolve, reject) => {
39+
function checkJavaRuntime(context: ExtensionContext): Promise<string> {
40+
return new Promise(async (resolve, reject) => {
4041
let source: string;
41-
let javaHome: string = readJavaConfig();
42+
let javaHome = await checkJavaPreferences(context);
4243
if (javaHome) {
4344
source = `java.home variable defined in ${env.appName} settings`;
4445
} else {
@@ -78,11 +79,6 @@ function checkJavaRuntime(): Promise<string> {
7879
});
7980
}
8081

81-
function readJavaConfig(): string {
82-
const config = workspace.getConfiguration();
83-
return config.get<string>('java.home', null);
84-
}
85-
8682
function checkJavaVersion(javaHome: string): Promise<number> {
8783
return new Promise((resolve, reject) => {
8884
cp.execFile(javaHome + '/bin/java', ['-version'], {}, (error, stdout, stderr) => {

src/settings.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
'use strict';
22

3+
import * as path from 'path';
34
import { window, Uri, workspace, WorkspaceConfiguration, commands, ConfigurationTarget, env, ExtensionContext, TextEditor, Range, Disposable } from 'vscode';
45
import { LanguageClient } from 'vscode-languageclient';
56
import { Commands } from './commands';
67
import { getJavaConfiguration } from './utils';
78

89
const DEFAULT_HIDDEN_FILES: string[] = ['**/.classpath', '**/.project', '**/.settings', '**/.factorypath'];
10+
export const IS_WORKSPACE_JDK_ALLOWED = "java.ls.isJdkAllowed";
11+
export const IS_WORKSPACE_VMARGS_ALLOWED = "java.ls.isVmargsAllowed";
12+
const extensionName = 'Language Support for Java';
913

1014
const changeItem = {
1115
global: 'Exclude globally',
@@ -116,3 +120,75 @@ export function getJavaEncoding(): string {
116120
}
117121
return javaEncoding;
118122
}
123+
124+
export async function checkJavaPreferences(context: ExtensionContext) {
125+
let javaHome = workspace.getConfiguration().inspect<string>('java.home').workspaceValue;
126+
let isVerified = javaHome === undefined || javaHome === null;
127+
if (isVerified) {
128+
javaHome = workspace.getConfiguration().inspect<string>('java.home').globalValue;
129+
}
130+
const allow = 'Allow';
131+
const disallow = 'Disallow';
132+
const key = getKey(IS_WORKSPACE_JDK_ALLOWED, context.storagePath, javaHome);
133+
const globalState = context.globalState;
134+
if (!isVerified) {
135+
isVerified = globalState.get(key);
136+
if (isVerified === undefined) {
137+
await window.showErrorMessage(`Security Warning! Do you allow this workspace to set the java.home variable? \n java.home: ${javaHome}`, disallow, allow).then(async selection => {
138+
if (selection === allow) {
139+
globalState.update(key, true);
140+
} else if (selection === disallow) {
141+
globalState.update(key, false);
142+
await workspace.getConfiguration().update('java.home', undefined, ConfigurationTarget.Workspace);
143+
}
144+
});
145+
isVerified = globalState.get(key);
146+
}
147+
}
148+
const vmargs = workspace.getConfiguration().inspect('java.jdt.ls.vmargs').workspaceValue;
149+
if (vmargs !== undefined) {
150+
const agentFlag = getJavaagentFlag(vmargs);
151+
if (agentFlag !== null) {
152+
const keyVmargs = getKey(IS_WORKSPACE_VMARGS_ALLOWED, context.storagePath, vmargs);
153+
const vmargsVerified = globalState.get(keyVmargs);
154+
if (vmargsVerified === undefined || vmargsVerified === null) {
155+
await window.showErrorMessage(`Security Warning! The java.jdt.ls.vmargs variable defined in ${env.appName} settings includes the (${agentFlag}) javagent preference. Do you allow it to be used?`, disallow, allow).then(async selection => {
156+
if (selection === allow) {
157+
globalState.update(keyVmargs, true);
158+
} else if (selection === disallow) {
159+
globalState.update(keyVmargs, false);
160+
await workspace.getConfiguration().update('java.jdt.ls.vmargs', undefined, ConfigurationTarget.Workspace);
161+
}
162+
});
163+
}
164+
}
165+
}
166+
if (isVerified) {
167+
return javaHome;
168+
} else {
169+
return workspace.getConfiguration().inspect<string>('java.home').globalValue;
170+
}
171+
}
172+
173+
export function getKey(prefix, storagePath, value) {
174+
const workspacePath = path.resolve(storagePath + '/jdt_ws');
175+
if (workspace.name !== undefined) {
176+
return `${prefix}::${workspacePath}::${value}`;
177+
}
178+
else {
179+
return `${prefix}::${value}`;
180+
}
181+
}
182+
183+
export function getJavaagentFlag(vmargs) {
184+
const javaagent = '-javaagent:';
185+
const args = vmargs.split(" ");
186+
let agentFlag = null;
187+
for (const arg of args) {
188+
if (arg.startsWith(javaagent)) {
189+
agentFlag = arg.substring(javaagent.length);
190+
break;
191+
}
192+
}
193+
return agentFlag;
194+
}

0 commit comments

Comments
 (0)