CVE-2021-28793: Incorrect Authorization (CWE-863)
value: vscode-restructuredtext before 146.0.0 contains an incorrect access control vulnerability, where a crafted project folder could execute arbitrary binaries via crafted workspace configuration.
CWE description: The product performs an authorization check when an actor attempts to access a resource or perform an action, but it does not correctly perform the check. This allows attackers to bypass intended access restrictions.
CVSS v3 base score: 9.8
Published: 2021-04-20T13:15Z
Repository: https://github.com/vscode-restructuredtext/vscode-restructuredtext
Language: TypeScript
Files changed: package-lock.json, package.json, src/config.ts, src/extension.ts, src/features/utils/configuration.ts, src/stateUtils.ts
Commit date: 2021-04-05 18:39:11 -0400
Commit message: Patch to address workspace security issue.

Diff: diff --git a/package-lock.json b/package-lock.json
index 4235dee..ae80bf4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1285,22 +1285,36 @@
         "readable-stream": "^2.0.0",
         "stream-shift": "^1.0.0"
       }
     },
     "elliptic": {
-      "version": "6.5.3",
-      "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz",
-      "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==",
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz",
+      "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==",
       "dev": true,
       "requires": {
-        "bn.js": "^4.4.0",
-        "brorand": "^1.0.1",
+        "bn.js": "^4.11.9",
+        "brorand": "^1.1.0",
         "hash.js": "^1.0.0",
-        "hmac-drbg": "^1.0.0",
-        "inherits": "^2.0.1",
-        "minimalistic-assert": "^1.0.0",
-        "minimalistic-crypto-utils": "^1.0.0"
+        "hmac-drbg": "^1.0.1",
+        "inherits": "^2.0.4",
+        "minimalistic-assert": "^1.0.1",
+        "minimalistic-crypto-utils": "^1.0.1"
+      },
+      "dependencies": {
+        "bn.js": {
+          "version": "4.12.0",
+          "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+          "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
+          "dev": true
+        },
+        "inherits": {
+          "version": "2.0.4",
+          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+          "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+          "dev": true
+        }
       }
     },
     "emoji-regex": {
       "version": "7.0.3",
       "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
diff --git a/package.json b/package.json
index 9ecc20d..7263512 100644
--- a/package.json
+++ b/package.json
@@ -238,10 +238,15 @@
       },
       {
         "command": "restructuredtext.preview.toggleLock",
         "title": "%restructuredtext.preview.toggleLock.title%",
         "category": "reStructuredText"
+      },
+      {
+        "command": "restructuredtext.workspace.isTrusted.toggle",
+        "title": "reStructuredText: Toggle Workspace Trust Flag",
+        "description": "Toggle the workspace trust flag. Workspace settings that determine tool locations are disabled by default in untrusted workspaces."
       }
     ],
     "menus": {
       "editor/title": [
         {
diff --git a/src/config.ts b/src/config.ts
new file mode 100644
index 0000000..9ef36d0
--- /dev/null
+++ b/src/config.ts
@@ -0,0 +1,151 @@
+/* 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.
+ *--------------------------------------------------------*/
+
+import vscode = require('vscode');
+import { getFromWorkspaceState, updateWorkspaceState } from './stateUtils';
+
+const WORKSPACE_IS_TRUSTED_KEY = 'WORKSPACE_IS_TRUSTED_KEY';
+const SECURITY_SENSITIVE_CONFIG: string[] = [
+    'sphinxBuildPath',
+    'linter.executablePath'
+];
+
+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('restructuredtext.workspace.isTrusted.toggle', toggleWorkspaceIsTrusted));
+
+    if (isTrusted) {
+        return;
+    }
+    const ignored = ignoredWorkspaceConfig(vscode.workspace.getConfiguration('restructuredtext'), SECURITY_SENSITIVE_CONFIG);
+    if (ignored.length === 0) {
+        return;
+    }
+    const ignoredSettings = ignored.map((x) => `"restructuredtext.${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 "reStructuredText: 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://docs.restructuredtext.net/articles/configuration.html#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);
+}
+
+// reStructuredText 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 !== 'restructuredtext' || 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.
+// tslint:disable-next-line: max-classes-per-file
+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);
+    }
+}
+
+export 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);
+}
+
+// True if the extension is running in known cloud-based IDEs.
+export const IsInCloudIDE = process.env.CLOUD_SHELL === 'true' || process.env.CODESPACES === 'true';
diff --git a/src/extension.ts b/src/extension.ts
index 0e1d130..884c366 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -7,60 +7,68 @@ import * as vscode from 'vscode';
 import { CommandManager } from './commandManager';
 import * as commands from './commands/index';
 import { RSTContentProvider } from './features/previewContentProvider';
 import { RSTPreviewManager } from './features/previewManager';
 import { Logger } from './logger';
-import { ExtensionContentSecurityPolicyArbiter, PreviewSecuritySelector } from './security';
 import { Python } from './python';
 import { RSTEngine } from './rstEngine';
+import { ExtensionContentSecurityPolicyArbiter, PreviewSecuritySelector } from './security';
 
 import * as listEditing from './features/listEditing';
+import { rstDocumentSymbolProvider } from './features/rstDocumentSymbolProvider';
 import RstLintingProvider from './features/rstLinter';
 import { underline } from './features/underline';
 import { Configuration } from './features/utils/configuration';
 import RstTransformerStatus from './features/utils/statusBar';
 import * as RstLanguageServer from './rstLsp/extension';
-import { rstDocumentSymbolProvider } from './features/rstDocumentSymbolProvider';
+import { setGlobalState, setWorkspaceState } from './stateUtils';
+import { initConfig } from './config';
 
-let extensionPath = "";
+let extensionPath = '';
 
 export function getExtensionPath(): string {
-	return extensionPath;
+    return extensionPath;
 }
 
 export async function activate(context: vscode.ExtensionContext): Promise<{ initializationFinished: Promise<void> }> {
-	extensionPath = context.extensionPath;
 
-	const logger = new Logger();
-	logger.log('Please visit https://docs.restructuredtext.net to learn how to configure the extension.');
+    setGlobalState(context.globalState);
+    setWorkspaceState(context.workspaceState);
+
+    await initConfig(context);
+
+    extensionPath = context.extensionPath;
 
-	const conflicting = Configuration.getConflictingExtensions();
-	for (const element of conflicting) {
-		const found = vscode.extensions.getExtension(element);
-		if (found) {
-			const message = `Found conflicting extension ${element}. Please uninstall it.`;
+    const logger = new Logger();
+    logger.log('Please visit https://docs.restructuredtext.net to learn how to configure the extension.');
+
+    const conflicting = Configuration.getConflictingExtensions();
+    for (const element of conflicting) {
+        const found = vscode.extensions.getExtension(element);
+        if (found) {
+            const message = `Found conflicting extension ${element}. Please uninstall it.`;
             logger.log(message);
-			vscode.window.showErrorMessage(message);
-		}
-	}
+            vscode.window.showErrorMessage(message);
+        }
+    }
 
     await logPlatform(logger);
-	const disableLsp = Configuration.getLanguageServerDisabled();
+    const disableLsp = Configuration.getLanguageServerDisabled();
 
     const python: Python = new Python(logger);
 
-	// activate language services
-	const rstLspPromise = RstLanguageServer.activate(context, logger, disableLsp, python);
+    // activate language services
+    const rstLspPromise = RstLanguageServer.activate(context, logger, disableLsp, python);
 
     // Section creation support.
-	context.subscriptions.push(
-		vscode.commands.registerTextEditorCommand('restructuredtext.features.underline.underline', underline),
-		vscode.commands.registerTextEditorCommand('restructuredtext.features.underline.underlineReverse',
-			(textEditor, edit) => underline(textEditor, edit, true)),
-	);
+    context.subscriptions.push(
+        vscode.commands.registerTextEditorCommand('restructuredtext.features.underline.underline', underline),
+        vscode.commands.registerTextEditorCommand('restructuredtext.features.underline.underlineReverse',
+            (textEditor, edit) => underline(textEditor, edit, true)),
+    );
 
-	// Linter support
+    // Linter support
     if (!Configuration.getLinterDisabled()) {
         const linter = new RstLintingProvider(logger, python);
         linter.activate(context.subscriptions);
     }
 
@@ -74,11 +82,11 @@ export async function activate(context: vscode.ExtensionContext): Promise<{ init
                 status.reset, status),
         );
 
         vscode.window.onDidChangeActiveTextEditor(status.update, status, context.subscriptions);
         status.update();
-	
+
         const cspArbiter = new ExtensionContentSecurityPolicyArbiter(context.globalState, context.workspaceState);
 
         const engine: RSTEngine = new RSTEngine(python, logger, status);
 
         const contentProvider = new RSTContentProvider(context, cspArbiter, engine, logger);
@@ -102,36 +110,36 @@ export async function activate(context: vscode.ExtensionContext): Promise<{ init
         context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(() => {
             logger.updateConfiguration();
             previewManager.updateConfiguration();
         }));
     }
-    
+
     // DocumentSymbolProvider Demo, for Outline View Test
     let disposableRstDSP = vscode.languages.registerDocumentSymbolProvider(
         { scheme: 'file', language: 'restructuredtext' }, new rstDocumentSymbolProvider()
     );
     context.subscriptions.push(disposableRstDSP);
 
     listEditing.activate(context);
 
-	return {
-		initializationFinished: Promise.all([rstLspPromise])
-			.then((promiseResult) => {
-				// This promise resolver simply swallows the result of Promise.all.
-				// When we decide we want to expose this level of detail
-				// to other extensions then we will design that return type and implement it here.
-			}),
-	};
+    return {
+        initializationFinished: Promise.all([rstLspPromise])
+            .then((promiseResult) => {
+                // This promise resolver simply swallows the result of Promise.all.
+                // When we decide we want to expose this level of detail
+                // to other extensions then we will design that return type and implement it here.
+            }),
+    };
 }
 
 async function logPlatform(logger: Logger): Promise<void> {
-	const os = require('os');
+    const os = require('os');
     let platform = os.platform();
     logger.log(`OS is ${platform}`);
-	if (platform === 'darwin' || platform === 'win32') {
-		return;
-	}
+    if (platform === 'darwin' || platform === 'win32') {
+        return;
+    }
 
     const osInfo = require('linux-os-info');
     const result = await osInfo();
     const dist = result.id;
     logger.log(`dist: ${dist}`);
diff --git a/src/features/utils/configuration.ts b/src/features/utils/configuration.ts
index e76c20f..e0d7a2d 100644
--- a/src/features/utils/configuration.ts
+++ b/src/features/utils/configuration.ts
@@ -1,12 +1,13 @@
 'use strict';
 
-import {
-    Uri, workspace, WorkspaceFolder, extensions, WorkspaceConfiguration
-} from 'vscode';
 import * as fs from 'fs';
 import * as path from 'path';
+import {
+    extensions, Uri, workspace, WorkspaceConfiguration, WorkspaceFolder
+} from 'vscode';
+import { getConfig } from '../../config';
 import { Constants } from './constants';
 
 export class Configuration {
 
     public static getConflictingExtensions(resource: Uri = null): string[] {
@@ -152,17 +153,18 @@ export class Configuration {
     }
 
     private static loadAnySetting<T>(
         configSection: string, defaultValue: T, resource: Uri, header: string = 'restructuredtext',
     ): T {
-        return workspace.getConfiguration(header, resource).get(configSection, defaultValue);
+        // return workspace.getConfiguration(header, resource).get(configSection, defaultValue);
+        return getConfig(header, resource).get(configSection, defaultValue);
     }
 
     private static async saveAnySetting<T>(
         configSection: string, value: T, resource: Uri, header: string = 'restructuredtext',
     ): Promise<T> {
-        await workspace.getConfiguration(header, resource).update(configSection, value);
+        await getConfig(header, resource).update(configSection, value);
         return value;
     }
 
     private static loadSetting(
         configSection: string,
diff --git a/src/stateUtils.ts b/src/stateUtils.ts
new file mode 100644
index 0000000..b0ca6a8
--- /dev/null
+++ b/src/stateUtils.ts
@@ -0,0 +1,91 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+/*---------------------------------------------------------
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See LICENSE in the project root for license information.
+ *--------------------------------------------------------*/
+
+import vscode = require('vscode');
+
+let globalState: vscode.Memento;
+let workspaceState: vscode.Memento;
+
+export function getFromGlobalState(key: string, defaultValue?: any): any {
+	if (!globalState) {
+		return defaultValue;
+	}
+	return globalState.get(key, defaultValue);
+}
+
+export function updateGlobalState(key: string, value: any) {
+	if (!globalState) {
+		return;
+	}
+	return globalState.update(key, value);
+}
+
+export function setGlobalState(state: vscode.Memento) {
+	globalState = state;
+}
+
+export function getGlobalState() {
+	return globalState;
+}
+
+export function resetGlobalState() {
+	resetStateQuickPick(globalState, updateGlobalState);
+}
+
+export function getFromWorkspaceState(key: string, defaultValue?: any) {
+	if (!workspaceState) {
+		return defaultValue;
+	}
+	return workspaceState.get(key, defaultValue);
+}
+
+export function updateWorkspaceState(key: string, value: any) {
+	if (!workspaceState) {
+		return;
+	}
+	return workspaceState.update(key, value);
+}
+
+export function setWorkspaceState(state: vscode.Memento) {
+	workspaceState = state;
+}
+
+export function getWorkspaceState(): vscode.Memento {
+	return workspaceState;
+}
+
+export function resetWorkspaceState() {
+	resetStateQuickPick(workspaceState, updateWorkspaceState);
+}
+
+export function getMementoKeys(state: vscode.Memento): string[] {
+	if (!state) {
+		return [];
+	}
+	// tslint:disable-next-line: no-empty
+	if ((state as any)._value) {
+		const keys = Object.keys((state as any)._value);
+		// Filter out keys with undefined values, so they are not shown
+		// in the quick pick menu.
+		return keys.filter((key) => state.get(key) !== undefined);
+	}
+	return [];
+}
+
+async function resetStateQuickPick(state: vscode.Memento, updateFn: (key: string, value: any) => {}) {
+	const items = await vscode.window.showQuickPick(getMementoKeys(state), {
+		canPickMany: true,
+		placeHolder: 'Select the keys to reset.'
+	});
+	resetItemsState(items, updateFn);
+}
+
+export function resetItemsState(items: string[], updateFn: (key: string, value: any) => {}) {
+	if (!items) {
+		return;
+	}
+	items.forEach((item) => updateFn(item, undefined));
+}

Vulnerable code:       "version": "6.5.3",
      "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz",
      "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==",
        "bn.js": "^4.4.0",
        "brorand": "^1.0.1",
        "hmac-drbg": "^1.0.0",
        "inherits": "^2.0.1",
        "minimalistic-assert": "^1.0.0",
        "minimalistic-crypto-utils": "^1.0.0"
import { ExtensionContentSecurityPolicyArbiter, PreviewSecuritySelector } from './security';
import { rstDocumentSymbolProvider } from './features/rstDocumentSymbolProvider';
let extensionPath = "";
	return extensionPath;
	extensionPath = context.extensionPath;
	const logger = new Logger();
	logger.log('Please visit https://docs.restructuredtext.net to learn how to configure the extension.');
	const conflicting = Configuration.getConflictingExtensions();
	for (const element of conflicting) {
		const found = vscode.extensions.getExtension(element);
		if (found) {
			const message = `Found conflicting extension ${element}. Please uninstall it.`;
			vscode.window.showErrorMessage(message);
		}
	}
	const disableLsp = Configuration.getLanguageServerDisabled();
	// activate language services
	const rstLspPromise = RstLanguageServer.activate(context, logger, disableLsp, python);
	context.subscriptions.push(
		vscode.commands.registerTextEditorCommand('restructuredtext.features.underline.underline', underline),
		vscode.commands.registerTextEditorCommand('restructuredtext.features.underline.underlineReverse',
			(textEditor, edit) => underline(textEditor, edit, true)),
	);
	// Linter support
	
    
	return {
		initializationFinished: Promise.all([rstLspPromise])
			.then((promiseResult) => {
				// This promise resolver simply swallows the result of Promise.all.
				// When we decide we want to expose this level of detail
				// to other extensions then we will design that return type and implement it here.
			}),
	};
	const os = require('os');
	if (platform === 'darwin' || platform === 'win32') {
		return;
	}
import {
    Uri, workspace, WorkspaceFolder, extensions, WorkspaceConfiguration
} from 'vscode';
        return workspace.getConfiguration(header, resource).get(configSection, defaultValue);
        await workspace.getConfiguration(header, resource).update(configSection, value);
Fixed code:       "version": "6.5.4",
      "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz",
      "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==",
        "bn.js": "^4.11.9",
        "brorand": "^1.1.0",
        "hmac-drbg": "^1.0.1",
        "inherits": "^2.0.4",
        "minimalistic-assert": "^1.0.1",
        "minimalistic-crypto-utils": "^1.0.1"
      },
      "dependencies": {
        "bn.js": {
          "version": "4.12.0",
          "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
          "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
          "dev": true
        },
        "inherits": {
          "version": "2.0.4",
          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
          "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
          "dev": true
        }
      },
      {
        "command": "restructuredtext.workspace.isTrusted.toggle",
        "title": "reStructuredText: Toggle Workspace Trust Flag",
        "description": "Toggle the workspace trust flag. Workspace settings that determine tool locations are disabled by default in untrusted workspaces."
/* 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.
 *--------------------------------------------------------*/

import vscode = require('vscode');
import { getFromWorkspaceState, updateWorkspaceState } from './stateUtils';

const WORKSPACE_IS_TRUSTED_KEY = 'WORKSPACE_IS_TRUSTED_KEY';
const SECURITY_SENSITIVE_CONFIG: string[] = [
    'sphinxBuildPath',
    'linter.executablePath'
];

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('restructuredtext.workspace.isTrusted.toggle', toggleWorkspaceIsTrusted));

    if (isTrusted) {
        return;
    }
    const ignored = ignoredWorkspaceConfig(vscode.workspace.getConfiguration('restructuredtext'), SECURITY_SENSITIVE_CONFIG);
    if (ignored.length === 0) {
        return;
    }
    const ignoredSettings = ignored.map((x) => `"restructuredtext.${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 "reStructuredText: 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://docs.restructuredtext.net/articles/configuration.html#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);
}

// reStructuredText 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 !== 'restructuredtext' || 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.
// tslint:disable-next-line: max-classes-per-file
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);
    }
}

