MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System;5using System.Collections;6using System.Collections.Generic;7using System.Collections.ObjectModel;8using System.Diagnostics.CodeAnalysis;9using System.Globalization;10using System.IO;11using System.Linq;12using System.Management.Automation;13using System.Management.Automation.Configuration;14using System.Management.Automation.Internal;15using System.Management.Automation.Language;16using System.Management.Automation.Runspaces;17using System.Management.Automation.Security;18using System.Reflection;19using System.Runtime.InteropServices;20using System.Text;21using System.Xml;22using System.Diagnostics;23 24using Microsoft.PowerShell.Cmdletization;25 26using Dbg = System.Management.Automation.Diagnostics;27 28//29// Now define the set of commands for manipulating modules.30//31 32namespace Microsoft.PowerShell.Commands33{34 #region ModuleCmdletBase class35 36 /// <summary>37 /// This is the base class for some of the module cmdlets. It contains a number of38 /// utility functions for these classes.39 /// </summary>40 public class ModuleCmdletBase : PSCmdlet41 {42 /// <summary>43 /// Flags defining how a module manifest should be processed.44 /// </summary>45 [Flags]46 internal enum ManifestProcessingFlags47 {48 /// <summary>49 /// Write errors (otherwise no non-terminating-errors are written)50 /// </summary>51 WriteErrors = 0x1,52 53 /// <summary>54 /// Return null on first error (otherwise we try to process other elements of the manifest)55 /// </summary>56 NullOnFirstError = 0x2,57 58 /// <summary>59 /// Load elements of the manifest (i.e. types/format.ps1xml, nested modules, etc.)60 /// </summary>61 LoadElements = 0x4,62 63 /// <summary>64 /// Write warnings.65 /// </summary>66 WriteWarnings = 0x8,67 68 /// <summary>69 /// Force full module manifest processing.70 /// </summary>71 Force = 0x10,72 73 /// <summary>74 /// Ignore PowerShellHostName and PowerShellHostVersion while75 /// processing Manifest fields.76 /// This is used for GetModule where loading elements does not happen.77 /// </summary>78 IgnoreHostNameAndHostVersion = 0x20,79 }80 81 /// <summary>82 /// Options set during module import.83 /// </summary>84 [SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")]85 protected internal struct ImportModuleOptions86 {87 /// <summary>88 /// Holds the value of NoClobber parameter in Import-Module89 /// This is used when importing modules.90 /// </summary>91 internal bool NoClobber;92 93 /// <summary>94 /// If Scope parameter is Local, this is true.95 /// </summary>96 internal bool Local;97 98 /// <summary>99 /// Lets nested module import to export all of its functions, regardless of language boundaries.100 /// This will be allowed when the manifest explicitly exports functions which will limit all visible module functions.101 /// </summary>102 internal bool AllowNestedModuleFunctionsToExport;103 104 /// <summary>105 /// Flag that controls Export-PSSession -AllowClobber parameter in generating proxy modules from remote sessions.106 /// Historically -AllowClobber in these scenarios was set as True.107 /// </summary>108 internal bool NoClobberExportPSSession;109 110 /// <summary>111 /// Flag that controls skipping the System32 module path when searching a module in module paths. It also suppresses112 /// writing out errors when specified.113 /// </summary>114 internal bool SkipSystem32ModulesAndSuppressError;115 }116 117 /// <summary>118 /// This parameter specified a prefix used to modify names of imported commands.119 /// </summary>120 internal string BasePrefix { get; set; } = string.Empty;121 122 /// <summary>123 /// Flags -force operations.124 /// </summary>125 internal bool BaseForce { get; set; }126 127 /// <summary>128 /// Flags -global operations (affects what gets returned by TargetSessionState)129 /// </summary>130 internal bool BaseGlobal { get; set; }131 132 /// <summary>133 /// If set, CompatiblePSEditions checking will be disabled for modules on the System32 path.134 /// </summary>135 internal bool BaseSkipEditionCheck { get; set; }136 137 internal SessionState TargetSessionState138 {139 get140 {141 if (BaseGlobal)142 {143 return this.Context.TopLevelSessionState.PublicSessionState;144 }145 else146 {147 return this.Context.SessionState;148 }149 }150 }151 152 /// <summary>153 /// Flags -passthru operations.154 /// </summary>155 internal bool BasePassThru { get; set; }156 157 /// <summary>158 /// Flags -passthru operations.159 /// </summary>160 internal bool BaseAsCustomObject { get; set; }161 162 /// <summary>163 /// Wildcard patterns for the function to import.164 /// </summary>165 [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Cmdlet parameters.")]166 internal List<WildcardPattern> BaseFunctionPatterns { get; set; }167 168 /// <summary>169 /// Wildcard patterns for the cmdlets to import.170 /// </summary>171 [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Cmdlet parameters.")]172 internal List<WildcardPattern> BaseCmdletPatterns { get; set; }173 174 /// <summary>175 /// Wildcard patterns for the variables to import.176 /// </summary>177 [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Cmdlet parameters.")]178 internal List<WildcardPattern> BaseVariablePatterns { get; set; }179 180 /// <summary>181 /// Wildcard patterns for the aliases to import.182 /// </summary>183 [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Cmdlet parameters.")]184 internal List<WildcardPattern> BaseAliasPatterns { get; set; }185 186 /// <summary>187 /// The minimum version number to check the module against. Used the underlying property188 /// for derived cmdlet parameters.189 /// </summary>190 internal Version BaseMinimumVersion { get; set; }191 192 /// <summary>193 /// The maximum version number to check the module against. Used the underlying property194 /// for derived cmdlet parameters.195 /// </summary>196 internal Version BaseMaximumVersion { get; set; }197 198 /// <summary>199 /// The version number to check the module against. Used the underlying property200 /// for derived cmdlet parameters.201 /// </summary>202 internal Version BaseRequiredVersion { get; set; }203 204 /// <summary>205 /// The Guid to check the module against. Used the underlying property206 /// for derived cmdlet parameters.207 /// </summary>208 internal Guid? BaseGuid { get; set; }209 210 /// <summary>211 /// The arguments to pass to the scriptblock used to create the module.212 /// </summary>213 [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Cmdlets use arrays for parameters.")]214 protected object[] BaseArgumentList { get; set; }215 216 /// <summary>217 /// Disable warnings on cmdlet and function names that have non-standard verbs218 /// or non-standard characters in the noun.219 /// </summary>220 protected bool BaseDisableNameChecking { get; set; } = true;221 222 /// <summary>223 /// Add module path to app domain level module path cache if name is not rooted.224 /// </summary>225 protected bool AddToAppDomainLevelCache { get; set; } = false;226 227 /// <summary>228 /// A handy match all pattern used to initialize various import and export lists...229 /// </summary>230 internal List<WildcardPattern> MatchAll231 {232 get233 {234 if (_matchAll == null)235 {236 _matchAll = new List<WildcardPattern>();237 _matchAll.Add(WildcardPattern.Get("*", WildcardOptions.IgnoreCase));238 }239 240 return _matchAll;241 }242 }243 244 private List<WildcardPattern> _matchAll;245 246 // The list of commands permitted in a module manifest247 internal static readonly string[] PermittedCmdlets = new string[] {248 "Import-LocalizedData", "ConvertFrom-StringData", "Write-Host", "Out-Host", "Join-Path" };249 250 internal static readonly string[] ModuleManifestMembers = new string[] {251 "ModuleToProcess",252 "NestedModules",253 "GUID",254 "Author",255 "CompanyName",256 "Copyright",257 "ModuleVersion",258 "Description",259 "PowerShellVersion",260 "PowerShellHostName",261 "PowerShellHostVersion",262 "CLRVersion",263 "DotNetFrameworkVersion",264 "ProcessorArchitecture",265 "RequiredModules",266 "TypesToProcess",267 "FormatsToProcess",268 "ScriptsToProcess",269 "PrivateData",270 "RequiredAssemblies",271 "ModuleList",272 "FileList",273 "FunctionsToExport",274 "VariablesToExport",275 "AliasesToExport",276 "CmdletsToExport",277 "DscResourcesToExport",278 "CompatiblePSEditions",279 "HelpInfoURI",280 "RootModule",281 "DefaultCommandPrefix"282 };283 284 private static readonly string[] s_moduleVersionMembers = new string[] {285 "ModuleName",286 "GUID",287 "ModuleVersion"288 };289 290 /// <summary>291 /// List of PowerShell built-in modules that are shipped with PowerShell only, not on PS Gallery.292 /// </summary>293 protected static readonly HashSet<string> BuiltInModules = new(StringComparer.OrdinalIgnoreCase)294 {295 "CimCmdlets",296 "Microsoft.PowerShell.Diagnostics",297 "Microsoft.PowerShell.Host",298 "Microsoft.PowerShell.Management",299 "Microsoft.PowerShell.Security",300 "Microsoft.PowerShell.Utility",301 "Microsoft.WSMan.Management",302 "PSDiagnostics",303 };304 305 /// <summary>306 /// When module manifests lack a CompatiblePSEditions field,307 /// they will be treated as if they have this value.308 /// The PSModuleInfo will still reflect the lack of value.309 /// </summary>310 internal static IReadOnlyList<string> DefaultCompatiblePSEditions { get; } = new string[]311 {312 "Desktop"313 };314 315 /// <summary>316 /// A counter for modules that are loaded using WindowsPS compat session.317 /// </summary>318 internal static int s_WindowsPowerShellCompatUsageCounter = 0;319 320 /// <summary>321 /// Session name for WindowsPS compat remoting session.322 /// </summary>323 internal const string WindowsPowerShellCompatRemotingSessionName = "WinPSCompatSession";324 325 /// <summary>326 /// Synchronization object for creation/cleanup of WindowsPS compat remoting session.327 /// </summary>328 internal static readonly object s_WindowsPowerShellCompatSyncObject = new object();329 330 private readonly Dictionary<string, PSModuleInfo> _currentlyProcessingModules = new Dictionary<string, PSModuleInfo>();331 332 internal bool LoadUsingModulePath(333 IEnumerable<string> modulePath,334 string name,335 SessionState ss,336 ImportModuleOptions options,337 ManifestProcessingFlags manifestProcessingFlags,338 out PSModuleInfo module)339 {340 return LoadUsingModulePath(parentModule: null, modulePath, name, ss, options, manifestProcessingFlags, out module);341 }342 343 internal bool LoadUsingModulePath(344 PSModuleInfo parentModule,345 IEnumerable<string> modulePath,346 string name,347 SessionState ss,348 ImportModuleOptions options,349 ManifestProcessingFlags manifestProcessingFlags,350 out PSModuleInfo module)351 {352 string extension = Path.GetExtension(name);353 string fileBaseName;354 module = null;355 if (string.IsNullOrEmpty(extension) || !ModuleIntrinsics.IsPowerShellModuleExtension(extension))356 {357 fileBaseName = name;358 extension = null;359 }360 else361 {362 fileBaseName = name.Substring(0, name.Length - extension.Length);363 }364 365 // Now search using the module path...366 bool found = false;367 foreach (string path in modulePath)368 {369 if (options.SkipSystem32ModulesAndSuppressError && ModuleUtils.IsOnSystem32ModulePath(path))370 {371 continue;372 }373#if UNIX374 foreach (string folder in Directory.EnumerateDirectories(path))375 {376 string moduleName = Path.GetFileName(folder);377 if (string.Equals(moduleName, fileBaseName, StringComparison.OrdinalIgnoreCase))378 {379 fileBaseName = moduleName;380#endif381 string qualifiedPath = Path.Combine(path, fileBaseName);382 module = LoadUsingMultiVersionModuleBase(qualifiedPath, manifestProcessingFlags, options, out found);383 if (!found)384 {385 if (name.AsSpan().IndexOfAny('\\', '/') == -1)386 {387 qualifiedPath = Path.Combine(qualifiedPath, fileBaseName);388 }389 else if (Directory.Exists(qualifiedPath))390 {391 // if it points to a directory, add the basename back onto the path...392 qualifiedPath = Path.Combine(qualifiedPath, Path.GetFileName(fileBaseName));393 }394 395 module = LoadUsingExtensions(parentModule, name, qualifiedPath, extension, null, this.BasePrefix, ss, options, manifestProcessingFlags, out found);396 }397 398 if (found)399 {400 break;401 }402#if UNIX403 }404 }405 406 if (found)407 {408 break;409 }410#endif411 }412 413 if (found)414 {415 // Cache the module's exported commands after importing it, or if the -Refresh flag is used on "Get-Module -List"416 if ((module != null) && !module.HadErrorsLoading)417 {418 AnalysisCache.CacheModuleExports(module, Context);419 }420 }421 422 return found;423 }424 425 /// <summary>426 /// Loads the latest valid version if moduleBase is a multi-versioned module directory.427 /// </summary>428 /// <param name="moduleBase">Module directory path.</param>429 /// <param name="manifestProcessingFlags">The flag that indicate manifest processing option.</param>430 /// <param name="importModuleOptions">The set of options that are used while importing a module.</param>431 /// <param name="found">True if a module was found.</param>432 /// <returns></returns>433 internal PSModuleInfo LoadUsingMultiVersionModuleBase(string moduleBase, ManifestProcessingFlags manifestProcessingFlags, ImportModuleOptions importModuleOptions, out bool found)434 {435 PSModuleInfo foundModule = null;436 found = false;437 438 foreach (var version in ModuleUtils.GetModuleVersionSubfolders(moduleBase))439 {440 // Skip the version folder if it is not equal to the required version or does not satisfy the minimum/maximum version criteria441 if (!ModuleIntrinsics.IsVersionMatchingConstraints(version, BaseRequiredVersion, BaseMinimumVersion, BaseMaximumVersion))442 {443 continue;444 }445 446 var qualifiedPathWithVersion = Path.Combine(moduleBase,447 Path.Combine(version.ToString(), Path.GetFileName(moduleBase)));448 string manifestPath = qualifiedPathWithVersion + StringLiterals.PowerShellDataFileExtension;449 var isValidModuleVersion = false;450 if (File.Exists(manifestPath))451 {452 isValidModuleVersion = version.Equals(ModuleIntrinsics.GetManifestModuleVersion(manifestPath));453 454 if (isValidModuleVersion)455 {456 foundModule = LoadUsingExtensions(null, moduleBase, qualifiedPathWithVersion,457 StringLiterals.PowerShellDataFileExtension,458 null,459 this.BasePrefix, /*SessionState*/ null,460 importModuleOptions,461 manifestProcessingFlags,462 out found);463 if (found)464 {465 break;466 }467 }468 }469 470 if (!isValidModuleVersion)471 {472 WriteVerbose(string.Format(CultureInfo.InvariantCulture, Modules.SkippingInvalidModuleVersionFolder,473 version.ToString(), moduleBase));474 }475 }476 477 return foundModule;478 }479 480 /// <summary>481 /// Load and execute the manifest psd1 file or a localized manifest psd1 file.482 /// </summary>483 private Hashtable LoadModuleManifestData(484 ExternalScriptInfo scriptInfo,485 string[] validMembers,486 ManifestProcessingFlags manifestProcessingFlags,487 ref bool containedErrors)488 {489 try490 {491 return LoadModuleManifestData(scriptInfo.Path, scriptInfo.ScriptBlock, validMembers, manifestProcessingFlags, ref containedErrors);492 }493 catch (RuntimeException pe)494 {495 if ((manifestProcessingFlags & ManifestProcessingFlags.WriteErrors) != 0)496 {497 string message = StringUtil.Format(Modules.InvalidModuleManifest, scriptInfo.Path, pe.Message);498 MissingMemberException mm = new MissingMemberException(message);499 ErrorRecord er = new ErrorRecord(mm, "Modules_InvalidManifest",500 ErrorCategory.ResourceUnavailable, scriptInfo.Path);501 WriteError(er);502 }503 504 containedErrors = true;505 return null;506 }507 }508 509 /// <summary>510 /// Extra variables that are allowed to be referenced in module manifest file.511 /// </summary>512 private static readonly string[] s_extraAllowedVariables = new string[] { SpecialVariables.PSScriptRoot, SpecialVariables.PSEdition, SpecialVariables.EnabledExperimentalFeatures };513 514 /// <summary>515 /// Load and execute the manifest psd1 file or a localized manifest psd1 file.516 /// </summary>517 internal Hashtable LoadModuleManifestData(518 string moduleManifestPath,519 ScriptBlock scriptBlock,520 string[] validMembers,521 ManifestProcessingFlags manifestProcessingFlags,522 ref bool containedErrors)523 {524 string message;525 526 var importingModule = (manifestProcessingFlags & ManifestProcessingFlags.LoadElements) != 0;527 var writingErrors = (manifestProcessingFlags & ManifestProcessingFlags.WriteErrors) != 0;528 529 // Load the data file(s) to get the module info...530 try531 {532 scriptBlock.CheckRestrictedLanguage(PermittedCmdlets, s_extraAllowedVariables, true);533 }534 catch (RuntimeException pe)535 {536 if (writingErrors)537 {538 message = StringUtil.Format(Modules.InvalidModuleManifest, moduleManifestPath, pe.Message);539 MissingMemberException mm = new MissingMemberException(message);540 ErrorRecord er = new ErrorRecord(mm, "Modules_InvalidManifest",541 ErrorCategory.ResourceUnavailable, moduleManifestPath);542 WriteError(er);543 }544 545 containedErrors = true;546 return null;547 }548 549 object result;550 object oldPSScriptRoot = Context.GetVariableValue(SpecialVariables.PSScriptRootVarPath);551 object oldPSCommandPath = Context.GetVariableValue(SpecialVariables.PSCommandPathVarPath);552 ArrayList errors = (ArrayList)Context.GetVariableValue(SpecialVariables.ErrorVarPath);553 int oldErrorCount = errors.Count;554 555 try556 {557 // Set the PSScriptRoot variable in the modules session state558 Context.SetVariable(SpecialVariables.PSScriptRootVarPath, Path.GetDirectoryName(moduleManifestPath));559 Context.SetVariable(SpecialVariables.PSCommandPathVarPath, moduleManifestPath);560 561 result = PSObject.Base(scriptBlock.InvokeReturnAsIs());562 }563 finally564 {565 Context.SetVariable(SpecialVariables.PSScriptRootVarPath, oldPSScriptRoot);566 Context.SetVariable(SpecialVariables.PSCommandPathVarPath, oldPSCommandPath);567 568 // We do not want any exceptions to show up in user's session state. So, we are removing errors in the finally block.569 // If we're not loading the manifest (and are examining it), prevent errors from showing up in570 // the user's session.571 if (!importingModule)572 {573 while (errors.Count > oldErrorCount) { errors.RemoveAt(0); }574 }575 }576 577 Hashtable data = result as Hashtable;578 if (data == null)579 {580 if (writingErrors)581 {582 message = StringUtil.Format(Modules.EmptyModuleManifest, moduleManifestPath);583 ArgumentException ae = new ArgumentException(message);584 ErrorRecord er = new ErrorRecord(ae, "Modules_InvalidManifest",585 ErrorCategory.ResourceUnavailable, moduleManifestPath);586 WriteError(er);587 }588 589 containedErrors = true;590 return null;591 }592 593 // MSFT:873446 Create a case insensitive comparer based hashtable to help594 // with case-insensitive comparison of keys.595 data = new Hashtable(data, StringComparer.OrdinalIgnoreCase);596 if (validMembers != null && !ValidateManifestHash(data, validMembers, moduleManifestPath, manifestProcessingFlags))597 {598 containedErrors = true;599 if ((manifestProcessingFlags & ManifestProcessingFlags.NullOnFirstError) != 0)600 return null;601 }602 603 return data;604 }605 606 /// <summary>607 /// Verify the hash contains only valid members. Write an error and return false if it is not valid.608 /// </summary>609 private bool ValidateManifestHash(610 Hashtable data,611 string[] validMembers,612 string moduleManifestPath,613 ManifestProcessingFlags manifestProcessingFlags)614 {615 bool result = true;616 617 StringBuilder badKeys = new StringBuilder();618 foreach (string s in data.Keys)619 {620 bool found = false;621 622 foreach (string member in validMembers)623 {624 if (s.Equals(member, StringComparison.OrdinalIgnoreCase))625 {626 found = true;627 }628 }629 630 if (!found)631 {632 if (badKeys.Length > 0)633 badKeys.Append(", ");634 badKeys.Append('\'');635 badKeys.Append(s);636 badKeys.Append('\'');637 }638 }639 640 if (badKeys.Length > 0)641 {642 result = false;643 string message = null;644 645 if ((manifestProcessingFlags & ManifestProcessingFlags.WriteErrors) != 0)646 {647 // Check for PowerShell Version before checking other keys648 // If a PowerShellVersion exists and does not match the requirements, then the error is InsufficientPowerShellVersion649 // Else, the error is InvalidManifestMember650 Version powerShellVersion;651 Version currentPowerShellVersion = PSVersionInfo.PSVersion;652 if (GetScalarFromData<Version>(data, moduleManifestPath, "PowerShellVersion", manifestProcessingFlags, out powerShellVersion) &&653 currentPowerShellVersion < powerShellVersion)654 {655 message = StringUtil.Format(Modules.ModuleManifestInsufficientPowerShellVersion,656 currentPowerShellVersion,657 moduleManifestPath, powerShellVersion);658 InvalidOperationException ioe = new InvalidOperationException(message);659 ErrorRecord er = new ErrorRecord(ioe, "Modules_InsufficientPowerShellVersion",660 ErrorCategory.ResourceUnavailable, moduleManifestPath);661 WriteError(er);662 }663 else664 {665 StringBuilder validMembersString = new StringBuilder("'");666 validMembersString.Append(validMembers[0]);667 for (int i = 1; i < validMembers.Length; i++)668 {669 validMembersString.Append("', '");670 validMembersString.Append(validMembers[i]);671 }672 673 validMembersString.Append('\'');674 message = StringUtil.Format(Modules.InvalidModuleManifestMember, moduleManifestPath, validMembersString, badKeys);675 InvalidOperationException ioe = new InvalidOperationException(message);676 ErrorRecord er = new ErrorRecord(ioe, "Modules_InvalidManifestMember",677 ErrorCategory.InvalidData, moduleManifestPath);678 WriteError(er);679 }680 }681 }682 683 return result;684 }685 686 private PSModuleInfo LoadModuleNamedInManifest(687 PSModuleInfo parentModule,688 ModuleSpecification moduleSpecification,689 string moduleBase,690 bool searchModulePath,691 string prefix,692 SessionState ss,693 ImportModuleOptions options,694 ManifestProcessingFlags manifestProcessingFlags,695 object privateData,696 out bool found,697 string shortModuleName,698 PSLanguageMode? manifestLanguageMode)699 {700 PSModuleInfo module = null;701 PSModuleInfo tempModuleInfoFromVerification = null;702 found = false;703 bool moduleFileFound = false;704 bool wasRooted = false;705 Version savedBaseMinimumVersion = BaseMinimumVersion;706 Version savedBaseMaximumVersion = BaseMaximumVersion;707 Version savedBaseRequiredVersion = BaseRequiredVersion;708 Guid? savedBaseGuid = BaseGuid;709 710 var importingModule = manifestProcessingFlags.HasFlag(ManifestProcessingFlags.LoadElements);711 string extension = Path.GetExtension(moduleSpecification.Name);712 713 // First check for fully-qualified paths - either absolute or relative714 string rootedPath = ResolveRootedFilePath(moduleSpecification.Name, this.Context);715 if (string.IsNullOrEmpty(rootedPath))716 {717 // Use the name of the parent module if it's specified, otherwise, use the current module name.718 // - If the current module is a nested module, then the parent module will be specified.719 // - If the current module is a root module, then the parent module will not be specified.720 string moduleName = parentModule?.Name ?? ModuleIntrinsics.GetModuleName(moduleSpecification.Name);721 rootedPath = FixFileName(moduleName, moduleBase, moduleSpecification.Name, extension: null, canLoadAssembly: importingModule);722 }723 else724 {725 wasRooted = true;726 }727 728 try729 {730 this.Context.Modules.IncrementModuleNestingDepth(this, rootedPath);731 BaseMinimumVersion = null;732 BaseMaximumVersion = null;733 BaseRequiredVersion = null;734 BaseGuid = null;735 736 // See if it's one of the powershell module extensions...737 if (!ModuleIntrinsics.IsPowerShellModuleExtension(extension))738 {739 // If the file exits, and it does not have a powershell module extension, we know for sure that the file is not a valid module740 if (File.Exists(rootedPath))741 {742 PSInvalidOperationException invalidOperation = PSTraceSource.NewInvalidOperationException(743 Modules.ManifestMemberNotValid,744 moduleSpecification.Name,745 "NestedModules",746 parentModule?.Path,747 StringUtil.Format(Modules.InvalidModuleExtension, extension, moduleSpecification.Name),748 ModuleIntrinsics.GetModuleName(parentModule?.Path));749 invalidOperation.SetErrorId("Modules_InvalidModuleExtension");750 throw invalidOperation;751 }752 753 extension = null;754 }755 756 // Now load the module from the module directory first...757 if (extension == null)758 {759 // No extension so we'll have to search using the extensions760 //761 if (VerifyIfNestedModuleIsAvailable(moduleSpecification, rootedPath, /*extension*/null, out tempModuleInfoFromVerification))762 {763 module = LoadUsingExtensions(764 parentModule,765 moduleSpecification.Name,766 fileBaseName: rootedPath,767 extension: null,768 moduleBase, // not using base from tempModuleInfoFromVerification as we are looking under moduleBase directory769 prefix,770 ss,771 options,772 manifestProcessingFlags,773 out found,774 out moduleFileFound);775 }776 777 // Win8: 262157 - Import-Module is giving errors while loading Nested Modules. (This is a V2 bug)778 // NestedModules = 'test2' ---> test2 is a directory under current module directory (e.g - Test1)779 // We also need to look for Test1\Test2\Test2.(psd1/psm1/dll)780 // With the call above, we are only looking at Test1\Test2.(psd1/psm1/dll)781 if (!found && !moduleFileFound)782 {783 string newRootedPath = Path.Combine(rootedPath, moduleSpecification.Name);784 string newModuleBase = Path.Combine(moduleBase, moduleSpecification.Name);785 if (VerifyIfNestedModuleIsAvailable(moduleSpecification, newRootedPath, /*extension*/null, out tempModuleInfoFromVerification))786 {787 module = LoadUsingExtensions(788 parentModule,789 moduleSpecification.Name,790 fileBaseName: newRootedPath,791 extension: null,792 newModuleBase, // not using base from tempModuleInfoFromVerification as we are looking under moduleBase directory793 prefix,794 ss,795 options,796 manifestProcessingFlags,797 out found,798 out moduleFileFound);799 }800 }801 }802 else803 {804 // Ok - we have a complete file name so load that...805 if (VerifyIfNestedModuleIsAvailable(moduleSpecification, rootedPath, extension, out tempModuleInfoFromVerification))806 {807 module = LoadModule(808 parentModule,809 rootedPath, // fileName810 moduleBase, // not using base from tempModuleInfoFromVerification as we have a complete file name811 prefix,812 ss,813 privateData,814 ref options,815 manifestProcessingFlags,816 out found,817 out moduleFileFound);818 }819 820 // Win8: 262157 - Import-Module is giving errors while loading Nested Modules. (This is a V2 bug)821 // Only look for the file if the file was not found with the previous search822 if (!found && !moduleFileFound)823 {824 string newRootedPath = Path.Combine(rootedPath, moduleSpecification.Name);825 string newModuleBase = Path.Combine(moduleBase, moduleSpecification.Name);826 if (VerifyIfNestedModuleIsAvailable(moduleSpecification, newRootedPath, extension, out tempModuleInfoFromVerification))827 {828 module = LoadModule(829 parentModule,830 newRootedPath, // fileName831 newModuleBase, // not using base from tempModuleInfoFromVerification as we are looking under moduleBase directory832 prefix,833 ss,834 privateData,835 ref options,836 manifestProcessingFlags,837 out found,838 out moduleFileFound);839 }840 }841 }842 843 // The rooted files wasn't found, so don't search anymore...844 if (!found && wasRooted)845 return null;846 847 if (searchModulePath && !found && !moduleFileFound)848 {849 if (VerifyIfNestedModuleIsAvailable(moduleSpecification, null, null, out tempModuleInfoFromVerification))850 {851 IEnumerable<string> modulePath = null;852 if (tempModuleInfoFromVerification != null)853 {854 var subdirName = Path.GetFileName(tempModuleInfoFromVerification.ModuleBase);855 Version version;856 if (Version.TryParse(subdirName, out version))857 {858 var moduleBaseWithoutVersion = Path.GetDirectoryName(tempModuleInfoFromVerification.ModuleBase);859 860 modulePath = new string[]861 {862 Path.GetDirectoryName(moduleBaseWithoutVersion),863 moduleBaseWithoutVersion864 };865 }866 else867 {868 modulePath = new string[]869 {870 Path.GetDirectoryName(tempModuleInfoFromVerification.ModuleBase),871 tempModuleInfoFromVerification.ModuleBase872 };873 }874 }875 else876 {877 modulePath = ModuleIntrinsics.GetModulePath(false, this.Context);878 }879 880 // Otherwise try the module path881 found = LoadUsingModulePath(parentModule, modulePath,882 moduleSpecification.Name, ss,883 options, manifestProcessingFlags, out module);884 }885 }886 887 if (manifestLanguageMode.HasValue && found && (module != null) && module.LanguageMode.HasValue)888 {889 // Check for script module language mode consistency. All loaded script modules must have the same language mode as the manifest.890 // If not then this indicates a malformed module and a possible exploit to make trusted private functions visible in a891 // Constrained Language session.892 if (module.LanguageMode != manifestLanguageMode)893 {894 if (SystemPolicy.GetSystemLockdownPolicy() != SystemEnforcementMode.Audit)895 {896 var languageModeError = PSTraceSource.NewInvalidOperationException(897 Modules.MismatchedLanguageModes,898 module.Name, manifestLanguageMode, module.LanguageMode);899 languageModeError.SetErrorId("Modules_MismatchedLanguageModes");900 throw languageModeError;901 }902 903 SystemPolicy.LogWDACAuditMessage(904 context: Context,905 title: Modules.WDACMismatchedLanguageModesTitle,906 message: Modules.WDACMismatchedLanguageModesMessage,907 fqid: "ModulesMismatchedLanguageModes",908 dropIntoDebugger: true);909 }910 }911 912 // At this point, we haven't found an actual module, so try loading it as a913 // PSSnapIn and then finally as an assembly in the GAC...914 if (!found && (moduleSpecification.Guid == null) && (moduleSpecification.Version == null) && (moduleSpecification.RequiredVersion == null) && (moduleSpecification.MaximumVersion == null))915 {916 // If we are in module analysis and the parent module declares non-wildcarded ExportedCmdlets, then we don't need to917 // actually process the binary module.918 bool shouldLoadModule = true;919 920 if ((parentModule != null) && !importingModule)921 {922 if ((parentModule.ExportedCmdlets != null) && (parentModule.ExportedCmdlets.Count > 0))923 {924 shouldLoadModule = false;925 926 foreach (string exportedCmdlet in parentModule.ExportedCmdlets.Keys)927 {928 if (WildcardPattern.ContainsWildcardCharacters(exportedCmdlet))929 {930 shouldLoadModule = true;931 break;932 }933 }934 935 found = true;936 }937 }938 939 if (shouldLoadModule)940 {941 try942 {943 // At this point, we are already exhaust all possible ways to load the nested module. The last option is to load it as a binary module/snapin.944 module = LoadBinaryModule(945 parentModule,946 moduleSpecification.Name,947 fileName: null,948 assemblyToLoad: null,949 moduleBase,950 ss,951 options,952 manifestProcessingFlags,953 prefix,954 out found,955 shortModuleName,956 disableFormatUpdates: false);957 }958 catch (FileNotFoundException)959 {960 // Loading the nested module as a pssnapin or assembly is our last attempt to resolve the nested module.961 // If we catch 'FileNotFoundException', it simply means that our last attempt failed because we couldn't find such a pssnapin or assembly.962 // In this case, we can safely ignore this exception and throw a 'Modules_ModuleFileNotFound' error.963 }964 965 if ((module != null) && importingModule)966 {967 AddModuleToModuleTables(this.Context, this.TargetSessionState.Internal, module);968 }969 }970 }971 972 return module;973 }974 finally975 {976 BaseMinimumVersion = savedBaseMinimumVersion;977 BaseMaximumVersion = savedBaseMaximumVersion;978 BaseRequiredVersion = savedBaseRequiredVersion;979 BaseGuid = savedBaseGuid;980 this.Context.Modules.DecrementModuleNestingCount();981 }982 }983 984 internal List<PSModuleInfo> GetModule(string[] names, bool all, bool refresh)985 {986 List<PSModuleInfo> modulesToReturn = new List<PSModuleInfo>();987 988 // Two lists - one to hold Module Paths and one to hold Module Names989 // For Module Paths, we don't do any path resolution990 List<string> modulePaths = new List<string>();991 List<string> moduleNames = new List<string>();992 993 if (names != null)994 {995 foreach (var n in names)996 {997 if (n.Contains(StringLiterals.DefaultPathSeparator) || n.Contains(StringLiterals.AlternatePathSeparator))998 {999 modulePaths.Add(n);1000 }1001 else1002 {1003 moduleNames.Add(n);1004 }1005 }1006 1007 modulesToReturn.AddRange(GetModuleForRootedPaths(modulePaths, all, refresh));1008 }1009 1010 // If no names were passed to this function, then this API will return list of all available modules1011 if (names == null || moduleNames.Count > 0)1012 {1013 modulesToReturn.AddRange(GetModuleForNames(moduleNames, all, refresh));1014 }1015 1016 return modulesToReturn;1017 }1018 1019 private IEnumerable<PSModuleInfo> GetModuleForRootedPaths(List<string> modulePaths, bool all, bool refresh)1020 {1021 // This is to filter out duplicate modules1022 var modules = new HashSet<string>(StringComparer.OrdinalIgnoreCase);1023 1024 foreach (string mp in modulePaths)1025 {1026 bool containsWildCards = false;1027 1028 string modulePath = mp.TrimEnd('\\');1029 1030 // If the given path contains wildcards, we won't throw error if no match module path is found.1031 if (WildcardPattern.ContainsWildcardCharacters(modulePath))1032 {1033 containsWildCards = true;1034 }1035 1036 // Now we resolve the possible paths in case it is relative path/path contains wildcards1037 var modulePathCollection = GetResolvedPathCollection(modulePath, this.Context);1038 1039 if (modulePathCollection != null)1040 {1041 foreach (string resolvedModulePath in modulePathCollection)1042 {1043 string moduleName = Path.GetFileName(resolvedModulePath);1044 1045 // If the given path is a valid module file, we will load the specific file1046 if (!Directory.Exists(resolvedModulePath) && ModuleIntrinsics.IsPowerShellModuleExtension(Path.GetExtension(moduleName)))1047 {1048 PSModuleInfo module = CreateModuleInfoForGetModule(resolvedModulePath, refresh);1049 if (module != null)1050 {1051 if (modules.Add(resolvedModulePath))1052 {1053 yield return module;1054 }1055 }1056 }1057 else1058 {1059 // Given path is a directory, we first check if it is end with module version.1060 Version version;1061 if (Version.TryParse(moduleName, out version))1062 {1063 moduleName = Path.GetFileName(Directory.GetParent(resolvedModulePath).Name);1064 }1065 1066 var availableModuleFiles = all1067 ? ModuleUtils.GetAllAvailableModuleFiles(resolvedModulePath)1068 : ModuleUtils.GetModuleFilesFromAbsolutePath(resolvedModulePath);1069 1070 bool foundModule = false;1071 foreach (string file in availableModuleFiles)1072 {1073 PSModuleInfo module = CreateModuleInfoForGetModule(file, refresh);1074 if (module != null)1075 {1076 if (string.Equals(moduleName, module.Name, StringComparison.OrdinalIgnoreCase))1077 {1078 foundModule = true;1079 // We need to list all versions of the module.1080 string subModulePath = Path.GetDirectoryName(file);1081 if (modules.Add(subModulePath))1082 {1083 yield return module;1084 }1085 }1086 }1087 }1088 1089 // Write error only if Name has no wild cards1090 if (!foundModule && !containsWildCards)1091 {1092 WriteError(CreateModuleNotFoundError(resolvedModulePath));1093 }1094 }1095 }1096 }1097 else1098 {1099 if (!containsWildCards)1100 {1101 WriteError(CreateModuleNotFoundError(modulePath));1102 }1103 }1104 }1105 }1106 1107 private static ErrorRecord CreateModuleNotFoundError(string modulePath)1108 {1109 string errorMessage = StringUtil.Format(Modules.ModuleNotFoundForGetModule, modulePath);1110 FileNotFoundException fnf = new FileNotFoundException(errorMessage);1111 ErrorRecord er = new ErrorRecord(fnf, "Modules_ModuleNotFoundForGetModule", ErrorCategory.ResourceUnavailable, modulePath);1112 return er;1113 }1114 1115 private IEnumerable<PSModuleInfo> GetModuleForNames(List<string> names, bool all, bool refresh)1116 {1117 IEnumerable<PSModuleInfo> allModules = null;1118 HashSet<string> modulePathSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);1119 1120 foreach (string path in ModuleIntrinsics.GetModulePath(false, Context))1121 {1122 string uniquePath = path.TrimEnd(Utils.Separators.Directory);1123 1124 // Ignore repeated module path.1125 if (!modulePathSet.Add(uniquePath))1126 {1127 continue;1128 }1129 1130 try1131 {1132 IEnumerable<PSModuleInfo> modulesFound = GetModulesFromOneModulePath(1133 names, uniquePath, all, refresh).OrderBy(static m => m.Name);1134 allModules = allModules == null ? modulesFound : allModules.Concat(modulesFound);1135 }1136 catch (Exception e) when (e is IOException || e is UnauthorizedAccessException)1137 {1138 // ignore directories that can't be accessed1139 continue;1140 }1141 }1142 1143 // Make sure we always return a non-null collection.1144 return allModules ?? Array.Empty<PSModuleInfo>();1145 }1146 1147 /// <summary>1148 /// Get modules based on the given names and module files.1149 /// </summary>1150 private IEnumerable<PSModuleInfo> GetModulesFromOneModulePath(List<string> names, string modulePath, bool all, bool refresh)1151 {1152 const WildcardOptions options = WildcardOptions.IgnoreCase | WildcardOptions.CultureInvariant;1153 IEnumerable<WildcardPattern> namePatterns = null;1154 if (names != null && names.Count > 0)1155 {1156 namePatterns = SessionStateUtilities.CreateWildcardsFromStrings(names, options);1157 }1158 1159 IEnumerable<string> moduleFiles = all1160 ? ModuleUtils.GetAllAvailableModuleFiles(modulePath)1161 : ModuleUtils.GetDefaultAvailableModuleFiles(modulePath);1162 1163 foreach (string file in moduleFiles)1164 {1165 if (namePatterns == null ||1166 SessionStateUtilities.MatchesAnyWildcardPattern(1167 Path.GetFileNameWithoutExtension(file), namePatterns, defaultValue: true))1168 {1169 PSModuleInfo module = CreateModuleInfoForGetModule(file, refresh);1170 if (module == null) { continue; }1171 1172 if (all || !ModuleUtils.IsModuleInVersionSubdirectory(file, out Version directoryVersion) || directoryVersion == module.Version)1173 {1174 yield return module;1175 }1176 }1177 }1178 1179 ClearAnalysisCaches();1180 }1181 1182 /// <summary>1183 /// Get the version type of input string MaximumVersion, translate "*" if there's any.1184 /// </summary>1185 internal static Version GetMaximumVersion(string stringVersion)1186 {1187 Version maxVersion;1188 // First, try to convert maxVersion to version directly1189 if (System.Version.TryParse(stringVersion, out maxVersion))1190 {1191 return maxVersion;1192 }1193 else1194 {1195 // If first conversion fails, try to convert * to maximum version1196 const string maxRange = "999999999";1197 if (stringVersion[stringVersion.Length - 1] == '*')1198 {1199 stringVersion = stringVersion.Substring(0, stringVersion.Length - 1);1200 stringVersion += maxRange;