mirror of
https://github.com/PowerShell/PowerShell
synced 2026-06-08 12:12:50 +00:00
Enforce the 'CompatiblePSEditions' check for modules from the legacy 'System32' module path (#7183)
- Add `%WINDIR%\System32\WindowsPowerShell\v1.0\Modules` (Windows PowerShell $PSHOME) to the end of the default PSCore 6 module path (i.e. the module path as initially set at startup). - Cause an error to be thrown by `Import-Module` when a module with `CompatiblePSEditions` not containing `"Core"` is being loaded from the 'System32' module path. - Suppress output of modules listed by `Get-Module -ListAvailable` from Windows PowerShell $PSHOME when `CompatiblePSEditions` does not contain `"Core"`. - Introduce the `-SkipCompatibilityCheck` switch parameter on both `Import-Module` and `Get-Module` to respectively allow importing incompatible modules and listing incompatible modules. - Adds a `PSEdition` column to the `PSModuleInfo` table view format. - Ensures that completions are not given for incompatible modules on the System32 module path.
This commit is contained in:
committed by
Travis Plunk
parent
da7b5216b5
commit
e857135d3c
@@ -84,6 +84,18 @@ namespace Microsoft.PowerShell.Commands
|
||||
[ArgumentCompleter(typeof(PSEditionArgumentCompleter))]
|
||||
public string PSEdition { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// When set, CompatiblePSEditions checking is disabled for modules in the System32 (Windows PowerShell) module directory.
|
||||
/// </summary>
|
||||
[Parameter(ParameterSetName = ParameterSet_AvailableLocally)]
|
||||
[Parameter(ParameterSetName = ParameterSet_AvailableInPsrpSession)]
|
||||
[Parameter(ParameterSetName = ParameterSet_AvailableInCimSession)]
|
||||
public SwitchParameter SkipEditionCheck
|
||||
{
|
||||
get { return (SwitchParameter)BaseSkipEditionCheck; }
|
||||
set { BaseSkipEditionCheck = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If specified, then Get-Module refreshes the internal cmdlet analysis cache
|
||||
/// </summary>
|
||||
@@ -341,6 +353,18 @@ namespace Microsoft.PowerShell.Commands
|
||||
ThrowTerminatingError(error);
|
||||
}
|
||||
|
||||
// -SkipEditionCheck only makes sense for -ListAvailable (otherwise the module is already loaded)
|
||||
if (SkipEditionCheck && !ListAvailable)
|
||||
{
|
||||
ErrorRecord error = new ErrorRecord(
|
||||
new InvalidOperationException(Modules.SkipEditionCheckNotSupportedWithoutListAvailable),
|
||||
nameof(Modules.SkipEditionCheckNotSupportedWithoutListAvailable),
|
||||
ErrorCategory.InvalidOperation,
|
||||
targetObject: null);
|
||||
|
||||
ThrowTerminatingError(error);
|
||||
}
|
||||
|
||||
var strNames = new List<string>();
|
||||
if (Name != null)
|
||||
{
|
||||
@@ -423,27 +447,12 @@ namespace Microsoft.PowerShell.Commands
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine whether a module info matches a given module specification table and specified PSEdition value.
|
||||
/// </summary>
|
||||
/// <param name="moduleInfo"></param>
|
||||
/// <param name="moduleSpecTable"></param>
|
||||
/// <param name="edition"></param>
|
||||
/// <returns></returns>
|
||||
private static bool ModuleMatch(PSModuleInfo moduleInfo, IDictionary<string, ModuleSpecification> moduleSpecTable, string edition)
|
||||
{
|
||||
ModuleSpecification moduleSpecification;
|
||||
return (String.IsNullOrEmpty(edition) || moduleInfo.CompatiblePSEditions.Contains(edition, StringComparer.OrdinalIgnoreCase)) &&
|
||||
(!moduleSpecTable.TryGetValue(moduleInfo.Name, out moduleSpecification) || ModuleIntrinsics.IsModuleMatchingModuleSpec(moduleInfo, moduleSpecification));
|
||||
}
|
||||
|
||||
private void GetAvailableViaCimSession(IEnumerable<string> names, IDictionary<string, ModuleSpecification> moduleSpecTable,
|
||||
CimSession cimSession, Uri resourceUri, string cimNamespace)
|
||||
{
|
||||
var remoteModules = GetAvailableViaCimSessionCore(names, cimSession, resourceUri, cimNamespace);
|
||||
IEnumerable<PSModuleInfo> remoteModules = GetAvailableViaCimSessionCore(names, cimSession, resourceUri, cimNamespace);
|
||||
|
||||
foreach (var remoteModule in remoteModules.Where(remoteModule => ModuleMatch(remoteModule, moduleSpecTable, PSEdition))
|
||||
)
|
||||
foreach (PSModuleInfo remoteModule in FilterModulesForEditionAndSpecification(remoteModules, moduleSpecTable))
|
||||
{
|
||||
RemoteDiscoveryHelper.AssociatePSModuleInfoWithSession(remoteModule, cimSession, resourceUri,
|
||||
cimNamespace);
|
||||
@@ -453,10 +462,9 @@ namespace Microsoft.PowerShell.Commands
|
||||
|
||||
private void GetAvailableViaPsrpSession(string[] names, IDictionary<string, ModuleSpecification> moduleSpecTable, PSSession session)
|
||||
{
|
||||
var remoteModules = GetAvailableViaPsrpSessionCore(names, session.Runspace);
|
||||
IEnumerable<PSModuleInfo> remoteModules = GetAvailableViaPsrpSessionCore(names, session.Runspace);
|
||||
|
||||
foreach (var remoteModule in remoteModules.Where(remoteModule => ModuleMatch(remoteModule, moduleSpecTable, PSEdition))
|
||||
)
|
||||
foreach (PSModuleInfo remoteModule in FilterModulesForEditionAndSpecification(remoteModules, moduleSpecTable))
|
||||
{
|
||||
RemoteDiscoveryHelper.AssociatePSModuleInfoWithSession(remoteModule, session);
|
||||
this.WriteObject(remoteModule);
|
||||
@@ -465,14 +473,10 @@ namespace Microsoft.PowerShell.Commands
|
||||
|
||||
private void GetAvailableLocallyModules(string[] names, IDictionary<string, ModuleSpecification> moduleSpecTable, bool all)
|
||||
{
|
||||
var refresh = Refresh.IsPresent;
|
||||
var modules = GetModule(names, all, refresh);
|
||||
|
||||
foreach (
|
||||
var psModule in
|
||||
modules.Where(module => ModuleMatch(module, moduleSpecTable, PSEdition)).Select(module => new PSObject(module))
|
||||
)
|
||||
IEnumerable<PSModuleInfo> modules = GetModule(names, all, Refresh);
|
||||
foreach (PSModuleInfo module in FilterModulesForEditionAndSpecification(modules, moduleSpecTable))
|
||||
{
|
||||
var psModule = new PSObject(module);
|
||||
psModule.TypeNames.Insert(0, "ModuleInfoGrouping");
|
||||
WriteObject(psModule);
|
||||
}
|
||||
@@ -482,14 +486,74 @@ namespace Microsoft.PowerShell.Commands
|
||||
{
|
||||
var modulesToWrite = Context.Modules.GetModules(names, all);
|
||||
|
||||
foreach (
|
||||
var moduleInfo in
|
||||
modulesToWrite.Where(moduleInfo => ModuleMatch(moduleInfo, moduleSpecTable, PSEdition))
|
||||
)
|
||||
foreach (PSModuleInfo moduleInfo in FilterModulesForEditionAndSpecification(modulesToWrite, moduleSpecTable))
|
||||
{
|
||||
WriteObject(moduleInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Filter an enumeration of PowerShell modules based on the required PowerShell edition
|
||||
/// and the module specification constraints set for each module (if any).
|
||||
/// </summary>
|
||||
/// <param name="modules">The modules to filter through.</param>
|
||||
/// <param name="moduleSpecificationTable">Module constraints, keyed by module name, to filter modules of that name by.</param>
|
||||
/// <returns>All modules from the original input that meet both any module edition and module specification constraints provided.</returns>
|
||||
private IEnumerable<PSModuleInfo> FilterModulesForEditionAndSpecification(
|
||||
IEnumerable<PSModuleInfo> modules,
|
||||
IDictionary<string, ModuleSpecification> moduleSpecificationTable)
|
||||
{
|
||||
#if !UNIX
|
||||
// Edition check only applies to Windows System32 module path
|
||||
if (!SkipEditionCheck && ListAvailable && !All)
|
||||
{
|
||||
modules = modules.Where(module => module.IsConsideredEditionCompatible);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!String.IsNullOrEmpty(PSEdition))
|
||||
{
|
||||
modules = modules.Where(module => module.CompatiblePSEditions.Contains(PSEdition, StringComparer.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
if (moduleSpecificationTable != null && moduleSpecificationTable.Count > 0)
|
||||
{
|
||||
modules = FilterModulesForSpecificationMatch(modules, moduleSpecificationTable);
|
||||
}
|
||||
|
||||
return modules;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Take an enumeration of modules and only return those that match a specification
|
||||
/// in the given specification table, or have no corresponding entry in the specification table.
|
||||
/// </summary>
|
||||
/// <param name="modules">The modules to filter by specification match.</param>
|
||||
/// <param name="moduleSpecificationTable">The specification lookup table to filter the modules on.</param>
|
||||
/// <returns>The modules that match their corresponding table entry, or which have no table entry.</returns>
|
||||
private static IEnumerable<PSModuleInfo> FilterModulesForSpecificationMatch(
|
||||
IEnumerable<PSModuleInfo> modules,
|
||||
IDictionary<string, ModuleSpecification> moduleSpecificationTable)
|
||||
{
|
||||
Dbg.Assert(moduleSpecificationTable != null, $"Caller to verify that {nameof(moduleSpecificationTable)} is not null");
|
||||
Dbg.Assert(moduleSpecificationTable.Count != 0, $"Caller to verify that {nameof(moduleSpecificationTable)} is not empty");
|
||||
|
||||
foreach (PSModuleInfo module in modules)
|
||||
{
|
||||
// No table entry means we return the module
|
||||
if (!moduleSpecificationTable.TryGetValue(module.Name, out ModuleSpecification moduleSpecification))
|
||||
{
|
||||
yield return module;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Modules with table entries only get returned if they match them
|
||||
if (ModuleIntrinsics.IsModuleMatchingModuleSpec(module, moduleSpecification))
|
||||
{
|
||||
yield return module;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -513,5 +577,4 @@ namespace Microsoft.PowerShell.Commands
|
||||
}
|
||||
}
|
||||
}
|
||||
} // Microsoft.PowerShell.Commands
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user