From 5437f387cc938cd32dcdc8f51eb5c1de046f006b Mon Sep 17 00:00:00 2001 From: "James Truher [MSFT]" Date: Mon, 19 Jun 2023 16:31:56 -0700 Subject: [PATCH] Add a new experimental feature to control native argument passing style on Windows (#18706) --- .../ExperimentalFeature.cs | 6 ++++- .../engine/InitialSessionState.cs | 22 ++++++++++++++++++- .../engine/NativeCommandProcessor.cs | 5 +++++ .../NativeCommandArguments.Tests.ps1 | 7 +++++- 4 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs b/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs index 44646ef128..45f8c98e7a 100644 --- a/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs +++ b/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs @@ -28,6 +28,7 @@ namespace System.Management.Automation internal const string PSFeedbackProvider = "PSFeedbackProvider"; internal const string PSCommandWithArgs = "PSCommandWithArgs"; internal const string PSConstrainedAuditLogging = "PSConstrainedAuditLogging"; + internal const string PSWindowsNativeCommandArgPassing = "PSWindowsNativeCommandArgPassing"; #endregion @@ -139,7 +140,10 @@ namespace System.Management.Automation description: "Enable `-CommandWithArgs` parameter for pwsh"), new ExperimentalFeature( name: PSConstrainedAuditLogging, - description: "PowerShell restriction logging when WDAC (Windows Defender Application Control) Code Integrity policy is set to Audit mode.") + description: "PowerShell restriction logging when WDAC (Windows Defender Application Control) Code Integrity policy is set to Audit mode."), + new ExperimentalFeature( + name: "PSWindowsNativeCommandArgPassing", + description: "Enable 'Windows' as the native command argument passing mode"), }; EngineExperimentalFeatures = new ReadOnlyCollection(engineFeatures); diff --git a/src/System.Management.Automation/engine/InitialSessionState.cs b/src/System.Management.Automation/engine/InitialSessionState.cs index d5cdfe1eba..6e5d26dd16 100644 --- a/src/System.Management.Automation/engine/InitialSessionState.cs +++ b/src/System.Management.Automation/engine/InitialSessionState.cs @@ -4534,7 +4534,7 @@ end { builtinVariables.Add( new SessionStateVariableEntry( SpecialVariables.NativeArgumentPassing, - Platform.IsWindows ? NativeArgumentPassingStyle.Windows : NativeArgumentPassingStyle.Standard, + GetPassingStyle(), RunspaceInit.NativeCommandArgumentPassingDescription, ScopedItemOptions.None, new ArgumentTypeConverterAttribute(typeof(NativeArgumentPassingStyle)))); @@ -4542,6 +4542,26 @@ end { BuiltInVariables = builtinVariables.ToArray(); } + /// + /// Assigns the default behavior for native argument passing. + /// If the system is non-Windows, we will return Standard. + /// If the experimental feature is enabled, we will return Windows. + /// Otherwise, we will return Legacy. + /// + private static NativeArgumentPassingStyle GetPassingStyle() + { +#if UNIX + return NativeArgumentPassingStyle.Standard; +#else + if (ExperimentalFeature.IsEnabled(ExperimentalFeature.PSWindowsNativeCommandArgPassing)) + { + return NativeArgumentPassingStyle.Windows; + } + + return NativeArgumentPassingStyle.Legacy; +#endif + } + internal static readonly SessionStateVariableEntry[] BuiltInVariables; /// diff --git a/src/System.Management.Automation/engine/NativeCommandProcessor.cs b/src/System.Management.Automation/engine/NativeCommandProcessor.cs index a5cc7874a6..117360e21e 100644 --- a/src/System.Management.Automation/engine/NativeCommandProcessor.cs +++ b/src/System.Management.Automation/engine/NativeCommandProcessor.cs @@ -583,6 +583,11 @@ namespace System.Management.Automation // Get the start info for the process. ProcessStartInfo startInfo = GetProcessStartInfo(redirectOutput, redirectError, redirectInput, soloCommand); + // Send Telemetry indicating what argument passing mode we are in. + ApplicationInsightsTelemetry.SendExperimentalUseData( + ExperimentalFeature.PSWindowsNativeCommandArgPassing, + NativeParameterBinderController.ArgumentPassingStyle.ToString()); + #if !UNIX string commandPath = this.Path.ToLowerInvariant(); if (commandPath.EndsWith("powershell.exe") || commandPath.EndsWith("powershell_ise.exe")) diff --git a/test/powershell/Language/Scripting/NativeExecution/NativeCommandArguments.Tests.ps1 b/test/powershell/Language/Scripting/NativeExecution/NativeCommandArguments.Tests.ps1 index 9cf10e8c90..8e09df9b69 100644 --- a/test/powershell/Language/Scripting/NativeExecution/NativeCommandArguments.Tests.ps1 +++ b/test/powershell/Language/Scripting/NativeExecution/NativeCommandArguments.Tests.ps1 @@ -5,7 +5,12 @@ param() Describe "Behavior is specific for each platform" -tags "CI" { It "PSNativeCommandArgumentPassing is set to 'Windows' on Windows systems" -skip:(-not $IsWindows) { - $PSNativeCommandArgumentPassing | Should -Be "Windows" + if ([Version]::TryParse($PSVersiontable.PSVersion.ToString(), [ref]$null)) { + $PSNativeCommandArgumentPassing | Should -BeExactly "Legacy" + } + else { + $PSNativeCommandArgumentPassing | Should -BeExactly "Windows" + } } It "PSNativeCommandArgumentPassing is set to 'Standard' on non-Windows systems" -skip:($IsWindows) { $PSNativeCommandArgumentPassing | Should -Be "Standard"