From f653b523246775504b24888cd25fe789214fbd5f Mon Sep 17 00:00:00 2001 From: Sergey Kazantsev Date: Fri, 23 Oct 2026 08:37:11 +0000 Subject: [PATCH] fix: Harden configuration parsing for nested settings --- patches/backported-patches.json | 12 +++ ...fix-config-nested-restricted-settings.diff | 77 +++++++++++++++++++ patches/sagemaker.series | 1 + patches/web-embedded-with-terminal.series | 1 + patches/web-embedded.series | 1 + patches/web-server.series | 1 + 6 files changed, 93 insertions(+) create mode 100644 patches/common/fix-config-nested-restricted-settings.diff diff --git a/patches/backported-patches.json b/patches/backported-patches.json index e8afbd4..86cdeba 100644 --- a/patches/backported-patches.json +++ b/patches/backported-patches.json @@ -88,5 +88,17 @@ "patch_path": "N/A", "link": "https://github.com/microsoft/vscode/commit/bc2d56c6f8da22a4bd31f741fcb034208770f158", "note": "Notebook Restricted-Mode bypass via mermaid render. Upstream vulnerable file extensions/mermaid-markdown-features/preview-src/notebook/index.ts (the 'temp.innerHTML = result' sink and renderMermaidBlocksInElement) does not exist in this branch's shipped source. The only mermaid extension here, mermaid-chat-features, renders diagrams via escapeHtmlText() into a
 and uses textContent (chat-webview-src/mermaidWebview.ts) inside a sandboxed webview with strict nonce CSP - no innerHTML of untrusted content. The markdown-language-features notebook renderer already sanitizes untrusted HTML with DOMPurify. No equivalent vulnerable innerHTML/insertAdjacentHTML sink for untrusted render output exists."
+  },
+  {
+    "finding_id": "CVE-2026-70334",
+    "affected_versions": "< 1.136.2",
+    "patch_path": "patches/common/fix-config-nested-restricted-settings.diff",
+    "link": "https://github.com/microsoft/vscode/commit/0684cb5905a3f23156f45a28135326e09310ff4d"
+  },
+  {
+    "finding_id": "GHSA-q2f8-hh8x-wpc2",
+    "affected_versions": "< 1.136.2",
+    "patch_path": "patches/common/fix-config-nested-restricted-settings.diff",
+    "link": "https://github.com/microsoft/vscode/commit/0684cb5905a3f23156f45a28135326e09310ff4d"
   }
 ]
