Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes372downloads
TestModuleManifestCommand.cs438 linesDownload Raw Back to Modules
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.IO;9using System.Linq;10using System.Management.Automation;11using System.Management.Automation.Internal;12 13//14// Now define the set of commands for manipulating modules.15//16 17namespace Microsoft.PowerShell.Commands18{19    #region Test-ModuleManifest20    /// <summary>21    /// This cmdlet takes a module manifest and validates the contents...22    /// </summary>23    [Cmdlet(VerbsDiagnostic.Test, "ModuleManifest", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096900")]24    [OutputType(typeof(PSModuleInfo))]25    public sealed class TestModuleManifestCommand : ModuleCmdletBase26    {27        /// <summary>28        /// Creates an instance of the Test-ModuleManifest command.29        /// </summary>30        public TestModuleManifestCommand()31        {32            // Test-ModuleManifest reads a manifest with ModuleCmdletBase.LoadModuleManifest().33            // This will error on an edition-incompatible manifest loaded from the System32 path,34            // unless BaseSkipEditionCheck is true. Since Test-ModuleManifest shouldn't care about35            // module edition (it just tests manifest validity), we always want to set this rather36            // than provide it as a switch on the cmdlet.37            BaseSkipEditionCheck = true;38        }39 40        /// <summary>41        /// The output path for the generated file...42        /// </summary>43        [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0, ValueFromPipelineByPropertyName = true)]44        public string Path45        {46            get { return _path; }47 48            set { _path = value; }49        }50 51        private string _path;52 53        /// <summary>54        /// Implements the record processing for this cmdlet.55        /// </summary>56        protected override void ProcessRecord()57        {58            ProviderInfo provider = null;59            Collection<string> filePaths;60 61            try62            {63                if (Context.EngineSessionState.IsProviderLoaded(Context.ProviderNames.FileSystem))64                {65                    filePaths =66                        SessionState.Path.GetResolvedProviderPathFromPSPath(_path, out provider);67                }68                else69                {70                    filePaths = new Collection<string>();71                    filePaths.Add(_path);72                }73            }74            catch (ItemNotFoundException)75            {76                string message = StringUtil.Format(Modules.ModuleNotFound, _path);77                FileNotFoundException fnf = new FileNotFoundException(message);78                ErrorRecord er = new ErrorRecord(fnf, "Modules_ModuleNotFound",79                    ErrorCategory.ResourceUnavailable, _path);80                WriteError(er);81                return;82            }83 84            // Make sure that the path is in the file system - that's all we can handle currently...85            if (!provider.NameEquals(this.Context.ProviderNames.FileSystem))86            {87                // "The current provider ({0}) cannot open a file"88                throw InterpreterError.NewInterpreterException(_path, typeof(RuntimeException),89                    null, "FileOpenError", ParserStrings.FileOpenError, provider.FullName);90            }91 92            // Make sure at least one file was found...93            if (filePaths == null || filePaths.Count < 1)94            {95                string message = StringUtil.Format(Modules.ModuleNotFound, _path);96                FileNotFoundException fnf = new FileNotFoundException(message);97                ErrorRecord er = new ErrorRecord(fnf, "Modules_ModuleNotFound",98                    ErrorCategory.ResourceUnavailable, _path);99                WriteError(er);100                return;101            }102 103            if (filePaths.Count > 1)104            {105                // "The path resolved to more than one file; can only process one file at a time."106                throw InterpreterError.NewInterpreterException(filePaths, typeof(RuntimeException),107                    null, "AmbiguousPath", ParserStrings.AmbiguousPath);108            }109 110            string filePath = filePaths[0];111            ExternalScriptInfo scriptInfo = null;112            string ext = System.IO.Path.GetExtension(filePath);113            if (ext.Equals(StringLiterals.PowerShellDataFileExtension, StringComparison.OrdinalIgnoreCase))114            {115                // Create a script info for loading the file...116                string scriptName;117                scriptInfo = GetScriptInfoForFile(filePath, out scriptName, false);118 119                // we should reserve the Context.ModuleBeingProcessed unchanged after loadModuleManifest(), otherwise the module won't be importable next time.120                PSModuleInfo module;121                string _origModuleBeingProcessed = Context.ModuleBeingProcessed;122                try123                {124                    module = LoadModuleManifest(125                    scriptInfo,126                    ManifestProcessingFlags.WriteErrors | ManifestProcessingFlags.WriteWarnings /* but don't stop on first error and don't load elements */,127                    null,128                    null,129                    null,130                    null);131 132                    if (module != null)133                    {134                        // Validate file existence135                        if (module.RequiredAssemblies != null)136                        {137                            foreach (string requiredAssembliespath in module.RequiredAssemblies)138                            {139                                if (!IsValidFilePath(requiredAssembliespath, module, true) && !IsValidGacAssembly(requiredAssembliespath))140                                {141                                    string errorMsg = StringUtil.Format(Modules.InvalidRequiredAssembliesInModuleManifest, requiredAssembliespath, filePath);142                                    var errorRecord = new ErrorRecord(new DirectoryNotFoundException(errorMsg), "Modules_InvalidRequiredAssembliesInModuleManifest",143                                            ErrorCategory.ObjectNotFound, _path);144                                    WriteError(errorRecord);145                                }146                            }147                        }148 149                        if (!HasValidRootModule(module))150                        {151                            string errorMsg = StringUtil.Format(Modules.InvalidModuleManifest, module.RootModule, filePath);152                            var errorRecord = new ErrorRecord(new ArgumentException(errorMsg), "Modules_InvalidRootModuleInModuleManifest",153                                    ErrorCategory.InvalidArgument, _path);154                            WriteError(errorRecord);155                        }156 157                        Hashtable data = null;158                        Hashtable localizedData = null;159                        bool containerErrors = false;160                        LoadModuleManifestData(scriptInfo, ManifestProcessingFlags.WriteErrors | ManifestProcessingFlags.WriteWarnings, out data, out localizedData, ref containerErrors);161                        ModuleSpecification[] nestedModules;162                        GetScalarFromData(data, scriptInfo.Path, "NestedModules", ManifestProcessingFlags.WriteErrors | ManifestProcessingFlags.WriteWarnings, out nestedModules);163                        if (nestedModules != null)164                        {165                            foreach (ModuleSpecification nestedModule in nestedModules)166                            {167                                if (!IsValidFilePath(nestedModule.Name, module, true)168                                    && !IsValidFilePath(nestedModule.Name + StringLiterals.PowerShellILAssemblyExtension, module, true)169                                    && !IsValidFilePath(nestedModule.Name + StringLiterals.PowerShellNgenAssemblyExtension, module, true)170                                    && !IsValidFilePath(nestedModule.Name + StringLiterals.PowerShellILExecutableExtension, module, true)171                                    && !IsValidFilePath(nestedModule.Name + StringLiterals.PowerShellModuleFileExtension, module, true)172                                    && !IsValidGacAssembly(nestedModule.Name))173                                {174                                    Collection<PSModuleInfo> modules = GetModuleIfAvailable(nestedModule);175                                    if (modules.Count == 0)176                                    {177                                        string errorMsg = StringUtil.Format(Modules.InvalidNestedModuleinModuleManifest, nestedModule.Name, filePath);178                                        var errorRecord = new ErrorRecord(new DirectoryNotFoundException(errorMsg), "Modules_InvalidNestedModuleinModuleManifest",179                                                ErrorCategory.ObjectNotFound, _path);180                                        WriteError(errorRecord);181                                    }182                                }183                            }184                        }185 186                        ModuleSpecification[] requiredModules;187                        GetScalarFromData(data, scriptInfo.Path, "RequiredModules", ManifestProcessingFlags.WriteErrors | ManifestProcessingFlags.WriteWarnings, out requiredModules);188                        if (requiredModules != null)189                        {190                            foreach (ModuleSpecification requiredModule in requiredModules)191                            {192                                var modules = GetModule(new[] { requiredModule.Name }, all: false, refresh: true);193                                if (modules.Count == 0)194                                {195                                    string errorMsg = StringUtil.Format(Modules.InvalidRequiredModulesinModuleManifest, requiredModule.Name, filePath);196                                    var errorRecord = new ErrorRecord(new DirectoryNotFoundException(errorMsg), "Modules_InvalidRequiredModulesinModuleManifest",197                                            ErrorCategory.ObjectNotFound, _path);198                                    WriteError(errorRecord);199                                }200                            }201                        }202 203                        string[] fileListPaths;204                        GetScalarFromData(data, scriptInfo.Path, "FileList", ManifestProcessingFlags.WriteErrors | ManifestProcessingFlags.WriteWarnings, out fileListPaths);205                        if (fileListPaths != null)206                        {207                            foreach (string fileListPath in fileListPaths)208                            {209                                if (!IsValidFilePath(fileListPath, module, true))210                                {211                                    string errorMsg = StringUtil.Format(Modules.InvalidFilePathinModuleManifest, fileListPath, filePath);212                                    var errorRecord = new ErrorRecord(new DirectoryNotFoundException(errorMsg), "Modules_InvalidFilePathinModuleManifest",213                                            ErrorCategory.ObjectNotFound, _path);214                                    WriteError(errorRecord);215                                }216                            }217                        }218 219                        ModuleSpecification[] moduleListModules;220                        GetScalarFromData(data, scriptInfo.Path, "ModuleList", ManifestProcessingFlags.WriteErrors | ManifestProcessingFlags.WriteWarnings, out moduleListModules);221                        if (moduleListModules != null)222                        {223                            foreach (ModuleSpecification moduleListModule in moduleListModules)224                            {225                                var modules = GetModule(new[] { moduleListModule.Name }, all: false, refresh: true);226                                if (modules.Count == 0)227                                {228                                    string errorMsg = StringUtil.Format(Modules.InvalidModuleListinModuleManifest, moduleListModule.Name, filePath);229                                    var errorRecord = new ErrorRecord(new DirectoryNotFoundException(errorMsg), "Modules_InvalidModuleListinModuleManifest",230                                            ErrorCategory.ObjectNotFound, _path);231                                    WriteError(errorRecord);232                                }233                            }234                        }235 236                        if (module.CompatiblePSEditions.Any())237                        {238                            // The CompatiblePSEditions module manifest key is supported only on PowerShell version '5.1' or higher.239                            // Ensure that PowerShellVersion module manifest key value is '5.1' or higher.240                            //241                            var minimumRequiredPowerShellVersion = new Version(5, 1);242                            if ((module.PowerShellVersion == null) || module.PowerShellVersion < minimumRequiredPowerShellVersion)243                            {244                                string errorMsg = StringUtil.Format(Modules.InvalidPowerShellVersionInModuleManifest, filePath);245                                var errorRecord = new ErrorRecord(new ArgumentException(errorMsg), "Modules_InvalidPowerShellVersionInModuleManifest", ErrorCategory.InvalidArgument, _path);246                                WriteError(errorRecord);247                            }248                        }249                    }250                }251                finally252                {253                    Context.ModuleBeingProcessed = _origModuleBeingProcessed;254                }255 256                DirectoryInfo parent = null;257                try258                {259                    parent = Directory.GetParent(filePath);260                }261                catch (IOException) { }262                catch (UnauthorizedAccessException) { }263                catch (ArgumentException) { }264 265                Version version;266                if (parent != null && Version.TryParse(parent.Name, out version))267                {268                    if (!version.Equals(module.Version))269                    {270                        string message = StringUtil.Format(Modules.InvalidModuleManifestVersion, filePath, module.Version.ToString(), parent.FullName);271                        var ioe = new InvalidOperationException(message);272                        ErrorRecord er = new ErrorRecord(ioe, "Modules_InvalidModuleManifestVersion",273                            ErrorCategory.InvalidArgument, _path);274                        ThrowTerminatingError(er);275                    }276 277                    WriteVerbose(Modules.ModuleVersionEqualsToVersionFolder);278                }279 280                if (module != null)281                {282                    WriteObject(module);283                }284            }285            else286            {287                string message = StringUtil.Format(Modules.InvalidModuleManifestPath, filePath);288                InvalidOperationException ioe = new InvalidOperationException(message);289                ErrorRecord er = new ErrorRecord(ioe, "Modules_InvalidModuleManifestPath",290                    ErrorCategory.InvalidArgument, _path);291                ThrowTerminatingError(er);292            }293        }294 295        // All module extensions except ".psd1" are valid RootModule extensions296        private static readonly IReadOnlyList<string> s_validRootModuleExtensions = ModuleIntrinsics.PSModuleExtensions297            .Where(static ext => !string.Equals(ext, StringLiterals.PowerShellDataFileExtension, StringComparison.OrdinalIgnoreCase))298            .ToArray();299 300        /// <summary>301        /// Checks whether the RootModule field of a module is valid or not.302        /// Valid root modules are:303        ///  - null304        ///  - Empty string305        ///  - A valid non-psd1 module file (psm1, cdxml, xaml, dll), as name with extension, name without extension, or path.306        /// </summary>307        /// <param name="module">The module for which we want to check the validity of the root module.</param>308        /// <returns>True if the root module is valid, false otherwise.</returns>309        private bool HasValidRootModule(PSModuleInfo module)310        {311            // Empty/null root modules are allowed312            if (string.IsNullOrEmpty(module.RootModule))313            {314                return true;315            }316 317            // GAC assemblies are allowed318            if (IsValidGacAssembly(module.RootModule))319            {320                return true;321            }322 323            // Check for extensions324            string rootModuleExt = System.IO.Path.GetExtension(module.RootModule);325            if (!string.IsNullOrEmpty(rootModuleExt))326            {327                // Check that the root module's extension is an allowed one328                if (!s_validRootModuleExtensions.Contains(rootModuleExt, StringComparer.OrdinalIgnoreCase))329                {330                    return false;331                }332 333                // Check the file path of the full root module334                return IsValidFilePath(module.RootModule, module, verifyPathScope: true);335            }336 337            // We have no extension, so we need to check all of them338            foreach (string extension in s_validRootModuleExtensions)339            {340                if (IsValidFilePath(module.RootModule + extension, module, verifyPathScope: true))341                {342                    return true;343                }344            }345 346            return false;347        }348 349        /// <summary>350        /// Check if the given path is valid.351        /// </summary>352        /// <param name="path"></param>353        /// <param name="module"></param>354        /// <param name="verifyPathScope"></param>355        /// <returns></returns>356        private bool IsValidFilePath(string path, PSModuleInfo module, bool verifyPathScope)357        {358            try359            {360                if (!System.IO.Path.IsPathRooted(path))361                {362                    // we assume the relative path is under module scope, otherwise we will throw error anyway.363                    path = System.IO.Path.GetFullPath(module.ModuleBase + System.IO.Path.DirectorySeparatorChar + path);364                }365 366                // resolve the path so slashes are in the right direction367                CmdletProviderContext cmdContext = new CmdletProviderContext(this);368                Collection<PathInfo> pathInfos = SessionState.Path.GetResolvedPSPathFromPSPath(path, cmdContext);369                if (pathInfos.Count != 1)370                {371                    string message = StringUtil.Format(Modules.InvalidModuleManifestPath, path);372                    InvalidOperationException ioe = new InvalidOperationException(message);373                    ErrorRecord er = new ErrorRecord(ioe, "Modules_InvalidModuleManifestPath", ErrorCategory.InvalidArgument, path);374                    ThrowTerminatingError(er);375                }376 377                // `Path` returns the PSProviderPath which is fully qualified to the provider and the filesystem APIs378                // don't understand this.  Instead `ProviderPath` returns the path that the FileSystemProvider understands.379                path = pathInfos[0].ProviderPath;380 381                // First, we validate if the path does exist.382                if (!File.Exists(path) && !Directory.Exists(path))383                {384                    return false;385                }386 387                // Then, we validate if the path is under module scope388                if (verifyPathScope && !System.IO.Path.GetFullPath(path).StartsWith(System.IO.Path.GetFullPath(module.ModuleBase), StringComparison.OrdinalIgnoreCase))389                {390                    return false;391                }392            }393            catch (Exception exception)394            {395                if (exception is ArgumentException || exception is ArgumentNullException || exception is NotSupportedException || exception is PathTooLongException || exception is ItemNotFoundException)396                {397                    return false;398                }399            }400 401            return true;402        }403 404        /// <summary>405        /// Check if the given string is a valid gac assembly.406        /// </summary>407        /// <param name="assemblyName"></param>408        /// <returns></returns>409        private static bool IsValidGacAssembly(string assemblyName)410        {411#if UNIX412            return false;413#else414            string gacPath = System.Environment.GetEnvironmentVariable("windir") + "\\Microsoft.NET\\assembly";415            string assemblyFile = assemblyName;416            string ngenAssemblyFile = assemblyName;417            if (!assemblyName.EndsWith(StringLiterals.PowerShellILAssemblyExtension, StringComparison.OrdinalIgnoreCase))418            {419                assemblyFile = assemblyName + StringLiterals.PowerShellILAssemblyExtension;420                ngenAssemblyFile = assemblyName + StringLiterals.PowerShellNgenAssemblyExtension;421            }422 423            try424            {425                return Directory.EnumerateFiles(gacPath, assemblyFile, SearchOption.AllDirectories).Any()426                    || Directory.EnumerateFiles(gacPath, ngenAssemblyFile, SearchOption.AllDirectories).Any();427            }428            catch429            {430                return false;431            }432#endif433        }434    }435 436    #endregion437}438