export 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);
}

// True if the extension is running in known cloud-based IDEs.
export const IsInCloudIDE = process.env.CLOUD_SHELL === 'true' || process.env.CODESPACES === 'true';
import { ExtensionContentSecurityPolicyArbiter, PreviewSecuritySelector } from './security';
import { rstDocumentSymbolProvider } from './features/rstDocumentSymbolProvider';
import { setGlobalState, setWorkspaceState } from './stateUtils';
import { initConfig } from './config';
let extensionPath = '';
    return extensionPath;
    setGlobalState(context.globalState);
    setWorkspaceState(context.workspaceState);

    await initConfig(context);

    extensionPath = context.extensionPath;
    const logger = new Logger();
    logger.log('Please visit https://docs.restructuredtext.net to learn how to configure the extension.');

    const conflicting = Configuration.getConflictingExtensions();
    for (const element of conflicting) {
        const found = vscode.extensions.getExtension(element);
        if (found) {
            const message = `Found conflicting extension ${element}. Please uninstall it.`;
            vscode.window.showErrorMessage(message);
        }
    }
    const disableLsp = Configuration.getLanguageServerDisabled();
    // activate language services
    const rstLspPromise = RstLanguageServer.activate(context, logger, disableLsp, python);
    context.subscriptions.push(
        vscode.commands.registerTextEditorCommand('restructuredtext.features.underline.underline', underline),
        vscode.commands.registerTextEditorCommand('restructuredtext.features.underline.underlineReverse',
            (textEditor, edit) => underline(textEditor, edit, true)),
    );
    // Linter support


    return {
        initializationFinished: Promise.all([rstLspPromise])
            .then((promiseResult) => {
                // This promise resolver simply swallows the result of Promise.all.
                // When we decide we want to expose this level of detail
                // to other extensions then we will design that return type and implement it here.
            }),
    };
    const os = require('os');
    if (platform === 'darwin' || platform === 'win32') {
        return;
    }
