From ad51846daa6e677e4cdc1a2be62ec84772ad7fe2 Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Tue, 4 Dec 2018 11:56:17 -0800 Subject: [PATCH 1/5] Rename 's_wasSystemPolicyDebugPolicy' to make it less confusing and simplify 'HelperSecurity.psm1' --- .../security/wldpNativeMethods.cs | 10 +++--- .../ConstrainedLanguageDebugger.Tests.ps1 | 5 ++- .../HelpersSecurity/HelpersSecurity.psm1 | 35 ++++--------------- 3 files changed, 13 insertions(+), 37 deletions(-) diff --git a/src/System.Management.Automation/security/wldpNativeMethods.cs b/src/System.Management.Automation/security/wldpNativeMethods.cs index 34989c73c0f..fec3f2befae 100644 --- a/src/System.Management.Automation/security/wldpNativeMethods.cs +++ b/src/System.Management.Automation/security/wldpNativeMethods.cs @@ -48,11 +48,11 @@ private SystemPolicy() /// An EnforcementMode that describes the system policy public static SystemEnforcementMode GetSystemLockdownPolicy() { - if (s_wasSystemPolicyDebugPolicy || (s_systemLockdownPolicy == null)) + if (s_allowDebugOverridePolicy || (s_systemLockdownPolicy == null)) { lock (s_systemLockdownPolicyLock) { - if (s_wasSystemPolicyDebugPolicy || (s_systemLockdownPolicy == null)) + if (s_allowDebugOverridePolicy || (s_systemLockdownPolicy == null)) { s_systemLockdownPolicy = GetLockdownPolicy(null, null); } @@ -64,7 +64,7 @@ public static SystemEnforcementMode GetSystemLockdownPolicy() private static object s_systemLockdownPolicyLock = new Object(); private static Nullable s_systemLockdownPolicy = null; - private static bool s_wasSystemPolicyDebugPolicy = false; + private static bool s_allowDebugOverridePolicy = false; /// /// Gets lockdown policy as applied to a file @@ -340,7 +340,7 @@ private static SaferPolicy TestSaferPolicy(string testPathScript, string testPat private static SystemEnforcementMode GetDebugLockdownPolicy(string path) { - s_wasSystemPolicyDebugPolicy = true; + s_allowDebugOverridePolicy = true; // Support fall-back debug hook for path exclusions on non-WOA platforms if (path != null) @@ -419,7 +419,7 @@ internal static bool IsClassInApprovedList(Guid clsid) // Hook for testability. If we've got an environmental override, say that ADODB.Parameter // is not allowed. // 0000050b-0000-0010-8000-00aa006d2ea4 = ADODB.Parameter - if (s_wasSystemPolicyDebugPolicy) + if (s_allowDebugOverridePolicy) { if (String.Equals(clsid.ToString(), "0000050b-0000-0010-8000-00aa006d2ea4", StringComparison.OrdinalIgnoreCase)) { diff --git a/test/powershell/Modules/Microsoft.PowerShell.Security/ConstrainedLanguageDebugger.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Security/ConstrainedLanguageDebugger.Tests.ps1 index c91074aab76..31c047a2442 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Security/ConstrainedLanguageDebugger.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Security/ConstrainedLanguageDebugger.Tests.ps1 @@ -141,13 +141,12 @@ try # script debugger command processor. $script = @' Import-Module -Name HelpersSecurity - Import-Module -Name {0} -Force Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode $ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage" - Import-Module -Name {1} -Force + Import-Module -Name {0} -Force Set-PSBreakpoint -Command PublicFn PublicFn -'@ -f "$languageModuleDirectory\TestCmdletForConstrainedLanguage.dll", $trustedManifestFilePath +'@ -f $trustedManifestFilePath [powershell] $ps = [powershell]::Create() $ps.Runspace = $runspace diff --git a/test/tools/Modules/HelpersSecurity/HelpersSecurity.psm1 b/test/tools/Modules/HelpersSecurity/HelpersSecurity.psm1 index 92195b7f358..0f48322ef66 100644 --- a/test/tools/Modules/HelpersSecurity/HelpersSecurity.psm1 +++ b/test/tools/Modules/HelpersSecurity/HelpersSecurity.psm1 @@ -10,14 +10,6 @@ if ($IsWindows) #region Using directives using System; - using System.Globalization; - using System.Reflection; - using System.Collections; - using System.Collections.Generic; - using System.IO; - using System.Security; - using System.Runtime.InteropServices; - using System.Threading; using System.Management.Automation; #endregion @@ -27,42 +19,27 @@ if ($IsWindows) public sealed class InvokeLanguageModeTestingSupportCmdlet : PSCmdlet { [Parameter()] - public SwitchParameter EnableFullLanguageMode - { - get { return enableFullLanguageMode; } - set { enableFullLanguageMode = value; } - } - private SwitchParameter enableFullLanguageMode; + public SwitchParameter EnableFullLanguageMode { get; set; } [Parameter()] - public SwitchParameter SetLockdownMode - { - get { return setLockdownMode; } - set { setLockdownMode = value; } - } - private SwitchParameter setLockdownMode; + public SwitchParameter SetLockdownMode { get; set; } [Parameter()] - public SwitchParameter RevertLockdownMode - { - get { return revertLockdownMode; } - set { revertLockdownMode = value; } - } - private SwitchParameter revertLockdownMode; + public SwitchParameter RevertLockdownMode { get; set; } protected override void BeginProcessing() { - if (enableFullLanguageMode) + if (EnableFullLanguageMode) { SessionState.LanguageMode = PSLanguageMode.FullLanguage; } - if (setLockdownMode) + if (SetLockdownMode) { Environment.SetEnvironmentVariable("__PSLockdownPolicy", "0x80000007", EnvironmentVariableTarget.Machine); } - if (revertLockdownMode) + if (RevertLockdownMode) { Environment.SetEnvironmentVariable("__PSLockdownPolicy", null, EnvironmentVariableTarget.Machine); } From 361337289f1a3a29f46a63ddc63f292ad3cb8def Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Tue, 4 Dec 2018 11:59:44 -0800 Subject: [PATCH 2/5] Remove the unneeded 'IsInbox' to avoid running 'IsNanoServer' and 'IsIoT' at startup time --- .../CoreCLR/CorePsPlatform.cs | 27 ------------------- .../engine/Utils.cs | 2 +- 2 files changed, 1 insertion(+), 28 deletions(-) diff --git a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs index becede6497c..233b24a4426 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs @@ -121,32 +121,6 @@ public static bool IsIoT } } - /// - /// True if it is the inbox powershell for NanoServer or IoT. - /// - internal static bool IsInbox - { - get - { -#if UNIX - return false; -#else - if (_isInbox.HasValue) { return _isInbox.Value; } - - _isInbox = false; - if (IsNanoServer || IsIoT) - { - _isInbox = string.Equals( - Utils.DefaultPowerShellAppBase, - Utils.GetApplicationBaseFromRegistry(Utils.DefaultPowerShellShellID), - StringComparison.OrdinalIgnoreCase); - } - - return _isInbox.Value; -#endif - } - } - /// /// True if underlying system is Windows Desktop. /// @@ -168,7 +142,6 @@ public static bool IsWindowsDesktop #if !UNIX private static bool? _isNanoServer = null; private static bool? _isIoT = null; - private static bool? _isInbox = null; private static bool? _isWindowsDesktop = null; #endif diff --git a/src/System.Management.Automation/engine/Utils.cs b/src/System.Management.Automation/engine/Utils.cs index c17782be1a0..3e0d959a085 100644 --- a/src/System.Management.Automation/engine/Utils.cs +++ b/src/System.Management.Automation/engine/Utils.cs @@ -480,7 +480,7 @@ internal static bool IsValidPSEditionValue(string editionValue) /// /// This is used to construct the profile path. /// - internal static string ProductNameForDirectory = Platform.IsInbox ? "WindowsPowerShell" : "PowerShell"; + internal const string ProductNameForDirectory = "PowerShell"; /// /// The subdirectory of module paths From f7bae06459a98352b4f40fe7a395a9d803a20757 Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Tue, 4 Dec 2018 12:18:59 -0800 Subject: [PATCH 3/5] Update 'BindRunspace' to avoid getting all commands and unneeded method calls --- .../engine/InitialSessionState.cs | 107 ++++++++++++------ 1 file changed, 71 insertions(+), 36 deletions(-) diff --git a/src/System.Management.Automation/engine/InitialSessionState.cs b/src/System.Management.Automation/engine/InitialSessionState.cs index 6c83d545b5a..25e9eb251b9 100644 --- a/src/System.Management.Automation/engine/InitialSessionState.cs +++ b/src/System.Management.Automation/engine/InitialSessionState.cs @@ -2360,67 +2360,104 @@ private void Bind_LoadAssemblies(ExecutionContext context) internal Exception BindRunspace(Runspace initializedRunspace, PSTraceSource runspaceInitTracer) { - // Get initial list of public commands in session. - HashSet publicCommands = new HashSet(); - foreach (CommandInfo sessionCommand in initializedRunspace.ExecutionContext.SessionState.InvokeCommand.GetCommands( - "*", CommandTypes.Alias | CommandTypes.Function | CommandTypes.Filter | CommandTypes.Cmdlet, true)) - { - if (sessionCommand.Visibility == SessionStateEntryVisibility.Public) + // Get initial list of public commands from session in a lazy way. + // We could use Lazy<> with an initializer for the same purpose, but we can save allocations + // by using the local function. It avoids allocating the delegate, and it's more efficient on + // capturing variables from the enclosing scope by using a struct. + HashSet commands = null; + HashSet GetPublicCommands() + { + if (commands != null) { - publicCommands.Add(sessionCommand); + return commands; + } + + commands = new HashSet(); + foreach (CommandInfo sessionCommand in initializedRunspace.ExecutionContext.SessionState.InvokeCommand.GetCommands( + "*", CommandTypes.Alias | CommandTypes.Function | CommandTypes.Filter | CommandTypes.Cmdlet, nameIsPattern: true)) + { + if (sessionCommand.Visibility == SessionStateEntryVisibility.Public) + { + commands.Add(sessionCommand); + } } + return commands; } - // If a user has any module with the same name as that of the core module( or nested module inside the core module) - // in his module path, then that will get loaded instead of the actual nested module (from the GAC - in our case) - // Hence, searching only from the system module path while loading the core modules var unresolvedCmdsToExpose = new HashSet(this.UnresolvedCommandsToExpose, StringComparer.OrdinalIgnoreCase); - ProcessImportModule(initializedRunspace, CoreModulesToImport, ModuleIntrinsics.GetPSHomeModulePath(), publicCommands, unresolvedCmdsToExpose); + if (CoreModulesToImport.Count > 0 || unresolvedCmdsToExpose.Count > 0) + { + // If a user has any module with the same name as that of the core module( or nested module inside the core module) + // in his module path, then that will get loaded instead of the actual nested module (from the GAC - in our case) + // Hence, searching only from the system module path while loading the core modules + ProcessImportModule(initializedRunspace, CoreModulesToImport, ModuleIntrinsics.GetPSHomeModulePath(), GetPublicCommands(), unresolvedCmdsToExpose); + } // Win8:328748 - functions defined in global scope end up in a module // Since we import the core modules, EngineSessionState's module is set to the last imported module. So, if a function is defined in global scope, it ends up in that module. // Setting the module to null fixes that. initializedRunspace.ExecutionContext.EngineSessionState.Module = null; - Exception moduleImportException = ProcessImportModule(initializedRunspace, ModuleSpecificationsToImport, string.Empty, publicCommands, unresolvedCmdsToExpose); - if (moduleImportException != null) + if (ModuleSpecificationsToImport.Count > 0 || unresolvedCmdsToExpose.Count > 0) { - runspaceInitTracer.WriteLine( - "Runspace open failed while loading module: First error {1}", moduleImportException); - return moduleImportException; + Exception moduleImportException = ProcessImportModule(initializedRunspace, ModuleSpecificationsToImport, string.Empty, GetPublicCommands(), unresolvedCmdsToExpose); + if (moduleImportException != null) + { + runspaceInitTracer.WriteLine( + "Runspace open failed while loading module: First error {1}", moduleImportException); + return moduleImportException; + } } // If we still have unresolved commands after importing specified modules, then try finding associated module for // each unresolved command and import that module. - string[] foundModuleList = GetModulesForUnResolvedCommands(unresolvedCmdsToExpose, initializedRunspace.ExecutionContext); - if (foundModuleList.Length > 0) + if (unresolvedCmdsToExpose.Count > 0) { - ProcessImportModule(initializedRunspace, foundModuleList, string.Empty, publicCommands, unresolvedCmdsToExpose); + string[] foundModuleList = GetModulesForUnResolvedCommands(unresolvedCmdsToExpose, initializedRunspace.ExecutionContext); + if (foundModuleList.Length > 0) + { + ProcessImportModule(initializedRunspace, foundModuleList, string.Empty, GetPublicCommands(), unresolvedCmdsToExpose); + } } - ProcessDynamicVariables(initializedRunspace); - ProcessCommandModifications(initializedRunspace); + // Process dynamic variables if any are defined. + if (DynamicVariablesToDefine.Count > 0) + { + ProcessDynamicVariables(initializedRunspace); + } - // Process User: drive - Exception userDriveException = ProcessUserDrive(initializedRunspace); - if (userDriveException != null) + // Process command modifications if any are defined. + if (CommandModifications.Count > 0) { - runspaceInitTracer.WriteLine( - "Runspace open failed while processing user drive with error {1}", userDriveException); + ProcessCommandModifications(initializedRunspace); + } - Exception result = PSTraceSource.NewInvalidOperationException(userDriveException, RemotingErrorIdStrings.UserDriveProcessingThrewTerminatingError, userDriveException.Message); - return result; + // Process the 'User:' drive if 'UserDriveEnabled' is set. + if (UserDriveEnabled) + { + Exception userDriveException = ProcessUserDrive(initializedRunspace); + if (userDriveException != null) + { + runspaceInitTracer.WriteLine( + "Runspace open failed while processing user drive with error {1}", userDriveException); + + Exception result = PSTraceSource.NewInvalidOperationException(userDriveException, RemotingErrorIdStrings.UserDriveProcessingThrewTerminatingError, userDriveException.Message); + return result; + } } // Process startup scripts - Exception startupScriptException = ProcessStartupScripts(initializedRunspace); - if (startupScriptException != null) + if (StartupScripts.Count > 0) { - runspaceInitTracer.WriteLine( - "Runspace open failed while running startup script: First error {1}", startupScriptException); + Exception startupScriptException = ProcessStartupScripts(initializedRunspace); + if (startupScriptException != null) + { + runspaceInitTracer.WriteLine( + "Runspace open failed while running startup script: First error {1}", startupScriptException); - Exception result = PSTraceSource.NewInvalidOperationException(startupScriptException, RemotingErrorIdStrings.StartupScriptThrewTerminatingError, startupScriptException.Message); - return result; + Exception result = PSTraceSource.NewInvalidOperationException(startupScriptException, RemotingErrorIdStrings.StartupScriptThrewTerminatingError, startupScriptException.Message); + return result; + } } // Start transcribing @@ -2639,8 +2676,6 @@ private Exception ProcessDynamicVariables(Runspace initializedRunspace) private Exception ProcessUserDrive(Runspace initializedRunspace) { - if (!UserDriveEnabled) { return null; } - Exception ex = null; try { From d32458ef80794f56c53e1f3ca76390072a633eae Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Tue, 4 Dec 2018 12:22:34 -0800 Subject: [PATCH 4/5] [Feature] Avoid creating a 'IsSafeValueVisitor' every time when 'IsScriptBlockInFactASafeHashtable' runs --- .../engine/parser/SafeValues.cs | 24 ++++++++++++------- .../engine/runtime/CompiledScriptBlock.cs | 2 +- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/System.Management.Automation/engine/parser/SafeValues.cs b/src/System.Management.Automation/engine/parser/SafeValues.cs index 92f0a913051..0e4e4c0dcbe 100644 --- a/src/System.Management.Automation/engine/parser/SafeValues.cs +++ b/src/System.Management.Automation/engine/parser/SafeValues.cs @@ -41,11 +41,7 @@ internal class IsSafeValueVisitor : ICustomAstVisitor public static bool IsAstSafe(Ast ast, GetSafeValueVisitor.SafeValueContext safeValueContext) { IsSafeValueVisitor visitor = new IsSafeValueVisitor(safeValueContext); - if ((bool)ast.Accept(visitor) && visitor._visitCount < MaxVisitCount) - { - return true; - } - return false; + return visitor.IsAstSafe(ast); } internal IsSafeValueVisitor(GetSafeValueVisitor.SafeValueContext safeValueContext) @@ -53,15 +49,27 @@ internal IsSafeValueVisitor(GetSafeValueVisitor.SafeValueContext safeValueContex _safeValueContext = safeValueContext; } + internal bool IsAstSafe(Ast ast) + { + if ((bool)ast.Accept(this) && _visitCount < MaxVisitCount) + { + return true; + } + return false; + } + + // A readonly singleton with the default SafeValueContext. + internal readonly static IsSafeValueVisitor Default = new IsSafeValueVisitor(GetSafeValueVisitor.SafeValueContext.Default); + // This is a check of the number of visits - private int _visitCount = 0; - private const int MaxVisitCount = 5000; + private uint _visitCount = 0; + private const uint MaxVisitCount = 5000; private const int MaxHashtableKeyCount = 500; // Used to determine if we are being called within a GetPowerShell() context, // which does some additional security verification outside of the scope of // what we can verify. - private GetSafeValueVisitor.SafeValueContext _safeValueContext; + private readonly GetSafeValueVisitor.SafeValueContext _safeValueContext; public object VisitErrorStatement(ErrorStatementAst errorStatementAst) { return false; } public object VisitErrorExpression(ErrorExpressionAst errorExpressionAst) { return false; } diff --git a/src/System.Management.Automation/engine/runtime/CompiledScriptBlock.cs b/src/System.Management.Automation/engine/runtime/CompiledScriptBlock.cs index 113ae5457ff..98cfdd837fa 100644 --- a/src/System.Management.Automation/engine/runtime/CompiledScriptBlock.cs +++ b/src/System.Management.Automation/engine/runtime/CompiledScriptBlock.cs @@ -263,7 +263,7 @@ bool IsScriptBlockInFactASafeHashtable() // After the above steps, we know the ScriptBlockAst is in fact just a HashtableAst, // now we need to check if the HashtableAst is safe. - return IsSafeValueVisitor.IsAstSafe(hashtableAst, GetSafeValueVisitor.SafeValueContext.Default); + return IsSafeValueVisitor.Default.IsAstSafe(hashtableAst); } } From a2625b50fa3e651e82e4a8b88edad7384c738ce0 Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Wed, 5 Dec 2018 12:07:41 -0800 Subject: [PATCH 5/5] [Feature] Rename the overload methods 'ProcessImportModule' and update comment --- .../engine/InitialSessionState.cs | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/System.Management.Automation/engine/InitialSessionState.cs b/src/System.Management.Automation/engine/InitialSessionState.cs index 25e9eb251b9..4b0ce2849b1 100644 --- a/src/System.Management.Automation/engine/InitialSessionState.cs +++ b/src/System.Management.Automation/engine/InitialSessionState.cs @@ -2360,28 +2360,30 @@ private void Bind_LoadAssemblies(ExecutionContext context) internal Exception BindRunspace(Runspace initializedRunspace, PSTraceSource runspaceInitTracer) { - // Get initial list of public commands from session in a lazy way. + // Get the initial list of public commands from session in a lazy way, so that we can defer + // the work until it's actually needed. + // // We could use Lazy<> with an initializer for the same purpose, but we can save allocations // by using the local function. It avoids allocating the delegate, and it's more efficient on // capturing variables from the enclosing scope by using a struct. - HashSet commands = null; + HashSet publicCommands = null; HashSet GetPublicCommands() { - if (commands != null) + if (publicCommands != null) { - return commands; + return publicCommands; } - commands = new HashSet(); + publicCommands = new HashSet(); foreach (CommandInfo sessionCommand in initializedRunspace.ExecutionContext.SessionState.InvokeCommand.GetCommands( "*", CommandTypes.Alias | CommandTypes.Function | CommandTypes.Filter | CommandTypes.Cmdlet, nameIsPattern: true)) { if (sessionCommand.Visibility == SessionStateEntryVisibility.Public) { - commands.Add(sessionCommand); + publicCommands.Add(sessionCommand); } } - return commands; + return publicCommands; } var unresolvedCmdsToExpose = new HashSet(this.UnresolvedCommandsToExpose, StringComparer.OrdinalIgnoreCase); @@ -2390,7 +2392,7 @@ HashSet GetPublicCommands() // If a user has any module with the same name as that of the core module( or nested module inside the core module) // in his module path, then that will get loaded instead of the actual nested module (from the GAC - in our case) // Hence, searching only from the system module path while loading the core modules - ProcessImportModule(initializedRunspace, CoreModulesToImport, ModuleIntrinsics.GetPSHomeModulePath(), GetPublicCommands(), unresolvedCmdsToExpose); + ProcessModulesToImport(initializedRunspace, CoreModulesToImport, ModuleIntrinsics.GetPSHomeModulePath(), GetPublicCommands(), unresolvedCmdsToExpose); } // Win8:328748 - functions defined in global scope end up in a module @@ -2400,7 +2402,7 @@ HashSet GetPublicCommands() if (ModuleSpecificationsToImport.Count > 0 || unresolvedCmdsToExpose.Count > 0) { - Exception moduleImportException = ProcessImportModule(initializedRunspace, ModuleSpecificationsToImport, string.Empty, GetPublicCommands(), unresolvedCmdsToExpose); + Exception moduleImportException = ProcessModulesToImport(initializedRunspace, ModuleSpecificationsToImport, string.Empty, GetPublicCommands(), unresolvedCmdsToExpose); if (moduleImportException != null) { runspaceInitTracer.WriteLine( @@ -2416,7 +2418,7 @@ HashSet GetPublicCommands() string[] foundModuleList = GetModulesForUnResolvedCommands(unresolvedCmdsToExpose, initializedRunspace.ExecutionContext); if (foundModuleList.Length > 0) { - ProcessImportModule(initializedRunspace, foundModuleList, string.Empty, GetPublicCommands(), unresolvedCmdsToExpose); + ProcessModulesToImport(initializedRunspace, foundModuleList, string.Empty, GetPublicCommands(), unresolvedCmdsToExpose); } } @@ -2840,7 +2842,7 @@ private Exception ProcessPowerShellCommand(PowerShell psToInvoke, Runspace initi return null; } - private RunspaceOpenModuleLoadException ProcessImportModule( + private RunspaceOpenModuleLoadException ProcessModulesToImport( Runspace initializedRunspace, IEnumerable moduleList, string path, @@ -2854,7 +2856,7 @@ private RunspaceOpenModuleLoadException ProcessImportModule( string moduleName = module as string; if (moduleName != null) { - exceptionToReturn = ProcessImportModule(initializedRunspace, moduleName, null, path, publicCommands); + exceptionToReturn = ProcessOneModule(initializedRunspace, moduleName, null, path, publicCommands); } else { @@ -2865,7 +2867,7 @@ private RunspaceOpenModuleLoadException ProcessImportModule( { // if only name is specified in the module spec, just try import the module // ie., don't take the performance overhead of calling GetModule. - exceptionToReturn = ProcessImportModule(initializedRunspace, moduleSpecification.Name, null, path, publicCommands); + exceptionToReturn = ProcessOneModule(initializedRunspace, moduleSpecification.Name, null, path, publicCommands); } else { @@ -2873,7 +2875,7 @@ private RunspaceOpenModuleLoadException ProcessImportModule( if (moduleInfos != null && moduleInfos.Count > 0) { - exceptionToReturn = ProcessImportModule(initializedRunspace, moduleSpecification.Name, moduleInfos[0], path, publicCommands); + exceptionToReturn = ProcessOneModule(initializedRunspace, moduleSpecification.Name, moduleInfos[0], path, publicCommands); } else { @@ -3014,7 +3016,7 @@ private IEnumerable LookupCommands( /// if is null, import module using . Otherwise, /// import module using /// - private RunspaceOpenModuleLoadException ProcessImportModule(Runspace initializedRunspace, string name, PSModuleInfo moduleInfoToLoad, string path, HashSet publicCommands) + private RunspaceOpenModuleLoadException ProcessOneModule(Runspace initializedRunspace, string name, PSModuleInfo moduleInfoToLoad, string path, HashSet publicCommands) { using (PowerShell pse = PowerShell.Create()) {