Skip to content

Commit 4307305

Browse files
authored
shellEnv: disable powershell discovery on Windows (microsoft#252703)
* shellEnv: disable powershell discovery on Windows Closes microsoft#251957 Closes microsoft#251901 Closes microsoft#251446 * comments
1 parent b863337 commit 4307305

File tree

1 file changed

+22
-61
lines changed

1 file changed

+22
-61
lines changed

src/vs/platform/shell/node/shellEnv.ts

Lines changed: 22 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,22 @@
55

66
import { spawn } from 'child_process';
77
import { homedir } from 'os';
8-
import { basename, dirname, extname, isAbsolute, join } from '../../../base/common/path.js';
9-
import { localize } from '../../../nls.js';
8+
import { first, Promises } from '../../../base/common/async.js';
109
import { CancellationToken, CancellationTokenSource } from '../../../base/common/cancellation.js';
1110
import { toErrorMessage } from '../../../base/common/errorMessage.js';
1211
import { CancellationError, isCancellationError } from '../../../base/common/errors.js';
12+
import { clamp } from '../../../base/common/numbers.js';
13+
import { basename, join } from '../../../base/common/path.js';
1314
import { IProcessEnvironment, isMacintosh, isWindows, OS } from '../../../base/common/platform.js';
1415
import { generateUuid } from '../../../base/common/uuid.js';
16+
import { StreamSplitter } from '../../../base/node/nodeStreams.js';
17+
import { Promises as FSPromises } from '../../../base/node/pfs.js';
1518
import { getSystemShell } from '../../../base/node/shell.js';
19+
import { localize } from '../../../nls.js';
20+
import { IConfigurationService } from '../../configuration/common/configuration.js';
1621
import { NativeParsedArgs } from '../../environment/common/argv.js';
1722
import { isLaunchedFromCli } from '../../environment/node/argvHelper.js';
1823
import { ILogService } from '../../log/common/log.js';
19-
import { first, Promises } from '../../../base/common/async.js';
20-
import { IConfigurationService } from '../../configuration/common/configuration.js';
21-
import { clamp } from '../../../base/common/numbers.js';
22-
import { findExecutable, getWindowPathExtensions } from '../../../base/node/processes.js';
23-
import { equalsIgnoreCase } from '../../../base/common/strings.js';
24-
import { Promises as FSPromises } from '../../../base/node/pfs.js';
25-
import { StreamSplitter } from '../../../base/node/nodeStreams.js';
2624

2725
let shellEnvPromise: Promise<typeof process.env> | undefined = undefined;
2826

@@ -36,6 +34,13 @@ let shellEnvPromise: Promise<typeof process.env> | undefined = undefined;
3634
*/
3735
export async function getResolvedShellEnv(configurationService: IConfigurationService, logService: ILogService, args: NativeParsedArgs, env: IProcessEnvironment): Promise<typeof process.env> {
3836

37+
// Skip on windows
38+
if (isWindows) {
39+
logService.trace('resolveShellEnv(): skipped (Windows)');
40+
41+
return {};
42+
}
43+
3944
// Skip if --force-disable-user-env
4045
if (args['force-disable-user-env']) {
4146
logService.trace('resolveShellEnv(): skipped (--force-disable-user-env)');
@@ -113,21 +118,13 @@ async function doResolveShellEnv(logService: ILogService, token: CancellationTok
113118
};
114119

115120
logService.trace('doResolveShellEnv#env', env);
116-
const systemShell = await getSystemShell(OS, env); // note: windows always resolves a powershell instance
121+
const systemShell = await getSystemShell(OS, env);
117122
logService.trace('doResolveShellEnv#shell', systemShell);
118123

119-
let name = basename(systemShell);
120-
if (isWindows) {
121-
const nameExt = extname(name);
122-
if (getWindowPathExtensions().some(e => equalsIgnoreCase(e, nameExt))) {
123-
name = name.substring(0, name.length - nameExt.length); // remove any .exe/.cmd/... from the name for matching logic on Windows
124-
}
125-
}
126-
124+
const name = basename(systemShell);
127125
let command: string, shellArgs: Array<string>;
128-
const extraArgs = '';
129126
if (/^(?:pwsh|powershell)(?:-preview)?$/.test(name)) {
130-
const profilePaths = await getPowershellProfilePaths(systemShell);
127+
const profilePaths = getPowershellProfilePaths();
131128
const profilePathThatExists = await first(profilePaths.map(profilePath => async () => (await FSPromises.exists(profilePath)) ? profilePath : undefined));
132129
if (!profilePathThatExists) {
133130
logService.trace('doResolveShellEnv#noPowershellProfile after testing paths', profilePaths);
@@ -141,25 +138,15 @@ async function doResolveShellEnv(logService: ILogService, token: CancellationTok
141138
// so we use "double single quotes" which is how you escape single
142139
// quotes inside of a single quoted string.
143140
command = `Write-Output '${mark}'; [System.Environment]::GetEnvironmentVariables() | ConvertTo-Json -Compress; Write-Output '${mark}'`;
144-
145-
// Improve unicode support on Windows by setting the code page to UTF-8
146-
if (isWindows) {
147-
command = `chcp 65001; ${command}`;
148-
}
149-
150-
// -Login is not a supported argument on PowerShell 5, which is a version of
151-
// powershell that is exclusive to Windows. Providing it would error. Also,
152-
// -Login is documented as a no-op on Windows on Powershell 7, so simply omit
153-
// it to avoid causing errors or requiring a version check.
154-
shellArgs = isWindows ? ['-Command'] : ['-Login', '-Command'];
141+
shellArgs = ['-Login', '-Command'];
155142
} else if (name === 'nu') { // nushell requires ^ before quoted path to treat it as a command
156-
command = `^'${process.execPath}' ${extraArgs} -p '"${mark}" + JSON.stringify(process.env) + "${mark}"'`;
143+
command = `^'${process.execPath}' -p '"${mark}" + JSON.stringify(process.env) + "${mark}"'`;
157144
shellArgs = ['-i', '-l', '-c'];
158145
} else if (name === 'xonsh') { // #200374: native implementation is shorter
159146
command = `import os, json; print("${mark}", json.dumps(dict(os.environ)), "${mark}")`;
160147
shellArgs = ['-i', '-l', '-c'];
161148
} else {
162-
command = `'${process.execPath}' ${extraArgs} -p '"${mark}" + JSON.stringify(process.env) + "${mark}"'`;
149+
command = `'${process.execPath}' -p '"${mark}" + JSON.stringify(process.env) + "${mark}"'`;
163150

164151
if (name === 'tcsh' || name === 'csh') {
165152
shellArgs = ['-ic'];
@@ -296,37 +283,11 @@ async function doResolveShellEnv(logService: ILogService, token: CancellationTok
296283
*
297284
* @see https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_profiles?view=powershell-7.5
298285
*/
299-
async function getPowershellProfilePaths(psExecutable: string) {
286+
function getPowershellProfilePaths() {
300287
const paths: string[] = [];
301288
const userHome = homedir();
302-
if (isWindows) {
303289

304-
// "The $PSHOME variable stores the installation directory for PowerShell" --
305-
// but this is not set ambiently on the operating system.
306-
let pshome = process.env.PSHOME;
307-
if (!pshome) {
308-
if (!isAbsolute(psExecutable)) {
309-
const found = await findExecutable(psExecutable);
310-
if (!found) {
311-
return [];
312-
}
313-
314-
pshome = dirname(found);
315-
} else {
316-
pshome = dirname(psExecutable);
317-
}
318-
}
319-
320-
paths.push(
321-
join(pshome, 'Profile.ps1'), // All Users, All Hosts
322-
join(pshome, 'Microsoft.PowerShell_profile.ps1'), // All Users, Current Host
323-
join(userHome, 'Documents', 'PowerShell', 'Profile.ps1'), // Current User, All Hosts
324-
join(userHome, 'Documents', 'PowerShell', 'Microsoft.PowerShell_profile.ps1'), // Current User, Current Host
325-
326-
join(userHome, 'Documents', 'WindowsPowerShell', 'Profile.ps1'), // (Powershell 5) Current User, All Hosts
327-
join(userHome, 'Documents', 'WindowsPowerShell', 'Microsoft.PowerShell_profile.ps1'), // (Powershell 5) Current User, Current Host
328-
);
329-
} else if (isMacintosh) {
290+
if (isMacintosh) {
330291

331292
// note: powershell 7 is the first (and yet only) powershell version on posix,
332293
// so no need to look for any extra paths yet.

0 commit comments

Comments
 (0)