import {
    extensions, Uri, workspace, WorkspaceConfiguration, WorkspaceFolder
} from 'vscode';
import { getConfig } from '../../config';
        // return workspace.getConfiguration(header, resource).get(configSection, defaultValue);
        return getConfig(header, resource).get(configSection, defaultValue);
        await getConfig(header, resource).update(configSection, value);
/* eslint-disable @typescript-eslint/no-explicit-any */
/*---------------------------------------------------------
 * Copyright (C) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License. See LICENSE in the project root for license information.
 *--------------------------------------------------------*/

import vscode = require('vscode');

let globalState: vscode.Memento;
let workspaceState: vscode.Memento;

export function getFromGlobalState(key: string, defaultValue?: any): any {
	if (!globalState) {
		return defaultValue;
	}
	return globalState.get(key, defaultValue);
}

export function updateGlobalState(key: string, value: any) {
	if (!globalState) {
		return;
	}
	return globalState.update(key, value);
}

export function setGlobalState(state: vscode.Memento) {
	globalState = state;
}

export function getGlobalState() {
	return globalState;
}

export function resetGlobalState() {
	resetStateQuickPick(globalState, updateGlobalState);
}

export function getFromWorkspaceState(key: string, defaultValue?: any) {
	if (!workspaceState) {
		return defaultValue;
	}
	return workspaceState.get(key, defaultValue);
}