diff --git a/patches/common/fix-config-nested-restricted-settings.diff b/patches/common/fix-config-nested-restricted-settings.diff
new file mode 100644
index 0000000..1b65e8c
--- /dev/null
+++ b/patches/common/fix-config-nested-restricted-settings.diff
@@ -0,0 +1,77 @@
+From: Sandeep Somavarapu 
+Subject: Filter restricted settings in nested configuration objects
+Origin: upstream, https://github.com/microsoft/vscode/commit/0684cb5905a3f23156f45a28135326e09310ff4d
+Bug: GHSA-q2f8-hh8x-wpc2
+
+ConfigurationModelParser only checked top-level keys against the
+configuration registry. Walk nested objects and check each setting by its
+effective dotted path. Remove when Code-OSS is updated to >= 1.136.2.
+Index: code-editor-src/src/vs/platform/configuration/common/configurationModels.ts
+===================================================================
+--- code-editor-src.orig/src/vs/platform/configuration/common/configurationModels.ts
++++ code-editor-src/src/vs/platform/configuration/common/configurationModels.ts
+@@ -441,12 +441,11 @@ export class ConfigurationModelParser {
+ 				hasExcludedProperties = hasExcludedProperties || result.hasExcludedProperties;
+ 				restricted.push(...result.restricted);
+ 			} else {
+-				const propertySchema = configurationProperties[key];
+-				if (propertySchema?.restricted) {
+-					restricted.push(key);
+-				}
+-				if (this.shouldInclude(key, propertySchema, excludedConfigurationProperties, options)) {
+-					raw[key] = properties[key];
++				const result = this.filterProperty(key, properties[key], configurationProperties, excludedConfigurationProperties, options);
++				restricted.push(...result.restricted);
++				hasExcludedProperties = hasExcludedProperties || result.hasExcludedProperties;
++				if (result.included) {
++					raw[key] = result.value;
+ 				} else {
+ 					hasExcludedProperties = true;
+ 				}
+@@ -455,6 +454,46 @@ export class ConfigurationModelParser {
+ 		return { raw, restricted, hasExcludedProperties };
+ 	}
+ 
++	private filterProperty(path: string, value: unknown, configurationProperties: IStringDictionary, excludedConfigurationProperties: IStringDictionary, options: ConfigurationParseOptions): { included: boolean; value: unknown; restricted: string[]; hasExcludedProperties: boolean } {
++		const propertySchema = configurationProperties[path];
++		if (propertySchema) {
++			return { included: this.shouldInclude(path, propertySchema, excludedConfigurationProperties, options), value, restricted: propertySchema.restricted ? [path] : [], hasExcludedProperties: false };
++		}
++		if (!types.isObject(value) || Array.isArray(value)) {
++			return { included: this.shouldInclude(path, propertySchema, excludedConfigurationProperties, options), value, restricted: [], hasExcludedProperties: false };
++		}
++		// Excluded properties can themselves be object-typed (e.g. `mcp.enterpriseManagedAuth.idp`), so treat them as a leaf too.
++		if (excludedConfigurationProperties[path]) {
++			return { included: this.shouldInclude(path, propertySchema, excludedConfigurationProperties, options), value, restricted: [], hasExcludedProperties: false };
++		}
++		// If the whole object is explicitly excluded, do not recurse into its children.
++		if (options.exclude?.includes(path)) {
++			return { included: false, value, restricted: [], hasExcludedProperties: false };
++		}
++		const keys = Object.keys(value);
++		if (keys.length === 0) {
++			return { included: this.shouldInclude(path, propertySchema, excludedConfigurationProperties, options), value, restricted: [], hasExcludedProperties: false };
++		}
++		const nested: IStringDictionary = {};
++		const restricted: string[] = [];
++		let included = false;
++		let hasExcludedProperties = false;
++		for (const key of keys) {
++			const result = this.filterProperty(`${path}.${key}`, (value as IStringDictionary)[key], configurationProperties, excludedConfigurationProperties, options);
++			if (result.restricted.length) {
++				restricted.push(...result.restricted);
++			}
++			hasExcludedProperties = hasExcludedProperties || result.hasExcludedProperties;
++			if (result.included) {
++				nested[key] = result.value;
++				included = true;
++			} else {
++				hasExcludedProperties = true;
++			}
++		}
++		return { included, value: nested, restricted, hasExcludedProperties };
++	}
++
+ 	private shouldInclude(key: string, propertySchema: IConfigurationPropertySchema | undefined, excludedConfigurationProperties: IStringDictionary, options: ConfigurationParseOptions): boolean {
+ 		if (options.exclude?.includes(key)) {
+ 			return false;
diff --git a/patches/sagemaker.series b/patches/sagemaker.series
index ccfc8bf..b25c373 100644
--- a/patches/sagemaker.series
+++ b/patches/sagemaker.series
@@ -72,3 +72,4 @@ common/finding-override-undici.diff
 common/finding-override-form-data.diff
 common/finding-override-tar.diff
 common/fix-copilot-debug-override-user-scope.diff
+common/fix-config-nested-restricted-settings.diff
diff --git a/patches/web-embedded-with-terminal.series b/patches/web-embedded-with-terminal.series
index 4ea4488..4e1bd06 100644
--- a/patches/web-embedded-with-terminal.series
+++ b/patches/web-embedded-with-terminal.series
@@ -68,3 +68,4 @@ common/fix-remote-hosts-loopback.diff
 common/finding-override-undici.diff
 common/finding-override-form-data.diff
 common/fix-copilot-debug-override-user-scope.diff
+common/fix-config-nested-restricted-settings.diff
diff --git a/patches/web-embedded.series b/patches/web-embedded.series
index 721ea56..b3fa141 100644
--- a/patches/web-embedded.series
+++ b/patches/web-embedded.series
@@ -71,3 +71,4 @@ common/fix-remote-hosts-loopback.diff
 common/finding-override-undici.diff
 common/finding-override-form-data.diff
 common/fix-copilot-debug-override-user-scope.diff
+common/fix-config-nested-restricted-settings.diff
diff --git a/patches/web-server.series b/patches/web-server.series
index 34bdd22..a41164f 100644
--- a/patches/web-server.series
+++ b/patches/web-server.series
@@ -50,3 +50,4 @@ common/fix-remote-hosts-loopback.diff
 common/finding-override-undici.diff
 common/finding-override-form-data.diff
 common/fix-copilot-debug-override-user-scope.diff
+common/fix-config-nested-restricted-settings.diff