mirror of
https://github.com/PowerShell/PowerShell
synced 2026-06-08 12:12:50 +00:00
Added setting to disable implicit WinPS module loading (#11332)
This commit is contained in:
committed by
Aditya Patwardhan
parent
ee71081432
commit
ea48e07c48
@@ -103,6 +103,12 @@ namespace System.Management.Automation
|
||||
var moduleManifestProperties = PsUtils.GetModuleManifestProperties(modulePath, PsUtils.FastModuleManifestAnalysisPropertyNames);
|
||||
if (moduleManifestProperties != null)
|
||||
{
|
||||
if (!Configuration.PowerShellConfig.Instance.IsImplicitWinCompatEnabled() && ModuleIsEditionIncompatible(modulePath, moduleManifestProperties))
|
||||
{
|
||||
ModuleIntrinsics.Tracer.WriteLine($"Module lies on the Windows System32 legacy module path and is incompatible with current PowerShell edition, skipping module: {modulePath}");
|
||||
return null;
|
||||
}
|
||||
|
||||
Version version;
|
||||
if (ModuleUtils.IsModuleInVersionSubdirectory(modulePath, out version))
|
||||
{
|
||||
@@ -485,6 +491,14 @@ namespace System.Management.Automation
|
||||
{
|
||||
ModuleIntrinsics.Tracer.WriteLine("Requested caching for {0}", module.Name);
|
||||
|
||||
// Don't cache incompatible modules on the system32 module path even if loaded with
|
||||
// -SkipEditionCheck, since it will break subsequent sessions
|
||||
if (!Configuration.PowerShellConfig.Instance.IsImplicitWinCompatEnabled() && !module.IsConsideredEditionCompatible)
|
||||
{
|
||||
ModuleIntrinsics.Tracer.WriteLine($"Module '{module.Name}' not edition compatible and not cached.");
|
||||
return;
|
||||
}
|
||||
|
||||
DateTime lastWriteTime;
|
||||
ModuleCacheEntry moduleCacheEntry;
|
||||
GetModuleEntryFromCache(module.Path, out lastWriteTime, out moduleCacheEntry);
|
||||
|
||||
@@ -2363,18 +2363,58 @@ namespace Microsoft.PowerShell.Commands
|
||||
bool isConsideredCompatible = ModuleUtils.IsPSEditionCompatible(moduleManifestPath, inferredCompatiblePSEditions);
|
||||
if (!BaseSkipEditionCheck && !isConsideredCompatible)
|
||||
{
|
||||
if (importingModule)
|
||||
if (PowerShellConfig.Instance.IsImplicitWinCompatEnabled())
|
||||
{
|
||||
IList<PSModuleInfo> moduleProxies = ImportModulesUsingWinCompat(new string [] {moduleManifestPath}, null, new ImportModuleOptions());
|
||||
if (importingModule)
|
||||
{
|
||||
IList<PSModuleInfo> moduleProxies = ImportModulesUsingWinCompat(new string [] {moduleManifestPath}, null, new ImportModuleOptions());
|
||||
|
||||
// we are loading by a single ManifestPath so expect max of 1
|
||||
if(moduleProxies.Count > 0)
|
||||
{
|
||||
return moduleProxies[0];
|
||||
// we are loading by a single ManifestPath so expect max of 1
|
||||
if(moduleProxies.Count > 0)
|
||||
{
|
||||
return moduleProxies[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
containedErrors = true;
|
||||
if (writingErrors)
|
||||
{
|
||||
return null;
|
||||
message = StringUtil.Format(
|
||||
Modules.ImplicitWinCompatDisabled,
|
||||
moduleManifestPath,
|
||||
string.Join(',', inferredCompatiblePSEditions));
|
||||
|
||||
ErrorRecord er = new ErrorRecord(
|
||||
new InvalidOperationException(message),
|
||||
nameof(Modules) + "_" + nameof(Modules.ImplicitWinCompatDisabled),
|
||||
ErrorCategory.ResourceUnavailable,
|
||||
moduleManifestPath);
|
||||
|
||||
WriteError(er);
|
||||
}
|
||||
|
||||
if (bailOnFirstError)
|
||||
{
|
||||
// If we're trying to load the module, return null so that caches
|
||||
// are not polluted
|
||||
if (importingModule)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// If we return null with Get-Module, a fake module info will be created. Since
|
||||
// we want to suppress output of the module, we need to do that here.
|
||||
return new PSModuleInfo(moduleManifestPath, context: null, sessionState: null)
|
||||
{
|
||||
HadErrorsLoading = true,
|
||||
IsConsideredEditionCompatible = false,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ namespace System.Management.Automation.Configuration
|
||||
{
|
||||
private const string ConfigFileName = "powershell.config.json";
|
||||
private const string ExecutionPolicyDefaultShellKey = "Microsoft.PowerShell:ExecutionPolicy";
|
||||
private const string DisableImplicitWinCompatKey = "DisableImplicitWinCompat";
|
||||
|
||||
// Provide a singleton
|
||||
internal static readonly PowerShellConfig Instance = new PowerShellConfig();
|
||||
@@ -214,6 +215,18 @@ namespace System.Management.Automation.Configuration
|
||||
}
|
||||
}
|
||||
|
||||
internal bool IsImplicitWinCompatEnabled()
|
||||
{
|
||||
bool? settingValue = ReadValueFromFile<bool?>(ConfigScope.CurrentUser, DisableImplicitWinCompatKey);
|
||||
if (!settingValue.HasValue)
|
||||
{
|
||||
// if the setting is not mentioned in configuration files, then the default DisableImplicitWinCompat value is False
|
||||
settingValue = ReadValueFromFile<bool?>(ConfigScope.AllUsers, DisableImplicitWinCompatKey, defaultValue: false);
|
||||
}
|
||||
|
||||
return !settingValue.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Corresponding settings of the original Group Policies.
|
||||
/// </summary>
|
||||
|
||||
@@ -594,6 +594,9 @@
|
||||
<data name="PSEditionNotSupported" xml:space="preserve">
|
||||
<value>Module '{0}' does not support current PowerShell edition '{1}'. Its supported editions are '{2}'. Use 'Import-Module -SkipEditionCheck' to ignore the compatibility of this module.</value>
|
||||
</data>
|
||||
<data name="ImplicitWinCompatDisabled" xml:space="preserve">
|
||||
<value>Module '{0}' supports PowerShell edition '{1}' and cannot be loaded implicitly using the Windows Compatibility feature because it is disabled in the settings file. Use 'Import-Module -UseWindowsPowerShell' to load this module with Windows PowerShell or 'Import-Module -SkipEditionCheck' to try to load the module with the current PowerShell.</value>
|
||||
</data>
|
||||
<data name="ExperimentalFeatureNameMissingOrEmpty" xml:space="preserve">
|
||||
<value>A non-empty string value should be specified for an experimental feature declared in the module manifest.</value>
|
||||
</data>
|
||||
|
||||
+53
-6
@@ -390,13 +390,26 @@ Describe "Import-Module from CompatiblePSEditions-checked paths" -Tag "CI" {
|
||||
}
|
||||
}
|
||||
|
||||
Describe "Additional tests for Import-Module with WinCompat" -Tag "CI" {
|
||||
Describe "Additional tests for Import-Module with WinCompat" -Tag "Feature" {
|
||||
|
||||
BeforeAll {
|
||||
$originalDefaultParameterValues = $PSDefaultParameterValues.Clone()
|
||||
if ( ! $IsWindows ) {
|
||||
$PSDefaultParameterValues["it:skip"] = $true
|
||||
}
|
||||
|
||||
$ModuleName = "DesktopModule"
|
||||
$ModuleName2 = "DesktopModule2"
|
||||
$basePath = Join-Path $TestDrive "WinCompatModules"
|
||||
Remove-Item -Path $basePath -Recurse -ErrorAction SilentlyContinue
|
||||
# create an incompatible module that generates an error on import
|
||||
New-EditionCompatibleModule -ModuleName $ModuleName -CompatiblePSEditions "Desktop" -Dir $basePath -ErrorGenerationCode '1/0;'
|
||||
# create an incompatible module
|
||||
New-EditionCompatibleModule -ModuleName $ModuleName2 -CompatiblePSEditions "Desktop" -Dir $basePath
|
||||
}
|
||||
|
||||
AfterAll {
|
||||
$global:PSDefaultParameterValues = $originalDefaultParameterValues
|
||||
}
|
||||
|
||||
Context "Tests that ErrorAction/WarningAction have effect when Import-Module with WinCompat is used" {
|
||||
@@ -408,34 +421,68 @@ Describe "Additional tests for Import-Module with WinCompat" -Tag "CI" {
|
||||
Restore-ModulePath
|
||||
}
|
||||
|
||||
It "Verify that Error is generated with default ErrorAction" -Skip:(-not $IsWindows) {
|
||||
It "Verify that Error is generated with default ErrorAction" {
|
||||
$LogPath = Join-Path $TestDrive (New-Guid).ToString()
|
||||
pwsh -NoProfile -NonInteractive -c "[System.Management.Automation.Internal.InternalTestHooks]::SetTestHook('TestWindowsPowerShellPSHomeLocation', `'$basePath`');Import-Module $ModuleName" *> $LogPath
|
||||
$LogPath | Should -FileContentMatch 'divide by zero'
|
||||
}
|
||||
|
||||
It "Verify that Warning is generated with default WarningAction" -Skip:(-not $IsWindows) {
|
||||
It "Verify that Warning is generated with default WarningAction" {
|
||||
$LogPath = Join-Path $TestDrive (New-Guid).ToString()
|
||||
pwsh -NoProfile -NonInteractive -c "[System.Management.Automation.Internal.InternalTestHooks]::SetTestHook('TestWindowsPowerShellPSHomeLocation', `'$basePath`');Import-Module $ModuleName" *> $LogPath
|
||||
$LogPath | Should -FileContentMatch 'loaded in Windows PowerShell'
|
||||
}
|
||||
|
||||
It "Verify that Error is Not generated with -ErrorAction Ignore" -Skip:(-not $IsWindows) {
|
||||
It "Verify that Error is Not generated with -ErrorAction Ignore" {
|
||||
$LogPath = Join-Path $TestDrive (New-Guid).ToString()
|
||||
pwsh -NoProfile -NonInteractive -c "[System.Management.Automation.Internal.InternalTestHooks]::SetTestHook('TestWindowsPowerShellPSHomeLocation', `'$basePath`');Import-Module $ModuleName -ErrorAction Ignore" *> $LogPath
|
||||
$LogPath | Should -Not -FileContentMatch 'divide by zero'
|
||||
}
|
||||
|
||||
It "Verify that Warning is Not generated with -WarningAction Ignore" -Skip:(-not $IsWindows) {
|
||||
It "Verify that Warning is Not generated with -WarningAction Ignore" {
|
||||
$LogPath = Join-Path $TestDrive (New-Guid).ToString()
|
||||
pwsh -NoProfile -NonInteractive -c "[System.Management.Automation.Internal.InternalTestHooks]::SetTestHook('TestWindowsPowerShellPSHomeLocation', `'$basePath`');Import-Module $ModuleName -WarningAction Ignore" *> $LogPath
|
||||
$LogPath | Should -Not -FileContentMatch 'loaded in Windows PowerShell'
|
||||
}
|
||||
|
||||
It "Fails to import incompatible module if implicit WinCompat is disabled in config" {
|
||||
$LogPath = Join-Path $TestDrive (New-Guid).ToString()
|
||||
$ConfigPath = Join-Path $TestDrive 'powershell.config.json'
|
||||
'{"DisableImplicitWinCompat" : "True"}' | Out-File -Force $ConfigPath
|
||||
pwsh -NoProfile -NonInteractive -settingsFile $ConfigPath -c "[System.Management.Automation.Internal.InternalTestHooks]::SetTestHook('TestWindowsPowerShellPSHomeLocation', `'$basePath`');Import-Module $ModuleName2" *> $LogPath
|
||||
$LogPath | Should -FileContentMatch 'cannot be loaded implicitly using the Windows Compatibility'
|
||||
}
|
||||
|
||||
It "Fails to auto-import incompatible module during CommandDiscovery\ModuleAutoload if implicit WinCompat is Disabled in config" {
|
||||
$LogPath = Join-Path $TestDrive (New-Guid).ToString()
|
||||
$ConfigPath = Join-Path $TestDrive 'powershell.config.json'
|
||||
'{"DisableImplicitWinCompat" : "True"}' | Out-File -Force $ConfigPath
|
||||
pwsh -NoProfile -NonInteractive -settingsFile $ConfigPath -c "[System.Management.Automation.Internal.InternalTestHooks]::SetTestHook('TestWindowsPowerShellPSHomeLocation', `'$basePath`'); Test-$ModuleName2" *> $LogPath
|
||||
$LogPath | Should -FileContentMatch 'not recognized as the name of a cmdlet'
|
||||
}
|
||||
|
||||
It "Successfully auto-imports incompatible module during CommandDiscovery\ModuleAutoload if implicit WinCompat is Enabled in config" {
|
||||
$LogPath = Join-Path $TestDrive (New-Guid).ToString()
|
||||
$ConfigPath = Join-Path $TestDrive 'powershell.config.json'
|
||||
'{"DisableImplicitWinCompat" : "False"}' | Out-File -Force $ConfigPath
|
||||
pwsh -NoProfile -NonInteractive -settingsFile $ConfigPath -c "[System.Management.Automation.Internal.InternalTestHooks]::SetTestHook('TestWindowsPowerShellPSHomeLocation', `'$basePath`'); Test-$ModuleName2" *> $LogPath
|
||||
$LogPath | Should -FileContentMatch 'True'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Describe "PSModulePath changes interacting with other PowerShell processes" -Tag "Feature" {
|
||||
$PSDefaultParameterValues = @{ 'It:Skip' = (-not $IsWindows) }
|
||||
|
||||
BeforeAll {
|
||||
$originalDefaultParameterValues = $PSDefaultParameterValues.Clone()
|
||||
if ( ! $IsWindows ) {
|
||||
$PSDefaultParameterValues["it:skip"] = $true
|
||||
}
|
||||
}
|
||||
|
||||
AfterAll {
|
||||
$global:PSDefaultParameterValues = $originalDefaultParameterValues
|
||||
}
|
||||
|
||||
Context "System32 module path prepended to PSModulePath" {
|
||||
BeforeAll {
|
||||
|
||||
Reference in New Issue
Block a user