CVE-2021-30502: Insufficient Information (NVD-CWE-noinfo)
value: The unofficial vscode-ghc-simple (aka Simple Glasgow Haskell Compiler) extension before 0.2.3 for Visual Studio Code allows remote code execution via a crafted workspace configuration with replCommand.
CWE description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
CVSS v3 base score: 9.8
Published: 2021-04-25T03:15Z
Repository: https://github.com/dramforever/vscode-ghc-simple
Language: JSON
Files changed: package.json, src/bios/config.ts
Commit date: 2021-04-24 23:10:44 +0800
Commit message: [bios] Add replCommand trust mechanism

Diff: diff --git a/package.json b/package.json
index 10f7fbd..50bc181 100644
--- a/package.json
+++ b/package.json
@@ -142,10 +142,16 @@
                 "ghcSimple.replLinger": {
                     "type": "boolean",
                     "default": true,
                     "markdownDescription": "Whether GHCi sessions for workspaces linger after closing all associated documents.\n\nWhen enabled, the GHCi startup time is saved but takes more memory when idle. When disabled, kills GHCi after all associated files are closed, saves memory but takes time to restart. GHCi for a single file never linger."
                 },
+                "ghcSimple.trustedReplCommandConfigs": {
+                    "type": "object",
+                    "default": {},
+                    "scope": "application",
+                    "markdownDescription": "Using a `#ghcSimple.replCommand#` configuration from an untrusted workspace can pose a security risk. Whenever such a configuration occurs, you will be notified and if you choose to trust it, it will be recorded here."
+                },
                 "ghcSimple.startupCommands.all": {
                     "type": "array",
                     "items": {
                         "type": "string"
                     },
diff --git a/src/bios/config.ts b/src/bios/config.ts
index 18d32c8..201e57f 100644
--- a/src/bios/config.ts
+++ b/src/bios/config.ts
@@ -87,16 +87,49 @@ async function singleConfig(cwd?: string): Promise<Configuration> {
             dependencies: []
         }
     }
 }
 
+const alreadyShown = new Set();
+
+function handleReplCommandTrust(
+    workspaceUri: vscode.Uri,
+    replCommand: string
+): boolean {
+    if (workspaceUri.scheme !== 'file') return false;
+    const config = vscode.workspace.getConfiguration('ghcSimple', null);
+    const insp = config.inspect('trustedReplCommandConfigs').globalValue ?? {};
+    if (insp[workspaceUri.fsPath] === replCommand) {
+        return true;
+    } else {
+        if (! alreadyShown.has(workspaceUri.fsPath)) {
+            alreadyShown.add(workspaceUri.fsPath);
+            vscode.window.showWarningMessage(
+                `This workspace ${workspaceUri.fsPath} wants to run "${replCommand}" to start GHCi.\n\nAllow if you understand this and trust it.`,
+                'Allow', 'Ignore'
+            ).then((value) => {
+                alreadyShown.delete(workspaceUri.fsPath);
+                if (value == 'Allow') {
+                    const trusted = config.get('trustedReplCommandConfigs');
+                    trusted[workspaceUri.fsPath] = replCommand;
+                    config.update('trustedReplCommandConfigs', trusted, vscode.ConfigurationTarget.Global);
+                }
+            })
+        }
+        return false;
+    }
+}
+
 /** Configuration for a custom command */
 async function customConfig(
     replScope: 'workspace' | 'file',
     replCommand: string,
     workspaceUri: vscode.Uri
-): Promise<Configuration> {
+): Promise<Configuration | null> {
+    if (! handleReplCommandTrust(workspaceUri, replCommand))
+        return null;
+
     if (replCommand.indexOf('$stack_ide_targets') !== -1) {
         const sit = await getStackIdeTargets(workspaceUri);
         replCommand.replace(/\$stack_ide_targets/g, sit.join(' '));
     }
 

Vulnerable code: ): Promise<Configuration> {
Fixed code:                 "ghcSimple.trustedReplCommandConfigs": {
                    "type": "object",
                    "default": {},
                    "scope": "application",
                    "markdownDescription": "Using a `#ghcSimple.replCommand#` configuration from an untrusted workspace can pose a security risk. Whenever such a configuration occurs, you will be notified and if you choose to trust it, it will be recorded here."
                },
const alreadyShown = new Set();

function handleReplCommandTrust(
    workspaceUri: vscode.Uri,
    replCommand: string
): boolean {
    if (workspaceUri.scheme !== 'file') return false;
    const config = vscode.workspace.getConfiguration('ghcSimple', null);
    const insp = config.inspect('trustedReplCommandConfigs').globalValue ?? {};
    if (insp[workspaceUri.fsPath] === replCommand) {
        return true;
    } else {
        if (! alreadyShown.has(workspaceUri.fsPath)) {
            alreadyShown.add(workspaceUri.fsPath);
            vscode.window.showWarningMessage(
                `This workspace ${workspaceUri.fsPath} wants to run "${replCommand}" to start GHCi.\n\nAllow if you understand this and trust it.`,
                'Allow', 'Ignore'
            ).then((value) => {
                alreadyShown.delete(workspaceUri.fsPath);
                if (value == 'Allow') {
                    const trusted = config.get('trustedReplCommandConfigs');
                    trusted[workspaceUri.fsPath] = replCommand;
                    config.update('trustedReplCommandConfigs', trusted, vscode.ConfigurationTarget.Global);
                }
            })
        }
        return false;
    }
}

): Promise<Configuration | null> {
    if (! handleReplCommandTrust(workspaceUri, replCommand))
        return null;