src/config: remove WrappedConfiguration in favor of trusted workspace
Most of the code in config.ts was introduced as a stop-gap
before trusted workspace APIs became available. It's now
available, and our extension requires newer vscode engines.
It's time to remove this old hack. Bye!
Change-Id: Ic8e74178f09669f29490237bda0d745f867e7176
Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/347690
Trust: Hyang-Ah Hana Kim <hyangah@gmail.com>
Trust: Suzy Mueller <suzmue@golang.org>
Run-TryBot: Hyang-Ah Hana Kim <hyangah@gmail.com>
TryBot-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Suzy Mueller <suzmue@golang.org>
diff --git a/src/config.ts b/src/config.ts
index 56ba585..28efd4d 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -5,172 +5,6 @@
*--------------------------------------------------------*/
import vscode = require('vscode');
-import { getFromWorkspaceState, updateWorkspaceState } from './stateUtils';
-
-const WORKSPACE_IS_TRUSTED_KEY = 'WORKSPACE_IS_TRUSTED_KEY';
-const SECURITY_SENSITIVE_CONFIG: string[] = [
- 'alternateTools',
- 'gopath',
- 'goroot',
- 'inferGopath',
- 'toolsGopath',
- 'toolsEnvVars'
-];
-
-// Set true only if the vscode is the recent version that has the workspace trust API AND
-// if the security.workspace.trust is enabled. Change of this configuration requires restart
-// of VSCode, so we don't need to set up the configuration change listener.
-// TODO(hyangah): remove this and Configuration & WrappedConfiguration when we update
-// our extension to require 2021 June VSCode engine.
-const isVscodeWorkspaceTrustAPIAvailable =
- 'boolean' === typeof (vscode.workspace as any).isTrusted &&
- vscode.workspace.getConfiguration('security.workspace.trust')?.get('enabled') === true;
-
-// Initialize the singleton defaultConfig and register related commands.
-// Prompt if workspace configuration was found but had to be ignored until
-// the user has to explicitly opt in to trust the workspace.
-export async function initConfig(ctx: vscode.ExtensionContext) {
- ctx.subscriptions.push(vscode.commands.registerCommand('go.workspace.isTrusted.toggle', toggleWorkspaceIsTrusted));
-
- if (isVscodeWorkspaceTrustAPIAvailable) {
- return; // let vscode handle configuration management.
- }
-
- const isTrusted = getFromWorkspaceState(WORKSPACE_IS_TRUSTED_KEY, false);
- if (isTrusted !== defaultConfig.workspaceIsTrusted()) {
- defaultConfig.toggleWorkspaceIsTrusted();
- }
-
- if (isTrusted) {
- return;
- }
- const ignored = ignoredWorkspaceConfig(vscode.workspace.getConfiguration('go'), SECURITY_SENSITIVE_CONFIG);
- if (ignored.length === 0) {
- return;
- }
- const ignoredSettings = ignored.map((x) => `"go.${x}"`).join(',');
- const val = await vscode.window.showWarningMessage(
- `Some workspace/folder-level settings (${ignoredSettings}) from the untrusted workspace are disabled ` +
- 'by default. If this workspace is trusted, explicitly enable the workspace/folder-level settings ' +
- 'by running the "Go: Toggle Workspace Trust Flag" command.',
- 'OK',
- 'Trust This Workspace',
- 'More Info'
- );
- switch (val) {
- case 'Trust This Workspace':
- await toggleWorkspaceIsTrusted();
- break;
- case 'More Info':
- vscode.env.openExternal(
- vscode.Uri.parse('https://github.com/golang/vscode-go/blob/master/docs/settings.md#security')
- );
- break;
- default:
- break;
- }
-}
-
-function ignoredWorkspaceConfig(cfg: vscode.WorkspaceConfiguration, keys: string[]) {
- return keys.filter((key) => {
- const inspect = cfg.inspect(key);
- return inspect.workspaceValue !== undefined || inspect.workspaceFolderValue !== undefined;
- });
-}
-
-async function toggleWorkspaceIsTrusted() {
- if (isVscodeWorkspaceTrustAPIAvailable) {
- vscode.commands.executeCommand('workbench.action.manageTrust');
- return;
- }
- const v = defaultConfig.toggleWorkspaceIsTrusted();
- await updateWorkspaceState(WORKSPACE_IS_TRUSTED_KEY, v);
-}
-
-// Go extension configuration for a workspace.
-export class Configuration {
- constructor(private _workspaceIsTrusted = false, private getConfiguration = vscode.workspace.getConfiguration) {}
-
- public toggleWorkspaceIsTrusted() {
- this._workspaceIsTrusted = !this._workspaceIsTrusted;
- return this._workspaceIsTrusted;
- }
-
- // returns a Proxied vscode.WorkspaceConfiguration, which prevents
- // from using the workspace configuration if the workspace is untrusted.
- public get(section: string, uri?: vscode.Uri): vscode.WorkspaceConfiguration {
- const cfg = this.getConfiguration(section, uri);
- if (section !== 'go' || this._workspaceIsTrusted) {
- return cfg;
- }
- return new WrappedConfiguration(cfg);
- }
-
- public workspaceIsTrusted(): boolean {
- return this._workspaceIsTrusted;
- }
-}
-
-class vscodeConfiguration {
- public toggleWorkspaceIsTrusted() {
- /* no-op */
- }
- public get(section: string, uri?: vscode.Uri): vscode.WorkspaceConfiguration {
- return vscode.workspace.getConfiguration(section, uri);
- }
- public workspaceIsTrusted(): boolean {
- return !!(vscode.workspace as any).isTrusted;
- }
-}
-
-const defaultConfig = isVscodeWorkspaceTrustAPIAvailable ? new vscodeConfiguration() : new Configuration();
-
-// Returns the workspace Configuration used by the extension.
-export function DefaultConfig() {
- return defaultConfig;
-}
-
-// wrappedConfiguration wraps vscode.WorkspaceConfiguration.
-class WrappedConfiguration implements vscode.WorkspaceConfiguration {
- constructor(private readonly _wrapped: vscode.WorkspaceConfiguration) {
- // set getters for direct setting access (e.g. cfg.gopath), but don't overwrite _wrapped.
- const desc = Object.getOwnPropertyDescriptors(_wrapped);
- for (const prop in desc) {
- // TODO(hyangah): find a better way to exclude WrappedConfiguration's members.
- // These methods are defined by WrappedConfiguration.
- if (typeof prop === 'string' && !['get', 'has', 'inspect', 'update', '_wrapped'].includes(prop)) {
- const d = desc[prop];
- if (SECURITY_SENSITIVE_CONFIG.includes(prop)) {
- const inspect = this._wrapped.inspect(prop);
- d.value = inspect.globalValue ?? inspect.defaultValue;
- }
- Object.defineProperty(this, prop, desc[prop]);
- }
- }
- }
-
- public get(section: any, defaultValue?: any) {
- if (SECURITY_SENSITIVE_CONFIG.includes(section)) {
- const inspect = this._wrapped.inspect(section);
- return inspect.globalValue ?? defaultValue ?? inspect.defaultValue;
- }
- return this._wrapped.get(section, defaultValue);
- }
- public has(section: string) {
- return this._wrapped.has(section);
- }
- public inspect<T>(section: string) {
- return this._wrapped.inspect<T>(section);
- }
- public update(
- section: string,
- value: any,
- configurationTarget?: boolean | vscode.ConfigurationTarget,
- overrideInLanguage?: boolean
- ): Thenable<void> {
- return this._wrapped.update(section, value, configurationTarget, overrideInLanguage);
- }
-}
// getGoConfig is declared as an exported const rather than a function, so it can be stubbbed in testing.
export const getGoConfig = (uri?: vscode.Uri) => {
@@ -190,7 +24,7 @@
uri = null;
}
}
- return defaultConfig.get(section, uri);
+ return vscode.workspace.getConfiguration(section, uri);
}
// True if the extension is running in known cloud-based IDEs.
diff --git a/src/goMain.ts b/src/goMain.ts
index 941b693..b6f8d2a 100644
--- a/src/goMain.ts
+++ b/src/goMain.ts
@@ -9,7 +9,7 @@
'use strict';
import * as path from 'path';
-import { getGoConfig, getGoplsConfig, initConfig, IsInCloudIDE } from './config';
+import { getGoConfig, getGoplsConfig, IsInCloudIDE } from './config';
import { browsePackages } from './goBrowsePackage';
import { buildCode } from './goBuild';
import { check, notifyIfGeneratedFile, removeTestStatus } from './goCheck';
@@ -138,8 +138,6 @@
setWorkspaceState(ctx.workspaceState);
setEnvironmentVariableCollection(ctx.environmentVariableCollection);
- await initConfig(ctx);
-
const cfg = getGoConfig();
setLogConfig(cfg['logging']);
diff --git a/src/util.ts b/src/util.ts
index 2a35ddf..35eab17 100644
--- a/src/util.ts
+++ b/src/util.ts
@@ -12,7 +12,7 @@
import util = require('util');
import vscode = require('vscode');
import { NearestNeighborDict, Node } from './avlTree';
-import { DefaultConfig, getGoConfig } from './config';
+import { getGoConfig } from './config';
import { extensionId } from './const';
import { toolExecutionEnvironment } from './goEnv';
import { languageClient } from './goLanguageServer';
@@ -495,7 +495,7 @@
return toolsGopathForWorkspace;
}
- if (DefaultConfig().workspaceIsTrusted() === false) {
+ if (!vscode.workspace.isTrusted) {
return toolsGopathForWorkspace;
}
diff --git a/test/integration/config.test.ts b/test/integration/config.test.ts
deleted file mode 100644
index 525b42d..0000000
--- a/test/integration/config.test.ts
+++ /dev/null
@@ -1,70 +0,0 @@
-/* eslint-disable @typescript-eslint/no-unused-vars */
-/* eslint-disable @typescript-eslint/no-explicit-any */
-/*---------------------------------------------------------
- * Copyright 2021 The Go Authors. All rights reserved.
- * Licensed under the MIT License. See LICENSE in the project root for license information.
- *--------------------------------------------------------*/
-
-'use strict';
-
-import * as assert from 'assert';
-import { Configuration } from '../../src/config';
-import { MockCfg } from '../mocks/MockCfg';
-
-suite('GoConfiguration Tests', () => {
- function check(trusted: boolean, workspaceConfig: { [key: string]: any }, key: string, expected: any) {
- const getConfigurationFn = (section: string) => new MockCfg(workspaceConfig);
- const cfg = new Configuration(trusted, getConfigurationFn).get('go');
-
- const got0 = JSON.stringify(cfg.get(key));
- const got1 = JSON.stringify(cfg[key]);
- const want = JSON.stringify(expected);
- assert.strictEqual(got0, want, `cfg.get(${key}) = ${got0}, want ${want}`);
- assert.strictEqual(got1, want, `cfg[${key}] = ${got1}, want ${want}`);
- }
-
- test('trusted workspace accepts all workspace settings', () => {
- check(true, { goroot: 'goroot_val' }, 'goroot', 'goroot_val');
- check(true, { gopath: 'gopath_val' }, 'gopath', 'gopath_val');
- check(true, { toolsGopath: 'toolsGopath_val' }, 'toolsGopath', 'toolsGopath_val');
- check(true, { alternateTools: { go: 'foo' } }, 'alternateTools', { go: 'foo' });
- check(true, { inferGopath: true }, 'inferGopath', true);
-
- check(true, { buildFlags: ['-v'] }, 'buildFlags', ['-v']);
- check(true, { languageServerFlags: ['-rpc.trace'] }, 'languageServerFlags', ['-rpc.trace']);
- });
-
- test('untrusted workspace ignores dangerous settings', () => {
- check(false, { goroot: 'goroot_val' }, 'goroot', null);
- check(false, { gopath: 'gopath_val' }, 'gopath', null);
- check(false, { toolsGopath: 'toolsGopath_val' }, 'toolsGopath', null);
- check(false, { alternateTools: { go: 'foo' } }, 'alternateTools', {});
- check(false, { inferGopath: true }, 'inferGopath', false);
-
- check(false, { buildFlags: ['-v'] }, 'buildFlags', ['-v']);
- check(false, { languageServerFlags: ['-rpc.trace'] }, 'languageServerFlags', ['-rpc.trace']);
- });
-
- function checkGopls(trusted: boolean, workspaceConfig: { [key: string]: any }, key: string, expected: any) {
- const getConfigurationFn = (section: string) => new MockCfg(workspaceConfig);
- const cfg = new Configuration(trusted, getConfigurationFn).get('gopls');
-
- const got0 = JSON.stringify(cfg.get(key));
- const got1 = JSON.stringify(cfg[key]);
- const want = JSON.stringify(expected);
- assert.strictEqual(got0, want, `cfg.get(${key}) = ${got0}, want ${want}`);
- assert.strictEqual(got1, want, `cfg[${key}] = ${got1}, want ${want}`);
- }
-
- test('trusted workspace (gopls settings) accepts all settings', () => {
- // unaffected settings.
- checkGopls(true, { buildFlags: '-v' }, 'buildFlags', '-v');
- checkGopls(true, { env: { GOBIN: 'foo' } }, 'env', { GOBIN: 'foo' });
- });
-
- test('untrusted workspace (gopls settings) ignores dangerous settings', () => {
- // unaffected settings
- checkGopls(false, { buildFlags: '-v' }, 'buildFlags', '-v');
- checkGopls(false, { env: { GOBIN: 'foo' } }, 'env', { GOBIN: 'foo' });
- });
-});