CVE-2021-28793 (TypeScript, published 2021-04-20T13:15Z)
Description: 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.
Severity: HIGH (CVSS v3 base score: 9.8)
Weakness: CWE-863 \u2014 Incorrect Authorization
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.
Repository: https://github.com/vscode-restructuredtext/vscode-restructuredtext
Files changed:
package-lock.json
package.json
src/config.ts
src/extension.ts
src/features/utils/configuration.ts
src/stateUtils.ts
Commit message: Patch to address workspace security issue.


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