Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes308downloads
ExternalScriptInfo.cs634 linesDownload Raw Back to engine
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections.Generic;5using System.Collections.ObjectModel;6using System.IO;7using System.Management.Automation.Language;8using System.Management.Automation.Runspaces;9using System.Management.Automation.Security;10using System.Text;11 12using Microsoft.PowerShell.Commands;13 14namespace System.Management.Automation15{16    /// <summary>17    /// Provides information for scripts that are directly executable by PowerShell18    /// but are not built into the runspace configuration.19    /// </summary>20    public class ExternalScriptInfo : CommandInfo, IScriptCommandInfo21    {22        #region ctor23 24        /// <summary>25        /// Creates an instance of the ExternalScriptInfo class with the specified name, and path.26        /// </summary>27        /// <param name="name">28        /// The name of the script.29        /// </param>30        /// <param name="path">31        /// The path to the script32        /// </param>33        /// <param name="context">34        /// The context of the currently running engine.35        /// </param>36        /// <exception cref="ArgumentNullException">37        /// If <paramref name="context"/> is null.38        /// </exception>39        /// <exception cref="ArgumentException">40        /// If <paramref name="path"/> is null or empty.41        /// </exception>42        internal ExternalScriptInfo(string name, string path, ExecutionContext context)43            : base(name, CommandTypes.ExternalScript, context)44        {45            if (string.IsNullOrEmpty(path))46            {47                throw PSTraceSource.NewArgumentException(nameof(path));48            }49 50            Diagnostics.Assert(IO.Path.IsPathRooted(path), "Caller makes sure that 'path' is already resolved.");51 52            // Path might contain short-name syntax such as 'DOCUME~1'. Use Path.GetFullPath to expand the short name53            _path = IO.Path.GetFullPath(path);54            CommonInitialization();55        }56 57        /// <summary>58        /// Creates an instance of ExternalScriptInfo that has no ExecutionContext.59        /// This is used exclusively to pass it to the AuthorizationManager that just uses the path parameter.60        /// </summary>61        /// <param name="name">62        /// The name of the script.63        /// </param>64        /// <param name="path">65        /// The path to the script66        /// </param>67        /// <exception cref="ArgumentException">68        /// If <paramref name="path"/> is null or empty.69        /// </exception>70        internal ExternalScriptInfo(string name, string path) : base(name, CommandTypes.ExternalScript)71        {72            if (string.IsNullOrEmpty(path))73            {74                throw PSTraceSource.NewArgumentException(nameof(path));75            }76 77            Diagnostics.Assert(IO.Path.IsPathRooted(path), "Caller makes sure that 'path' is already resolved.");78 79            // Path might contain short-name syntax such as 'DOCUME~1'. Use Path.GetFullPath to expand the short name80            _path = IO.Path.GetFullPath(path);81            CommonInitialization();82        }83 84        /// <summary>85        /// This is a copy constructor, used primarily for get-command.86        /// </summary>87        internal ExternalScriptInfo(ExternalScriptInfo other)88            : base(other)89        {90            _path = other._path;91            CommonInitialization();92        }93 94        /// <summary>95        /// Common initialization for all constructors.96        /// </summary>97        private void CommonInitialization()98        {99            // Assume external scripts are untrusted by default (for Get-Command, etc)100            // until we've actually parsed their script block.101            if (SystemPolicy.GetSystemLockdownPolicy() != SystemEnforcementMode.None)102            {103                // Get the lock down policy with no handle. This only impacts command discovery,104                // as the real language mode assignment will be done when we read the script105                // contents.106                switch (SystemPolicy.GetLockdownPolicy(_path, null))107                {108                    case SystemEnforcementMode.None:109                        DefiningLanguageMode = PSLanguageMode.FullLanguage;110                        break;111 112                    case SystemEnforcementMode.Audit:113                        // For policy audit mode, language mode is set to CL but audit messages are emitted to log114                        // instead of applying restrictions.115                        DefiningLanguageMode = PSLanguageMode.ConstrainedLanguage;116                        break;117 118                    case SystemEnforcementMode.Enforce:119                        DefiningLanguageMode = PSLanguageMode.ConstrainedLanguage;120                        break;121                }122            }123        }124 125        /// <summary>126        /// Create a copy of commandInfo for GetCommandCommand so that we can generate parameter127        /// sets based on an argument list (so we can get the dynamic parameters.)128        /// </summary>129        internal override CommandInfo CreateGetCommandCopy(object[] argumentList)130        {131            ExternalScriptInfo copy = new ExternalScriptInfo(this) { IsGetCommandCopy = true, Arguments = argumentList };132            return copy;133        }134 135        #endregion ctor136 137        internal override HelpCategory HelpCategory138        {139            get { return HelpCategory.ExternalScript; }140        }141 142        /// <summary>143        /// Gets the path to the script file.144        /// </summary>145        public string Path146        {147            get { return _path; }148        }149 150        private readonly string _path = string.Empty;151 152        /// <summary>153        /// Gets the path to the script file.154        /// </summary>155        public override string Definition156        {157            get { return Path; }158        }159 160        /// <summary>161        /// Gets the source of this command.162        /// </summary>163        public override string Source164        {165            get { return this.Definition; }166        }167 168        /// <summary>169        /// Returns the syntax of a command.170        /// </summary>171        internal override string Syntax172        {173            get174            {175                StringBuilder synopsis = new StringBuilder();176 177                foreach (CommandParameterSetInfo parameterSet in ParameterSets)178                {179                    synopsis.AppendLine(180                        string.Format(181                            Globalization.CultureInfo.CurrentCulture,182                            "{0} {1}",183                            Name,184                            parameterSet));185                }186 187                return synopsis.ToString();188            }189        }190 191        /// <summary>192        /// Determine the visibility for this script...193        /// </summary>194        public override SessionStateEntryVisibility Visibility195        {196            get197            {198                if (Context == null)199                {200                    return SessionStateEntryVisibility.Public;201                }202 203                return Context.EngineSessionState.CheckScriptVisibility(_path);204            }205 206            set207            {208                throw PSTraceSource.NewNotImplementedException();209            }210        }211 212        /// <summary>213        /// The script block that represents the external script.214        /// </summary>215        public ScriptBlock ScriptBlock216        {217            get218            {219                if (_scriptBlock == null)220                {221                    // Skip ShouldRun check for .psd1 files.222                    // Use ValidateScriptInfo() for explicitly validating the checkpolicy for psd1 file.223                    //224                    if (!_path.EndsWith(".psd1", StringComparison.OrdinalIgnoreCase))225                    {226                        ValidateScriptInfo(null);227                    }228 229                    // parse the script into an expression tree...230                    ScriptBlock newScriptBlock = ParseScriptContents(new Parser(), _path, ScriptContents, DefiningLanguageMode);231                    this.ScriptBlock = newScriptBlock;232                }233 234                return _scriptBlock;235            }236 237            private set238            {239                _scriptBlock = value;240                if (value != null)241                {242                    _scriptBlock.LanguageMode = this.DefiningLanguageMode;243                }244            }245        }246 247        private ScriptBlock _scriptBlock;248        private ScriptBlockAst _scriptBlockAst;249 250        private static ScriptBlock ParseScriptContents(Parser parser, string fileName, string fileContents, PSLanguageMode? definingLanguageMode)251        {252            // If we are in ConstrainedLanguage mode but the defining language mode is FullLanguage, then we need253            // to parse the script contents in FullLanguage mode context.  Otherwise we will get bogus parsing errors254            // such as "Configuration keyword not allowed".255            if (definingLanguageMode.HasValue && (definingLanguageMode == PSLanguageMode.FullLanguage))256            {257                var context = LocalPipeline.GetExecutionContextFromTLS();258                if ((context != null) && (context.LanguageMode == PSLanguageMode.ConstrainedLanguage))259                {260                    context.LanguageMode = PSLanguageMode.FullLanguage;261                    try262                    {263                        return ScriptBlock.Create(parser, fileName, fileContents);264                    }265                    finally266                    {267                        context.LanguageMode = PSLanguageMode.ConstrainedLanguage;268                    }269                }270            }271 272            return ScriptBlock.Create(parser, fileName, fileContents);273        }274 275        internal ScriptBlockAst GetScriptBlockAst()276        {277            var scriptContents = ScriptContents;278            if (_scriptBlock == null)279            {280                this.ScriptBlock = ScriptBlock.TryGetCachedScriptBlock(_path, scriptContents);281            }282 283            if (_scriptBlock != null)284            {285                return (ScriptBlockAst)_scriptBlock.Ast;286            }287 288            if (_scriptBlockAst == null)289            {290                ParseError[] errors;291                Parser parser = new Parser();292 293                // If we are in ConstrainedLanguage mode but the defining language mode is FullLanguage, then we need294                // to parse the script contents in FullLanguage mode context.  Otherwise we will get bogus parsing errors295                // such as "Configuration or Class keyword not allowed".296                var context = LocalPipeline.GetExecutionContextFromTLS();297                if (context != null && context.LanguageMode == PSLanguageMode.ConstrainedLanguage &&298                    DefiningLanguageMode == PSLanguageMode.FullLanguage)299                {300                    context.LanguageMode = PSLanguageMode.FullLanguage;301                    try302                    {303                        _scriptBlockAst = parser.Parse(_path, ScriptContents, null, out errors, ParseMode.Default);304                    }305                    finally306                    {307                        context.LanguageMode = PSLanguageMode.ConstrainedLanguage;308                    }309                }310                else311                {312                    _scriptBlockAst = parser.Parse(_path, ScriptContents, null, out errors, ParseMode.Default);313                }314 315                if (errors.Length == 0)316                {317                    this.ScriptBlock = new ScriptBlock(_scriptBlockAst, isFilter: false);318                    ScriptBlock.CacheScriptBlock(_scriptBlock.Clone(), _path, scriptContents);319                }320            }321 322            return _scriptBlockAst;323        }324 325        /// <summary>326        /// Validates the external script info.327        /// </summary>328        /// <param name="host"></param>329        public void ValidateScriptInfo(Host.PSHost host)330        {331            if (!_signatureChecked)332            {333                ExecutionContext context = Context ?? LocalPipeline.GetExecutionContextFromTLS();334 335                ReadScriptContents();336 337                // We have no way to check the signature w/o context because we don't have338                // an AuthorizationManager.  This can happen during initialization when trying339                // to get the CommandMetadata for a script (either to prepopulate the metadata340                // or creating a proxy).  If context can be null under any other circumstances,341                // we need to make sure it's acceptable if the parser is invoked on unsigned scripts.342                if (context != null)343                {344                    CommandDiscovery.ShouldRun(context, host, this, CommandOrigin.Internal);345                    _signatureChecked = true;346                }347            }348        }349 350        /// <summary>351        /// The output type(s) is specified in the script block.352        /// </summary>353        public override ReadOnlyCollection<PSTypeName> OutputType354        {355            get { return ScriptBlock.OutputType; }356        }357 358        internal bool SignatureChecked359        {360            set { _signatureChecked = value; }361        }362 363        private bool _signatureChecked;364 365        #region Internal366 367        /// <summary>368        /// The command metadata for the script.369        /// </summary>370        internal override CommandMetadata CommandMetadata371        {372            get373            {374                return _commandMetadata ??=375                    new CommandMetadata(this.ScriptBlock, this.Name, LocalPipeline.GetExecutionContextFromTLS());376            }377        }378 379        private CommandMetadata _commandMetadata;380 381        /// <summary>382        /// True if the command has dynamic parameters, false otherwise.383        /// </summary>384        internal override bool ImplementsDynamicParameters385        {386            get387            {388                try389                {390                    return ScriptBlock.HasDynamicParameters;391                }392                catch (ParseException) { }393                catch (ScriptRequiresException) { }394 395                // If we got here, there was some sort of parsing exception.  We'll just396                // ignore it and assume the script does not implement dynamic parameters.397                // Furthermore, we'll clear out the fields so that the next attempt to398                // access ScriptBlock will result in an exception that doesn't get ignored.399                _scriptBlock = null;400                _scriptContents = null;401 402                return false;403            }404        }405 406        #endregion Internal407 408        private ScriptRequirements GetRequiresData()409        {410            return GetScriptBlockAst().ScriptRequirements;411        }412 413        internal string RequiresApplicationID414        {415            get416            {417                var data = GetRequiresData();418                return data?.RequiredApplicationId;419            }420        }421 422        internal uint ApplicationIDLineNumber423        {424            get { return 0; }425        }426 427        internal Version RequiresPSVersion428        {429            get430            {431                var data = GetRequiresData();432                return data?.RequiredPSVersion;433            }434        }435 436        internal IEnumerable<string> RequiresPSEditions437        {438            get439            {440                var data = GetRequiresData();441                return data?.RequiredPSEditions;442            }443        }444 445        internal IEnumerable<ModuleSpecification> RequiresModules446        {447            get448            {449                var data = GetRequiresData();450                return data?.RequiredModules;451            }452        }453 454        internal bool RequiresElevation455        {456            get457            {458                var data = GetRequiresData();459                return data != null && data.IsElevationRequired;460            }461        }462 463        internal uint PSVersionLineNumber464        {465            get { return 0; }466        }467 468        /// <summary>469        /// Gets the original contents of the script.470        /// </summary>471        public string ScriptContents472        {473            get474            {475                if (_scriptContents == null)476                {477                    ReadScriptContents();478                }479 480                return _scriptContents;481            }482        }483 484        private string _scriptContents;485 486        /// <summary>487        /// Gets the original encoding of the script.488        /// </summary>489        public Encoding OriginalEncoding490        {491            get492            {493                if (_scriptContents == null)494                {495                    ReadScriptContents();496                }497 498                return _originalEncoding;499            }500        }501 502        private Encoding _originalEncoding;503 504        private void ReadScriptContents()505        {506            if (_scriptContents == null)507            {508                // make sure we can actually load the script and that it's non-empty509                // before we call it.510 511                // Note, although we are passing ASCII as the encoding, the StreamReader512                // class still obeys the byte order marks at the beginning of the file513                // if present. If not present, then ASCII is used as the default encoding.514 515                try516                {517                    using (FileStream readerStream = new FileStream(_path, FileMode.Open, FileAccess.Read))518                    {519                        using (StreamReader scriptReader = new StreamReader(readerStream, Encoding.Default))520                        {521                            _scriptContents = scriptReader.ReadToEnd();522                            _originalEncoding = scriptReader.CurrentEncoding;523 524                            // Check this file against any system wide enforcement policies.525                            SystemScriptFileEnforcement filePolicyEnforcement = SystemPolicy.GetFilePolicyEnforcement(_path, readerStream);526                            switch (filePolicyEnforcement)527                            {528                                case SystemScriptFileEnforcement.None:529                                    if (Context != null)530                                    {531                                        DefiningLanguageMode = Context.LanguageMode;532                                    }533                                    break;534 535                                case SystemScriptFileEnforcement.Allow:536                                    DefiningLanguageMode = PSLanguageMode.FullLanguage;537                                    break;538 539                                case SystemScriptFileEnforcement.AllowConstrained:540                                    DefiningLanguageMode = PSLanguageMode.ConstrainedLanguage;541                                    break;542 543                                case SystemScriptFileEnforcement.AllowConstrainedAudit:544                                    SystemPolicy.LogWDACAuditMessage(545                                        context: Context,546                                        title: SecuritySupportStrings.ExternalScriptWDACLogTitle,547                                        message: string.Format(Globalization.CultureInfo.CurrentUICulture, SecuritySupportStrings.ExternalScriptWDACLogMessage, _path),548                                        fqid: "ScriptFileNotTrustedByPolicy");549                                    // We set the language mode to Constrained Language, even though in policy audit mode no restrictions are applied550                                    // and instead an audit log message is generated wherever a restriction would be applied.551                                    DefiningLanguageMode = PSLanguageMode.ConstrainedLanguage;552                                    break;553 554                                case SystemScriptFileEnforcement.Block:555                                    throw new PSSecurityException(556                                        string.Format(557                                            Globalization.CultureInfo.CurrentUICulture,558                                            SecuritySupportStrings.ScriptFileBlockedBySystemPolicy,559                                            _path));560 561                                default:562                                    throw new PSSecurityException(563                                        string.Format(564                                            Globalization.CultureInfo.CurrentUICulture,565                                            SecuritySupportStrings.UnknownSystemScriptFileEnforcement,566                                            filePolicyEnforcement));567                            }568                        }569                    }570                }571                catch (ArgumentException e)572                {573                    // This catches PSArgumentException as well.574                    ThrowCommandNotFoundException(e);575                }576                catch (IOException e)577                {578                    ThrowCommandNotFoundException(e);579                }580                catch (NotSupportedException e)581                {582                    ThrowCommandNotFoundException(e);583                }584                catch (UnauthorizedAccessException e)585                {586                    // this is unadvertised exception thrown by the StreamReader ctor when587                    // no permission to read the script file588                    ThrowCommandNotFoundException(e);589                }590            }591        }592 593        private static void ThrowCommandNotFoundException(Exception innerException)594        {595            CommandNotFoundException cmdE = new CommandNotFoundException(innerException.Message, innerException);596            throw cmdE;597        }598    }599 600    /// <summary>601    /// Thrown when fail to parse #requires statements. Caught by CommandDiscovery.602    /// </summary>603    internal class ScriptRequiresSyntaxException : ScriptRequiresException604    {605        internal ScriptRequiresSyntaxException(string message)606            : base(message)607        {608        }609    }610 611    /// <summary>612    /// Defines the name and version tuple of a PSSnapin.613    /// </summary>614    public class PSSnapInSpecification615    {616        internal PSSnapInSpecification(string psSnapinName)617        {618            PSSnapInInfo.VerifyPSSnapInFormatThrowIfError(psSnapinName);619            Name = psSnapinName;620            Version = null;621        }622 623        /// <summary>624        /// The name of the snapin.625        /// </summary>626        public string Name { get; internal set; }627 628        /// <summary>629        /// The version of the snapin.630        /// </summary>631        public Version Version { get; internal set; }632    }633}634