diff --git a/patches/backported-patches.json b/patches/backported-patches.json index 9456b19..e8afbd4 100644 --- a/patches/backported-patches.json +++ b/patches/backported-patches.json @@ -48,5 +48,45 @@ "patch_path": "N/A", "link": "https://github.com/advisories/GHSA-c82g-9gj4-hxp2", "note": "GitHub Copilot path traversal - Copilot not present in Code Editor" + }, + { + "finding_id": "CVE-2026-47282", + "affected_versions": "< 1.128.1", + "patch_path": "patches/common/fix-copilot-debug-override-user-scope.diff", + "link": "https://github.com/microsoft/vscode/commit/f05bcd1aa29cff28a62fe137b4a16fec9393f31b" + }, + { + "finding_id": "GHSA-wr9x-42j2-jvh3", + "affected_versions": "< 1.128.1", + "patch_path": "patches/common/fix-copilot-debug-override-user-scope.diff", + "link": "https://github.com/microsoft/vscode/commit/f05bcd1aa29cff28a62fe137b4a16fec9393f31b" + }, + { + "finding_id": "CVE-2026-57102", + "affected_versions": "< 1.128.1", + "patch_path": "patches/web-server/webview.diff", + "link": "https://github.com/microsoft/vscode/commit/236fa7d8ea86f0f4261df38e1a17b3c7e7002bd0", + "note": "Attacker-controlled parentOrigin RCE in the web worker extension host iframe. Already mitigated by the existing web-server/webview.diff: the iframe script forces parentOrigin = window.origin and only reads the searchParams parentOrigin after the hostname-validation branch, so the non-validated (same-origin) path never trusts an attacker-supplied parentOrigin. Equivalent to upstream's explicit rejection; not double-applied." + }, + { + "finding_id": "GHSA-v282-cxqj-xgj4", + "affected_versions": "< 1.128.1", + "patch_path": "patches/web-server/webview.diff", + "link": "https://github.com/microsoft/vscode/commit/236fa7d8ea86f0f4261df38e1a17b3c7e7002bd0", + "note": "Attacker-controlled parentOrigin RCE in the web worker extension host iframe. Already mitigated by the existing web-server/webview.diff: the iframe script forces parentOrigin = window.origin and only reads the searchParams parentOrigin after the hostname-validation branch, so the non-validated (same-origin) path never trusts an attacker-supplied parentOrigin. Equivalent to upstream's explicit rejection; not double-applied." + }, + { + "finding_id": "CVE-2026-57101", + "affected_versions": "< 1.128.1", + "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": "GHSA-9mw4-h26x-gfxw",
+ "affected_versions": "< 1.128.1",
+ "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."
}
]
diff --git a/patches/common/fix-copilot-debug-override-user-scope.diff b/patches/common/fix-copilot-debug-override-user-scope.diff
new file mode 100644
index 0000000..66e6d1f
--- /dev/null
+++ b/patches/common/fix-copilot-debug-override-user-scope.diff
@@ -0,0 +1,164 @@
+Restrict Copilot debug endpoint override settings to user (global) scope.
+Reads the advanced debug CAPI/proxy URL override settings only from the user
+(global) configuration, ignoring workspace and folder values so a malicious
+workspace .vscode/settings.json cannot redirect requests to an attacker host.
+Remove when Code-OSS is updated to >= 1.128.1.
+
+@backported: https://github.com/microsoft/vscode/commit/f05bcd1aa29cff28a62fe137b4a16fec9393f31b
+@finding-id: CVE-2026-47282 GHSA-wr9x-42j2-jvh3
+Index: b/extensions/copilot/src/extension/completions-core/vscode-node/extension/src/config.ts
+===================================================================
+--- a/extensions/copilot/src/extension/completions-core/vscode-node/extension/src/config.ts
++++ b/extensions/copilot/src/extension/completions-core/vscode-node/extension/src/config.ts
+@@ -15,7 +15,8 @@ import {
+ getOptionalConfigDefaultForKey,
+ ICompletionsConfigProvider,
+ ICompletionsEditorAndPluginInfo,
+- packageJson
++ packageJson,
++ userScopeOnlyKeys
+ } from '../../lib/src/config';
+ import { CopilotConfigPrefix } from '../../lib/src/constants';
+ import { Logger } from '../../lib/src/logger';
+@@ -44,13 +45,38 @@ export class VSCodeConfigProvider extend
+ }
+
+ override getConfig(key: ConfigKeyType): T {
++ if (userScopeOnlyKeys.has(key)) {
++ return this._getUserScopeValue(key) ?? getConfigDefaultForKey(key);
++ }
+ return getConfigKeyRecursively(this.config, key) ?? getConfigDefaultForKey(key);
+ }
+
+ override getOptionalConfig(key: ConfigKeyType): T | undefined {
++ if (userScopeOnlyKeys.has(key)) {
++ return this._getUserScopeValue(key) ?? getOptionalConfigDefaultForKey(key);
++ }
+ return getConfigKeyRecursively(this.config, key) ?? getOptionalConfigDefaultForKey(key);
+ }
+
++ private _getUserScopeValue(key: ConfigKeyType): T | undefined {
++ // Try the flat key path directly
++ const inspected = this.config.inspect(key);
++ if (inspected?.globalValue !== undefined) {
++ return inspected.globalValue;
++ }
++ // Handle object-style settings (e.g., "advanced": { "debug.overrideCapiUrl": "..." })
++ const firstDot = key.indexOf('.');
++ if (firstDot > 0) {
++ const parentKey = key.substring(0, firstDot);
++ const subKey = key.substring(firstDot + 1);
++ const parentInspect = this.config.inspect>(parentKey);
++ if (parentInspect?.globalValue && typeof parentInspect.globalValue === 'object') {
++ return getConfigKeyRecursively(parentInspect.globalValue as Record, subKey);
++ }
++ }
++ return undefined;
++ }
++
+ // Dumps config settings defined in the extension json
+ override dumpForTelemetry(): { [key: string]: string } {
+ return {};
+Index: b/extensions/copilot/src/extension/completions-core/vscode-node/lib/src/config.ts
+===================================================================
+--- a/extensions/copilot/src/extension/completions-core/vscode-node/lib/src/config.ts
++++ b/extensions/copilot/src/extension/completions-core/vscode-node/lib/src/config.ts
+@@ -75,6 +75,21 @@ export const ConfigKey = {
+ UseSplitContextPrompt: 'internal.useSplitContextPrompt',
+ };
+
++/**
++ * Settings that must only be read from user (global) scope, ignoring workspace/folder values.
++ * Prevents workspace-level override attacks via malicious .vscode/settings.json.
++ */
++export const userScopeOnlyKeys = new Set([
++ ConfigKey.DebugOverrideCapiUrl,
++ ConfigKey.DebugOverrideCapiUrlLegacy,
++ ConfigKey.DebugTestOverrideCapiUrl,
++ ConfigKey.DebugTestOverrideCapiUrlLegacy,
++ ConfigKey.DebugOverrideProxyUrl,
++ ConfigKey.DebugOverrideProxyUrlLegacy,
++ ConfigKey.DebugTestOverrideProxyUrl,
++ ConfigKey.DebugTestOverrideProxyUrlLegacy,
++]);
++
+ export type ConfigKeyType = string;
+
+ // How to determine where to terminate the completion to the current block.
+Index: b/extensions/copilot/src/platform/configuration/common/configurationService.ts
+===================================================================
+--- a/extensions/copilot/src/platform/configuration/common/configurationService.ts
++++ b/extensions/copilot/src/platform/configuration/common/configurationService.ts
+@@ -375,6 +375,12 @@ export const enum ConfigType {
+ export interface ConfigOptions {
+ readonly oldKey?: string;
+ readonly valueIgnoredForExternals?: boolean;
++ /**
++ * When true, only reads from user (global) scope, ignoring workspace and folder values.
++ * Use for security-sensitive settings (e.g., API endpoint overrides) that must not be
++ * controllable via a workspace's .vscode/settings.json.
++ */
++ readonly userScopeOnly?: boolean;
+ }
+
+ export interface Config extends BaseConfig {
+@@ -573,8 +579,8 @@ export namespace ConfigKey {
+ */
+ export namespace Shared {
+ /** Allows for overriding the base domain we use for making requests to the CAPI. This helps CAPI devs develop against a local instance. */
+- export const DebugOverrideProxyUrl = defineSetting('advanced.debug.overrideProxyUrl', ConfigType.Simple, undefined);
+- export const DebugOverrideCAPIUrl = defineSetting('advanced.debug.overrideCapiUrl', ConfigType.Simple, undefined);
++ export const DebugOverrideProxyUrl = defineSetting('advanced.debug.overrideProxyUrl', ConfigType.Simple, undefined, undefined, { userScopeOnly: true });
++ export const DebugOverrideCAPIUrl = defineSetting('advanced.debug.overrideCapiUrl', ConfigType.Simple, undefined, undefined, { userScopeOnly: true });
+ export const DebugUseNodeFetchFetcher = defineSetting('advanced.debug.useNodeFetchFetcher', ConfigType.Simple, true);
+ export const DebugUseNodeFetcher = defineSetting('advanced.debug.useNodeFetcher', ConfigType.Simple, false);
+ export const DebugUseElectronFetcher = defineSetting('advanced.debug.useElectronFetcher', ConfigType.Simple, true);
+Index: b/extensions/copilot/src/platform/configuration/vscode/configurationServiceImpl.ts
+===================================================================
+--- a/extensions/copilot/src/platform/configuration/vscode/configurationServiceImpl.ts
++++ b/extensions/copilot/src/platform/configuration/vscode/configurationServiceImpl.ts
+@@ -57,7 +57,9 @@ export class ConfigurationServiceImpl ex
+ const config = scope === undefined ? this.config : vscode.workspace.getConfiguration(CopilotConfigPrefix, scope);
+
+ let configuredValue: T | undefined;
+- if (key.advancedSubKey) {
++ if (key.options?.userScopeOnly) {
++ configuredValue = this._getUserScopeValue(config, key);
++ } else if (key.advancedSubKey) {
+ // This is a `github.copilot.advanced.*` setting
+
+ // First, let's try to read it using the flat style
+@@ -104,6 +106,33 @@ export class ConfigurationServiceImpl ex
+ return value.content;
+ }
+
++ private _getUserScopeValue(config: WorkspaceConfiguration, key: Config): T | undefined {
++ if (key.advancedSubKey) {
++ // Flat style: e.g., "github.copilot.advanced.debug.overrideCapiUrl": "..."
++ const flatInspect = config.inspect(key.id);
++ if (flatInspect?.globalValue !== undefined) {
++ return flatInspect.globalValue;
++ }
++ // Object style: e.g., "github.copilot.advanced": { "debug.overrideCapiUrl": "..." }
++ const advancedInspect = config.inspect>('advanced');
++ return advancedInspect?.globalValue?.[key.advancedSubKey];
++ }
++
++ const inspected = config.inspect(key.id);
++ if (inspected?.globalValue !== undefined) {
++ return inspected.globalValue;
++ }
++
++ if (key.oldId) {
++ const oldInspected = config.inspect(key.oldId);
++ if (oldInspected?.globalValue !== undefined) {
++ return oldInspected.globalValue;
++ }
++ }
++
++ return undefined;
++ }
++
+ inspectConfig(key: BaseConfig, scope?: vscode.ConfigurationScope): InspectConfigResult | undefined {
+ if (key.options?.valueIgnoredForExternals && !this._isInternal) {
+ return { defaultValue: this.getDefaultValue(key) };
diff --git a/patches/sagemaker.series b/patches/sagemaker.series
index 52f1e02..ccfc8bf 100644
--- a/patches/sagemaker.series
+++ b/patches/sagemaker.series
@@ -71,3 +71,4 @@ common/fix-remote-hosts-loopback.diff
common/finding-override-undici.diff
common/finding-override-form-data.diff
common/finding-override-tar.diff
+common/fix-copilot-debug-override-user-scope.diff
diff --git a/patches/web-embedded-with-terminal.series b/patches/web-embedded-with-terminal.series
index 2e0e0e5..4ea4488 100644
--- a/patches/web-embedded-with-terminal.series
+++ b/patches/web-embedded-with-terminal.series
@@ -67,3 +67,4 @@ common/fix-snippets-path-traversal.diff
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
diff --git a/patches/web-embedded.series b/patches/web-embedded.series
index be679f6..721ea56 100644
--- a/patches/web-embedded.series
+++ b/patches/web-embedded.series
@@ -70,3 +70,4 @@ common/fix-snippets-path-traversal.diff
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
diff --git a/patches/web-server.series b/patches/web-server.series
index 00fd5ef..34bdd22 100644
--- a/patches/web-server.series
+++ b/patches/web-server.series
@@ -49,3 +49,4 @@ common/fix-snippets-path-traversal.diff
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