Skip to content

Commit

Permalink
Warn about sensible java preferences in project settings
Browse files Browse the repository at this point in the history
Signed-off-by: Snjezana Peco <[email protected]>
  • Loading branch information
snjeza authored and fbricon committed Dec 10, 2019
1 parent 6a7db6e commit dc03e74
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 25 deletions.
11 changes: 6 additions & 5 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export function activate(context: ExtensionContext) {
}
let logfile = path.resolve(storagePath + '/lsp4xml.log');

return requirements.resolveRequirements().catch(error => {
return requirements.resolveRequirements(context).catch(error => {
//show error
window.showErrorMessage(error.message, error.label).then((selection) => {
if (error.label && error.label === selection && error.openUrl) {
Expand All @@ -60,7 +60,7 @@ export function activate(context: ExtensionContext) {
revealOutputChannelOn: RevealOutputChannelOn.Never,
//wrap with key 'settings' so it can be handled same a DidChangeConfiguration
initializationOptions: {
settings: getXMLSettings(),
settings: getXMLSettings(requirements.java_home),
extendedClientCapabilities: {
codeLens: {
codeLensKind: {
Expand All @@ -78,14 +78,14 @@ export function activate(context: ExtensionContext) {
middleware: {
workspace: {
didChangeConfiguration: () => {
languageClient.sendNotification(DidChangeConfigurationNotification.type, { settings: getXMLSettings() });
languageClient.sendNotification(DidChangeConfigurationNotification.type, { settings: getXMLSettings(requirements.java_home) });
onConfigurationChange();
}
}
}
}

let serverOptions = prepareExecutable(requirements, collectXmlJavaExtensions(extensions.all));
let serverOptions = prepareExecutable(requirements, collectXmlJavaExtensions(extensions.all), context);
let languageClient = new LanguageClient('xml', 'XML Support', serverOptions, clientOptions);
let toDispose = context.subscriptions;
let disposable = languageClient.start();
Expand Down Expand Up @@ -135,7 +135,7 @@ export function activate(context: ExtensionContext) {
* 'xml': {...}
* }
*/
function getXMLSettings(): JSON {
function getXMLSettings(javaHome: string): JSON {
let configXML = workspace.getConfiguration().get('xml');
let xml;
if (!configXML) { //Set default preferences if not provided
Expand Down Expand Up @@ -164,6 +164,7 @@ export function activate(context: ExtensionContext) {
}
xml['xml']['logs']['file'] = logfile;
xml['xml']['useCache'] = true;
xml['xml']['java']['home'] = javaHome;
return xml;
}
}
Expand Down
29 changes: 24 additions & 5 deletions src/javaServerStarter.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,52 @@
import { workspace } from 'vscode'
import { workspace, ExtensionContext } from 'vscode'
import { Executable, ExecutableOptions } from 'vscode-languageclient';
import { RequirementsData } from './requirements';
import * as os from 'os';
import * as path from 'path';
import { xmlServerVmargs, getJavaagentFlag, getKey, IS_WORKSPACE_VMARGS_XML_ALLOWED, getXMLConfiguration } from './settings';
const glob = require('glob');

declare var v8debug;

const DEBUG = (typeof v8debug === 'object') || startedInDebugMode();

export function prepareExecutable(requirements: RequirementsData, xmlJavaExtensions: string[]): Executable {
export function prepareExecutable(requirements: RequirementsData, xmlJavaExtensions: string[], context: ExtensionContext): Executable {
let executable: Executable = Object.create(null);
let options: ExecutableOptions = Object.create(null);
options.env = process.env;
options.stdio = 'pipe';
executable.options = options;
executable.command = path.resolve(requirements.java_home + '/bin/java');
executable.args = prepareParams(requirements, xmlJavaExtensions);
executable.args = prepareParams(requirements, xmlJavaExtensions, context);
return executable;
}

function prepareParams(requirements: RequirementsData, xmlJavaExtensions: string[]): string[] {
function prepareParams(requirements: RequirementsData, xmlJavaExtensions: string[], context: ExtensionContext): string[] {
let params: string[] = [];
if (DEBUG) {
params.push('-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=1054,quiet=y');
// suspend=y is the default. Use this form if you need to debug the server startup code:
//params.push('-agentlib:jdwp=transport=dt_socket,server=y,address=1054');
}
let vmargs = workspace.getConfiguration("xml").get("server.vmargs", '');
let vmargsCheck = workspace.getConfiguration().inspect(xmlServerVmargs).workspaceValue;
if (vmargsCheck !== undefined) {
const agentFlag = getJavaagentFlag(vmargsCheck);
if (agentFlag !== null) {
const keyVmargs = getKey(IS_WORKSPACE_VMARGS_XML_ALLOWED, context.storagePath, vmargsCheck);
const key = context.globalState.get(keyVmargs);
if (key !== true) {
vmargsCheck = workspace.getConfiguration().inspect(xmlServerVmargs).globalValue;
}
}
} else {
vmargsCheck = getXMLConfiguration().get('')
}
let vmargs;
if (vmargsCheck !== undefined) {
vmargs = vmargsCheck + '';
} else {
vmargs = '';
}
if (os.platform() == 'win32') {
const watchParentProcess = '-DwatchParentProcess=';
if (vmargs.indexOf(watchParentProcess) < 0) {
Expand Down
84 changes: 70 additions & 14 deletions src/requirements.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
'use strict';

import { workspace, Uri } from 'vscode';
import { window, workspace, Uri, ExtensionContext, ConfigurationTarget, env } from 'vscode';
import * as cp from 'child_process';
import * as path from 'path';
import { IS_WORKSPACE_JDK_XML_ALLOWED, getKey, IS_WORKSPACE_VMARGS_XML_ALLOWED, getJavaagentFlag, IS_WORKSPACE_JDK_ALLOWED, getXMLConfiguration, getJavaConfiguration, xmlServerVmargs } from './settings';

const pathExists = require('path-exists');
const expandHomeDir = require('expand-home-dir');
Expand All @@ -28,21 +29,20 @@ interface ErrorData {
* if any of the requirements fails to resolve.
*
*/
export async function resolveRequirements(): Promise<RequirementsData> {
const javaHome = await checkJavaRuntime();
export async function resolveRequirements(context: ExtensionContext): Promise<RequirementsData> {
const javaHome = await checkJavaRuntime(context);
const javaVersion = await checkJavaVersion(javaHome);
return Promise.resolve({ 'java_home': javaHome, 'java_version': javaVersion});
}

function checkJavaRuntime(): Promise<string> {
return new Promise((resolve, reject) => {
function checkJavaRuntime(context: ExtensionContext): Promise<string> {
return new Promise(async (resolve, reject) => {
let source : string;
let javaHome: string = readXMLJavaHomeConfig();

let javaHome = await readXMLJavaHomeConfig(context);
if (javaHome) {
source = 'The xml.java.home variable defined in VS Code settings';
} else {
javaHome = readJavaHomeConfig();
javaHome = await readJavaHomeConfig(context);
if (javaHome) {
source = 'The java.home variable defined in VS Code settings';
} else {
Expand Down Expand Up @@ -77,14 +77,70 @@ function checkJavaRuntime(): Promise<string> {
});
}


function readXMLJavaHomeConfig() : string {
return workspace.getConfiguration('xml').java.home;
export async function readXMLJavaHomeConfig(context: ExtensionContext) {
const xmlJavaHome = 'xml.java.home';
let javaHome = workspace.getConfiguration().inspect<string>(xmlJavaHome).workspaceValue;
let isVerified = javaHome === undefined || javaHome === null;
if (isVerified) {
javaHome = getXMLConfiguration().get("java.home");
}
const allow = 'Allow';
const disallow = 'Disallow';
const key = getKey(IS_WORKSPACE_JDK_XML_ALLOWED, context.storagePath, javaHome);
const globalState = context.globalState;
if (!isVerified) {
isVerified = globalState.get(key);
if (isVerified === undefined) {
await window.showErrorMessage(`Security Warning! Do you allow this workspace to set the ${xmlJavaHome} variable? \n ${xmlJavaHome}: ${javaHome}`, disallow, allow).then(async selection => {
if (selection === allow) {
globalState.update(key, true);
} else if (selection === disallow) {
globalState.update(key, false);
await workspace.getConfiguration().update(xmlJavaHome, undefined, ConfigurationTarget.Workspace);
}
});
isVerified = globalState.get(key);
}
}
const vmargs = workspace.getConfiguration().inspect(xmlServerVmargs).workspaceValue;
if (vmargs !== undefined) {
const agentFlag = getJavaagentFlag(vmargs);
if (agentFlag !== null) {
const keyVmargs = getKey(IS_WORKSPACE_VMARGS_XML_ALLOWED, context.storagePath, vmargs);
const vmargsVerified = globalState.get(keyVmargs);
if (vmargsVerified === undefined || vmargsVerified === null) {
await window.showErrorMessage(`Security Warning! The ${xmlServerVmargs} variable defined in ${env.appName} settings includes the (${agentFlag}) javagent preference. Do you allow it to be used?`, disallow, allow).then(async selection => {
if (selection === allow) {
globalState.update(keyVmargs, true);
} else if (selection === disallow) {
globalState.update(keyVmargs, false);
await workspace.getConfiguration().update(xmlServerVmargs, undefined, ConfigurationTarget.Workspace);
}
});
}
}
}
if (isVerified) {
return javaHome;
} else {
return workspace.getConfiguration().inspect<string>('xml.java.home').globalValue;
}
}

function readJavaHomeConfig() : string {
const config = workspace.getConfiguration();
return config.get<string>('java.home',null);
async function readJavaHomeConfig(context: ExtensionContext) {
let javaHome = workspace.getConfiguration().inspect<string>('java.home').workspaceValue;
let isVerified = javaHome === undefined || javaHome === null;
if (isVerified) {
javaHome = getJavaConfiguration().get('home');
}
const key = getKey(IS_WORKSPACE_JDK_ALLOWED, context.storagePath, javaHome);
const globalState = context.globalState;
isVerified = globalState.get(key);
if (isVerified) {
return javaHome;
} else {
return workspace.getConfiguration().inspect<string>('java.home').globalValue;
}
}

function checkJavaVersion(java_home: string): Promise<number> {
Expand Down
29 changes: 28 additions & 1 deletion src/settings.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { WorkspaceConfiguration, workspace, window, commands, extensions, Extension } from "vscode";
import { ScopeInfo } from "./extension";
import * as path from 'path';

let vmArgsCache;
let ignoreAutoCloseTags = false;
Expand All @@ -10,7 +11,10 @@ let oldJavaConfig: WorkspaceConfiguration = getJavaConfiguration();
const restartButton = 'Restart Now';
const ignoreButton = 'Ignore'
const restartId = "workbench.action.reloadWindow";

export const IS_WORKSPACE_JDK_ALLOWED = "java.ls.isJdkAllowed";
export const IS_WORKSPACE_JDK_XML_ALLOWED = "java.ls.isJdkXmlAllowed";
export const IS_WORKSPACE_VMARGS_XML_ALLOWED = "java.ls.isVmargsXmlAllowed";
export const xmlServerVmargs = 'xml.server.vmargs';

export function getXMLConfiguration(): WorkspaceConfiguration {
return getXConfiguration("xml")
Expand Down Expand Up @@ -143,4 +147,27 @@ function getScopeLevel(configurationKey : string, key : string) : ScopeInfo{
}
let scopeInfo : ScopeInfo = {"scope": scope, "configurationTarget": configurationTarget};
return scopeInfo;
}

export function getKey(prefix, storagePath, value) {
const workspacePath = path.resolve(storagePath + '/jdt_ws');
if (workspace.name !== undefined) {
return `${prefix}::${workspacePath}::${value}`;
}
else {
return `${prefix}::${value}`;
}
}

export function getJavaagentFlag(vmargs) {
const javaagent = '-javaagent:';
const args = vmargs.split(" ");
let agentFlag = null;
for (const arg of args) {
if (arg.startsWith(javaagent)) {
agentFlag = arg.substring(javaagent.length);
break;
}
}
return agentFlag;
}

0 comments on commit dc03e74

Please sign in to comment.