export function updateWorkspaceState(key: string, value: any) {
	if (!workspaceState) {
		return;
	}
	return workspaceState.update(key, value);
}

export function setWorkspaceState(state: vscode.Memento) {
	workspaceState = state;
}

export function getWorkspaceState(): vscode.Memento {
	return workspaceState;
}

export function resetWorkspaceState() {
	resetStateQuickPick(workspaceState, updateWorkspaceState);
}

export function getMementoKeys(state: vscode.Memento): string[] {
	if (!state) {
		return [];
	}
	// tslint:disable-next-line: no-empty
	if ((state as any)._value) {
		const keys = Object.keys((state as any)._value);
		// Filter out keys with undefined values, so they are not shown
		// in the quick pick menu.
		return keys.filter((key) => state.get(key) !== undefined);
	}
	return [];
}

async function resetStateQuickPick(state: vscode.Memento, updateFn: (key: string, value: any) => {}) {
	const items = await vscode.window.showQuickPick(getMementoKeys(state), {
		canPickMany: true,
		placeHolder: 'Select the keys to reset.'
	});
	resetItemsState(items, updateFn);
}

export function resetItemsState(items: string[], updateFn: (key: string, value: any) => {}) {
	if (!items) {
		return;
	}
	items.forEach((item) => updateFn(item, undefined));
}