| /*--------------------------------------------------------- |
| * Copyright 2021 The Go Authors. All rights reserved. |
| * Licensed under the MIT License. See LICENSE in the project root for license information. |
| *--------------------------------------------------------*/ |
| |
| import vscode = require('vscode'); |
| import { getFromWorkspaceState, updateWorkspaceState } from './stateUtils'; |
| |
| const WORKSPACE_IS_TRUSTED_KEY = 'WORKSPACE_IS_TRUSTED_KEY'; |
| const SECURITY_SENSITIVE_CONFIG: string[] = [ |
| 'goroot', 'gopath', 'toolsGopath', 'alternateTools', 'inferGopath' |
| ]; |
| |
| // 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) { |
| const isTrusted = getFromWorkspaceState(WORKSPACE_IS_TRUSTED_KEY, false); |
| if (isTrusted !== defaultConfig.workspaceIsTrusted) { |
| defaultConfig.toggleWorkspaceIsTrusted(); |
| } |
| ctx.subscriptions.push( |
| vscode.commands.registerCommand('go.workspace.isTrusted.toggle', 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() { |
| 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<T>(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; |
| } |
| } |
| |
| const defaultConfig = 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) { |
| if (typeof prop === 'string' && prop !== '_wrapped') { |
| 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) => { |
| return getConfig('go', uri); |
| }; |
| |
| // getGoplsConfig returns the user's gopls configuration. |
| export function getGoplsConfig(uri?: vscode.Uri) { |
| return getConfig('gopls', uri); |
| } |
| |
| function getConfig(section: string, uri?: vscode.Uri) { |
| if (!uri) { |
| if (vscode.window.activeTextEditor) { |
| uri = vscode.window.activeTextEditor.document.uri; |
| } else { |
| uri = null; |
| } |
| } |
| return defaultConfig.get(section, uri); |
| } |