Repository: microsoft/vscode (TypeScript)
Date: 2023-04-26 18:03:37+00:00
Task type: Bug Fix

Issue:
WorkspaceConfiguration.get returns string array which is mutable
<!-- ⚠️⚠️ Do Not Delete This! bug_report_template ⚠️⚠️ -->
<!-- Please read our Rules of Conduct: https://opensource.microsoft.com/codeofconduct/ -->
<!-- 🕮 Read our guide about submitting issues: https://github.com/microsoft/vscode/wiki/Submitting-Bugs-and-Suggestions -->
<!-- 🔎 Search existing issues to avoid creating duplicates. -->
<!-- 🧪 Test using the latest Insiders build to see if your issue has already been fixed: https://code.visualstudio.com/insiders/ -->
<!-- 💡 Instead of creating your report here, use 'Report Issue' from the 'Help' menu in VS Code to pre-fill useful information. -->
<!-- 🔧 Launch with `code --disable-extensions` to check. -->
Does this issue occur when all extensions are disabled?: No

<!-- 🪓 If you answered No above, use 'Help: Start Extension Bisect' from Command Palette to try to identify the cause. -->
<!-- 📣 Issues caused by an extension need to be reported directly to the extension publisher. The 'Help > Report Issue' dialog can assist with this. -->
- VS Code Version: 1.72.1 and 1.72.0-insider 
- OS Version: Windows_NT x64 10.0.22000

Steps to Reproduce:
Run the following code from a VSCode extension:
```
const settings = vscode.workspace.getConfiguration("extension-setting");

const originalConfig = settings.get<string[]>("key", []);

originalConfig.push("test");

 const modifiedConfig = settings.get<string[]>("test", []);
// modifiedConfig contains "test" entry
```
The expected behavior would be that the `modifiedConfig` does not contain an entry for "test" since there was no call to `settings.update`. It appears that modifications to the workspaceConfig are stored in memory but are not reflected in settings.json. The workaround is to use `const copyConfig: string[] = [...originalConfig];` to prevent modification of the `originalConfig`

This does not repro if the configuration type is a string or an object. For example:
```
const codeSearchServerSettings: CodeSearchServerSettings = vscode.workspace.getConfiguration('extension-setting').get<CodeSearchServerSettings>('key', defaultCodeSearchServerSettings);

codeSearchServerSettings.property = "foo";

const actualKeyValue = vscode.workspace.getConfiguration('extension-setting').get<CodeSearchServerSettings>('key', defaultCodeSearchServerSettings).property;
//actualKeyValue is not "foo
```


Issue discussion:

Solution patch:
diff --git a/src/vs/workbench/api/common/extHostConfiguration.ts b/src/vs/workbench/api/common/extHostConfiguration.ts
--- a/src/vs/workbench/api/common/extHostConfiguration.ts
+++ b/src/vs/workbench/api/common/extHostConfiguration.ts
@@ -236,6 +236,9 @@ export class ExtHostConfigProvider {
 								}
 							});
 						}
+						if (Array.isArray(target)) {
+							return deepClone(target);
+						}
 						return target;
 					};
 					result = cloneOnWriteProxy(result, key);


Test patch:
diff --git a/src/vs/workbench/api/test/browser/extHostConfiguration.test.ts b/src/vs/workbench/api/test/browser/extHostConfiguration.test.ts
--- a/src/vs/workbench/api/test/browser/extHostConfiguration.test.ts
+++ b/src/vs/workbench/api/test/browser/extHostConfiguration.test.ts
@@ -771,6 +771,16 @@ suite('ExtHostConfiguration', function () {
 		testObject.$acceptConfigurationChanged(newConfigData, configEventData);
 	});
 
+	test('get return instance of array value', function () {
+		const testObject = createExtHostConfiguration({ 'far': { 'boo': [] } });
+
+		const value: string[] = testObject.getConfiguration().get('far.boo', []);
+		value.push('a');
+
+		const actual = testObject.getConfiguration().get('far.boo', []);
+		assert.deepStrictEqual(actual, []);
+	});
+
 	function aWorkspaceFolder(uri: URI, index: number, name: string = ''): IWorkspaceFolder {
 		return new WorkspaceFolder({ uri, name, index });
 	}