Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes308downloads
SafeValues.cs804 linesDownload Raw Back to parser
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections;5using System.IO;6using System.Linq;7using System.Management.Automation.Internal;8 9/*10 *11 * This visitor makes a determination as to whether an operation is safe in a GetPowerShell API Context.12 * It is modeled on the ConstantValueVisitor with changes which allow those13 * operations which are deemed safe, rather than constant. The following are differences from14 * ConstantValueVisitor:15 *  o Because we are going to call for values in ScriptBlockToPowerShell, the16 *    Get*ValueVisitor class is removed17 *  o IsGetPowerShellSafeValueVisitor only needs to determine whether it is safe, we won't return18 *    anything but that determination (vs actually returning a value in the out constantValue parameter19 *    as is found in the ConstantValueVisitor).20 *  o the internal bool members (Checking* members in ConstantValues) aren't needed as those checks are not germane21 *  o VisitExpandableStringExpression may be safe under the proper circumstances22 *  o VisitIndexExpression may be safe under the proper circumstances23 *  o VisitStatementBlock is safe if its component statements are safe24 *  o VisitBinaryExpression is not safe as it allows for a DOS attack25 *  o VisitVariableExpression is generally safe, there are checks outside of this code for ensuring variables actually26 *    have provided references. Those other checks ensure that the variable isn't something like $PID or $HOME, etc.,27 *    otherwise it's a safe operation, such as reference to a variable such as $true, or passed parameters.28 *  o VisitTypeExpression is not safe as it enables determining what types are available on the system which29 *    can imply what software has been installed on the system.30 *  o VisitMemberExpression is not safe as allows for the same attack as VisitTypeExpression31 *  o VisitArrayExpression may be safe if its components are safe32 *  o VisitArrayLiteral may be safe if its components are safe33 *  o VisitHashtable may be safe if its components are safe34 *  o VisitTernaryExpression may be safe if its components are safe35 */36 37namespace System.Management.Automation.Language38{39    internal class IsSafeValueVisitor : ICustomAstVisitor240    {41        public static bool IsAstSafe(Ast ast, GetSafeValueVisitor.SafeValueContext safeValueContext)42        {43            IsSafeValueVisitor visitor = new IsSafeValueVisitor(safeValueContext);44            return visitor.IsAstSafe(ast);45        }46 47        internal IsSafeValueVisitor(GetSafeValueVisitor.SafeValueContext safeValueContext)48        {49            _safeValueContext = safeValueContext;50        }51 52        internal bool IsAstSafe(Ast ast)53        {54            if ((bool)ast.Accept(this) && _visitCount < MaxVisitCount)55            {56                return true;57            }58 59            return false;60        }61 62        // A readonly singleton with the default SafeValueContext.63        internal static readonly IsSafeValueVisitor Default = new IsSafeValueVisitor(GetSafeValueVisitor.SafeValueContext.Default);64 65        // This is a check of the number of visits66        private uint _visitCount = 0;67 68        private const uint MaxVisitCount = 5000;69        private const int MaxHashtableKeyCount = 500;70 71        // Used to determine if we are being called within a GetPowerShell() context,72        // which does some additional security verification outside of the scope of73        // what we can verify.74        private readonly GetSafeValueVisitor.SafeValueContext _safeValueContext;75 76        public object VisitErrorStatement(ErrorStatementAst errorStatementAst) { return false; }77 78        public object VisitErrorExpression(ErrorExpressionAst errorExpressionAst) { return false; }79 80        public object VisitScriptBlock(ScriptBlockAst scriptBlockAst) { return false; }81 82        public object VisitParamBlock(ParamBlockAst paramBlockAst) { return false; }83 84        public object VisitNamedBlock(NamedBlockAst namedBlockAst) { return false; }85 86        public object VisitTypeConstraint(TypeConstraintAst typeConstraintAst) { return false; }87 88        public object VisitAttribute(AttributeAst attributeAst) { return false; }89 90        public object VisitNamedAttributeArgument(NamedAttributeArgumentAst namedAttributeArgumentAst) { return false; }91 92        public object VisitParameter(ParameterAst parameterAst) { return false; }93 94        public object VisitFunctionDefinition(FunctionDefinitionAst functionDefinitionAst) { return false; }95 96        public object VisitIfStatement(IfStatementAst ifStmtAst) { return false; }97 98        public object VisitTrap(TrapStatementAst trapStatementAst) { return false; }99 100        public object VisitSwitchStatement(SwitchStatementAst switchStatementAst) { return false; }101 102        public object VisitDataStatement(DataStatementAst dataStatementAst) { return false; }103 104        public object VisitForEachStatement(ForEachStatementAst forEachStatementAst) { return false; }105 106        public object VisitDoWhileStatement(DoWhileStatementAst doWhileStatementAst) { return false; }107 108        public object VisitForStatement(ForStatementAst forStatementAst) { return false; }109 110        public object VisitWhileStatement(WhileStatementAst whileStatementAst) { return false; }111 112        public object VisitCatchClause(CatchClauseAst catchClauseAst) { return false; }113 114        public object VisitTryStatement(TryStatementAst tryStatementAst) { return false; }115 116        public object VisitBreakStatement(BreakStatementAst breakStatementAst) { return false; }117 118        public object VisitContinueStatement(ContinueStatementAst continueStatementAst) { return false; }119 120        public object VisitReturnStatement(ReturnStatementAst returnStatementAst) { return false; }121 122        public object VisitExitStatement(ExitStatementAst exitStatementAst) { return false; }123 124        public object VisitThrowStatement(ThrowStatementAst throwStatementAst) { return false; }125 126        public object VisitDoUntilStatement(DoUntilStatementAst doUntilStatementAst) { return false; }127 128        public object VisitAssignmentStatement(AssignmentStatementAst assignmentStatementAst) { return false; }129 130        public object VisitCommand(CommandAst commandAst) { return false; }131 132        public object VisitCommandExpression(CommandExpressionAst commandExpressionAst) { return false; }133 134        public object VisitCommandParameter(CommandParameterAst commandParameterAst) { return false; }135 136        public object VisitFileRedirection(FileRedirectionAst fileRedirectionAst) { return false; }137 138        public object VisitMergingRedirection(MergingRedirectionAst mergingRedirectionAst) { return false; }139 140        public object VisitAttributedExpression(AttributedExpressionAst attributedExpressionAst) { return false; }141 142        public object VisitBlockStatement(BlockStatementAst blockStatementAst) { return false; }143 144        public object VisitInvokeMemberExpression(InvokeMemberExpressionAst invokeMemberExpressionAst) { return false; }145 146        public object VisitTypeDefinition(TypeDefinitionAst typeDefinitionAst) { return false; }147 148        public object VisitPropertyMember(PropertyMemberAst propertyMemberAst) { return false; }149 150        public object VisitFunctionMember(FunctionMemberAst functionMemberAst) { return false; }151 152        public object VisitBaseCtorInvokeMemberExpression(BaseCtorInvokeMemberExpressionAst baseCtorInvokeMemberExpressionAst) { return false; }153 154        public object VisitUsingStatement(UsingStatementAst usingStatement) { return false; }155 156        public object VisitConfigurationDefinition(ConfigurationDefinitionAst configurationDefinitionAst) { return false; }157 158        public object VisitDynamicKeywordStatement(DynamicKeywordStatementAst dynamicKeywordAst) { return false; }159 160        public object VisitIndexExpression(IndexExpressionAst indexExpressionAst)161        {162            return (bool)indexExpressionAst.Index.Accept(this) && (bool)indexExpressionAst.Target.Accept(this);163        }164 165        public object VisitExpandableStringExpression(ExpandableStringExpressionAst expandableStringExpressionAst)166        {167            bool isSafe = true;168            foreach (var nestedExpression in expandableStringExpressionAst.NestedExpressions)169            {170                _visitCount++;171                if (!(bool)nestedExpression.Accept(this))172                {173                    isSafe = false;174                    break;175                }176            }177 178            return isSafe;179        }180 181        public object VisitStatementBlock(StatementBlockAst statementBlockAst)182        {183            bool isSafe = true;184            foreach (var statement in statementBlockAst.Statements)185            {186                _visitCount++;187                if (statement == null)188                {189                    isSafe = false;190                    break;191                }192 193                if (!(bool)statement.Accept(this))194                {195                    isSafe = false;196                    break;197                }198            }199 200            return isSafe;201        }202 203        public object VisitPipeline(PipelineAst pipelineAst)204        {205            var expr = pipelineAst.GetPureExpression();206            return expr != null && (bool)expr.Accept(this);207        }208 209        public object VisitTernaryExpression(TernaryExpressionAst ternaryExpressionAst)210        {211            return (bool)ternaryExpressionAst.Condition.Accept(this) &&212                   (bool)ternaryExpressionAst.IfTrue.Accept(this) &&213                   (bool)ternaryExpressionAst.IfFalse.Accept(this);214        }215 216        public object VisitBinaryExpression(BinaryExpressionAst binaryExpressionAst)217        {218            // This can be used for a denial of service219            // Write-Output (((((("AAAAAAAAAAAAAAAAAAAAAA"*2)*2)*2)*2)*2)*2)220            // Keep on going with that pattern, and we're generating gigabytes of strings.221            return false;222        }223 224        public object VisitUnaryExpression(UnaryExpressionAst unaryExpressionAst)225        {226            bool unaryExpressionIsSafe = unaryExpressionAst.TokenKind.HasTrait(TokenFlags.CanConstantFold) &&227                !unaryExpressionAst.TokenKind.HasTrait(TokenFlags.DisallowedInRestrictedMode) &&228                (bool)unaryExpressionAst.Child.Accept(this);229            if (unaryExpressionIsSafe)230            {231                _visitCount++;232            }233 234            return unaryExpressionIsSafe;235        }236 237        public object VisitConvertExpression(ConvertExpressionAst convertExpressionAst)238        {239            var type = convertExpressionAst.Type.TypeName.GetReflectionType();240            if (type == null)241            {242                return false;243            }244 245            if (!type.IsSafePrimitive())246            {247                // Only do conversions to built-in types - other conversions might not248                // be safe to optimize.249                return false;250            }251 252            _visitCount++;253            return (bool)convertExpressionAst.Child.Accept(this);254        }255 256        public object VisitConstantExpression(ConstantExpressionAst constantExpressionAst)257        {258            _visitCount++;259            return true;260        }261 262        public object VisitStringConstantExpression(StringConstantExpressionAst stringConstantExpressionAst)263        {264            _visitCount++;265            return true;266        }267 268        public object VisitSubExpression(SubExpressionAst subExpressionAst)269        {270            return subExpressionAst.SubExpression.Accept(this);271        }272 273        public object VisitUsingExpression(UsingExpressionAst usingExpressionAst)274        {275            // $using:true should be safe - it's silly to write that, but not harmful.276            _visitCount++;277            return usingExpressionAst.SubExpression.Accept(this);278        }279 280        public object VisitVariableExpression(VariableExpressionAst variableExpressionAst)281        {282            _visitCount++;283 284            if (_safeValueContext == GetSafeValueVisitor.SafeValueContext.GetPowerShell)285            {286                // GetPowerShell does its own validation of allowed variables in the287                // context of the entire script block, and then supplies this visitor288                // with the CommandExpressionAst directly. This289                // prevents us from evaluating variable safety in this visitor,290                // so we rely on GetPowerShell's implementation.291                return true;292            }293 294            if (_safeValueContext == GetSafeValueVisitor.SafeValueContext.ModuleAnalysis)295            {296                return variableExpressionAst.IsConstantVariable() ||297                       (variableExpressionAst.VariablePath.IsUnqualified &&298                        variableExpressionAst.VariablePath.UnqualifiedPath.Equals(SpecialVariables.PSScriptRoot, StringComparison.OrdinalIgnoreCase));299            }300 301            bool unused = false;302            return variableExpressionAst.IsSafeVariableReference(null, ref unused);303        }304 305        public object VisitTypeExpression(TypeExpressionAst typeExpressionAst)306        {307            // Type expressions are not safe as they allow fingerprinting by providing308            // a set of types, you can inspect the types in the AppDomain implying which assemblies are in use309            // and their version310            return false;311        }312 313        public object VisitMemberExpression(MemberExpressionAst memberExpressionAst)314        {315            return false;316        }317 318        public object VisitArrayExpression(ArrayExpressionAst arrayExpressionAst)319        {320            // An Array expression *may* be safe, if its components are safe321            return arrayExpressionAst.SubExpression.Accept(this);322        }323 324        public object VisitArrayLiteral(ArrayLiteralAst arrayLiteralAst)325        {326            bool isSafe = arrayLiteralAst.Elements.All(e => (bool)e.Accept(this));327            // An array literal is safe328            return isSafe;329        }330 331        public object VisitHashtable(HashtableAst hashtableAst)332        {333            if (hashtableAst.KeyValuePairs.Count > MaxHashtableKeyCount)334            {335                return false;336            }337 338            return hashtableAst.KeyValuePairs.All(pair => (bool)pair.Item1.Accept(this) && (bool)pair.Item2.Accept(this));339        }340 341        public object VisitScriptBlockExpression(ScriptBlockExpressionAst scriptBlockExpressionAst)342        {343            // Returning a ScriptBlock instance itself is OK, bad stuff only happens344            // when invoking one (which is blocked)345            return true;346        }347 348        public object VisitParenExpression(ParenExpressionAst parenExpressionAst)349        {350            return parenExpressionAst.Pipeline.Accept(this);351        }352    }353 354    /*355     * This implementation retrieves the safe value without directly calling the compiler356     * except in the case of handling the unary operator357     * ExecutionContext is provided to ensure we can resolve variables358     */359    internal sealed class GetSafeValueVisitor : ICustomAstVisitor2360    {361        internal enum SafeValueContext362        {363            Default,364            GetPowerShell,365            ModuleAnalysis,366            SkipHashtableSizeCheck,367        }368 369        // future proofing370        private GetSafeValueVisitor() { }371 372        public static object GetSafeValue(Ast ast, ExecutionContext context, SafeValueContext safeValueContext)373        {374            t_context = context;375 376            if (safeValueContext == SafeValueContext.SkipHashtableSizeCheck || IsSafeValueVisitor.IsAstSafe(ast, safeValueContext))377            {378                return ast.Accept(new GetSafeValueVisitor());379            }380 381            if (safeValueContext == SafeValueContext.ModuleAnalysis)382            {383                return null;384            }385 386            throw PSTraceSource.NewArgumentException(nameof(ast));387        }388 389        /// <summary>390        /// This field needs to be thread-static to make 'GetSafeValue' thread safe.391        /// </summary>392        [ThreadStatic]393        private static ExecutionContext t_context;394 395        public object VisitErrorStatement(ErrorStatementAst errorStatementAst) { throw PSTraceSource.NewArgumentException(nameof(errorStatementAst)); }396 397        public object VisitErrorExpression(ErrorExpressionAst errorExpressionAst) { throw PSTraceSource.NewArgumentException(nameof(errorExpressionAst)); }398 399        public object VisitScriptBlock(ScriptBlockAst scriptBlockAst) { throw PSTraceSource.NewArgumentException(nameof(scriptBlockAst)); }400 401        public object VisitParamBlock(ParamBlockAst paramBlockAst) { throw PSTraceSource.NewArgumentException(nameof(paramBlockAst)); }402 403        public object VisitNamedBlock(NamedBlockAst namedBlockAst) { throw PSTraceSource.NewArgumentException(nameof(namedBlockAst)); }404 405        public object VisitTypeConstraint(TypeConstraintAst typeConstraintAst) { throw PSTraceSource.NewArgumentException(nameof(typeConstraintAst)); }406 407        public object VisitAttribute(AttributeAst attributeAst) { throw PSTraceSource.NewArgumentException(nameof(attributeAst)); }408 409        public object VisitNamedAttributeArgument(NamedAttributeArgumentAst namedAttributeArgumentAst) { throw PSTraceSource.NewArgumentException(nameof(namedAttributeArgumentAst)); }410 411        public object VisitParameter(ParameterAst parameterAst) { throw PSTraceSource.NewArgumentException(nameof(parameterAst)); }412 413        public object VisitFunctionDefinition(FunctionDefinitionAst functionDefinitionAst) { throw PSTraceSource.NewArgumentException(nameof(functionDefinitionAst)); }414 415        public object VisitIfStatement(IfStatementAst ifStmtAst) { throw PSTraceSource.NewArgumentException(nameof(ifStmtAst)); }416 417        public object VisitTrap(TrapStatementAst trapStatementAst) { throw PSTraceSource.NewArgumentException(nameof(trapStatementAst)); }418 419        public object VisitSwitchStatement(SwitchStatementAst switchStatementAst) { throw PSTraceSource.NewArgumentException(nameof(switchStatementAst)); }420 421        public object VisitDataStatement(DataStatementAst dataStatementAst) { throw PSTraceSource.NewArgumentException(nameof(dataStatementAst)); }422 423        public object VisitForEachStatement(ForEachStatementAst forEachStatementAst) { throw PSTraceSource.NewArgumentException(nameof(forEachStatementAst)); }424 425        public object VisitDoWhileStatement(DoWhileStatementAst doWhileStatementAst) { throw PSTraceSource.NewArgumentException(nameof(doWhileStatementAst)); }426 427        public object VisitForStatement(ForStatementAst forStatementAst) { throw PSTraceSource.NewArgumentException(nameof(forStatementAst)); }428 429        public object VisitWhileStatement(WhileStatementAst whileStatementAst) { throw PSTraceSource.NewArgumentException(nameof(whileStatementAst)); }430 431        public object VisitCatchClause(CatchClauseAst catchClauseAst) { throw PSTraceSource.NewArgumentException(nameof(catchClauseAst)); }432 433        public object VisitTryStatement(TryStatementAst tryStatementAst) { throw PSTraceSource.NewArgumentException(nameof(tryStatementAst)); }434 435        public object VisitBreakStatement(BreakStatementAst breakStatementAst) { throw PSTraceSource.NewArgumentException(nameof(breakStatementAst)); }436 437        public object VisitContinueStatement(ContinueStatementAst continueStatementAst) { throw PSTraceSource.NewArgumentException(nameof(continueStatementAst)); }438 439        public object VisitReturnStatement(ReturnStatementAst returnStatementAst) { throw PSTraceSource.NewArgumentException(nameof(returnStatementAst)); }440 441        public object VisitExitStatement(ExitStatementAst exitStatementAst) { throw PSTraceSource.NewArgumentException(nameof(exitStatementAst)); }442 443        public object VisitThrowStatement(ThrowStatementAst throwStatementAst) { throw PSTraceSource.NewArgumentException(nameof(throwStatementAst)); }444 445        public object VisitDoUntilStatement(DoUntilStatementAst doUntilStatementAst) { throw PSTraceSource.NewArgumentException(nameof(doUntilStatementAst)); }446 447        public object VisitAssignmentStatement(AssignmentStatementAst assignmentStatementAst) { throw PSTraceSource.NewArgumentException(nameof(assignmentStatementAst)); }448 449        public object VisitCommand(CommandAst commandAst) { throw PSTraceSource.NewArgumentException(nameof(commandAst)); }450 451        public object VisitCommandExpression(CommandExpressionAst commandExpressionAst) { throw PSTraceSource.NewArgumentException(nameof(commandExpressionAst)); }452 453        public object VisitCommandParameter(CommandParameterAst commandParameterAst) { throw PSTraceSource.NewArgumentException(nameof(commandParameterAst)); }454 455        public object VisitFileRedirection(FileRedirectionAst fileRedirectionAst) { throw PSTraceSource.NewArgumentException(nameof(fileRedirectionAst)); }456 457        public object VisitMergingRedirection(MergingRedirectionAst mergingRedirectionAst) { throw PSTraceSource.NewArgumentException(nameof(mergingRedirectionAst)); }458 459        public object VisitAttributedExpression(AttributedExpressionAst attributedExpressionAst) { throw PSTraceSource.NewArgumentException(nameof(attributedExpressionAst)); }460 461        public object VisitBlockStatement(BlockStatementAst blockStatementAst) { throw PSTraceSource.NewArgumentException(nameof(blockStatementAst)); }462 463        public object VisitInvokeMemberExpression(InvokeMemberExpressionAst invokeMemberExpressionAst) { throw PSTraceSource.NewArgumentException(nameof(invokeMemberExpressionAst)); }464 465        public object VisitTypeDefinition(TypeDefinitionAst typeDefinitionAst) { throw PSTraceSource.NewArgumentException(nameof(typeDefinitionAst)); }466 467        public object VisitPropertyMember(PropertyMemberAst propertyMemberAst) { throw PSTraceSource.NewArgumentException(nameof(propertyMemberAst)); }468 469        public object VisitFunctionMember(FunctionMemberAst functionMemberAst) { throw PSTraceSource.NewArgumentException(nameof(functionMemberAst)); }470 471        public object VisitBaseCtorInvokeMemberExpression(BaseCtorInvokeMemberExpressionAst baseCtorInvokeMemberExpressionAst) { throw PSTraceSource.NewArgumentException(nameof(baseCtorInvokeMemberExpressionAst)); }472 473        public object VisitUsingStatement(UsingStatementAst usingStatement) { throw PSTraceSource.NewArgumentException(nameof(usingStatement)); }474 475        public object VisitConfigurationDefinition(ConfigurationDefinitionAst configurationDefinitionAst) { throw PSTraceSource.NewArgumentException(nameof(configurationDefinitionAst)); }476 477        public object VisitDynamicKeywordStatement(DynamicKeywordStatementAst dynamicKeywordAst) { throw PSTraceSource.NewArgumentException(nameof(dynamicKeywordAst)); }478 479        //480        // This is similar to logic used deep in the engine for slicing something that can be sliced481        // It's recreated here because there isn't really a simple API which can be called for this case.482        // this can throw, but there really isn't useful information we can add, as the483        // offending expression will be presented in the case of any failure484        //485        private static object GetSingleValueFromTarget(object target, object index)486        {487            var targetString = target as string;488            if (targetString != null)489            {490                var offset = (int)index;491                if (Math.Abs(offset) >= targetString.Length)492                {493                    return null;494                }495 496                return offset >= 0 ? targetString[offset] : targetString[targetString.Length + offset];497            }498 499            var targetArray = target as object[];500            if (targetArray != null)501            {502                // this can throw, that just gets percolated back503                var offset = (int)index;504                if (Math.Abs(offset) >= targetArray.Length)505                {506                    return null;507                }508 509                return offset >= 0 ? targetArray[offset] : targetArray[targetArray.Length + offset];510            }511 512            var targetHashtable = target as Hashtable;513            if (targetHashtable != null)514            {515                return targetHashtable[index];516            }517            // The actual exception doesn't really matter because the caller in ScriptBlockToPowerShell518            // will present the user with the offending script segment519            throw new Exception();520        }521 522        private static object GetIndexedValueFromTarget(object target, object index)523        {524            var indexArray = index as object[];525            return indexArray != null ? ((object[])indexArray).Select(i => GetSingleValueFromTarget(target, i)).ToArray() : GetSingleValueFromTarget(target, index);526        }527 528        public object VisitIndexExpression(IndexExpressionAst indexExpressionAst)529        {530            // Get the value of the index and value and call the compiler531            var index = indexExpressionAst.Index.Accept(this);532            var target = indexExpressionAst.Target.Accept(this);533 534            if (index is null || target is null)535            {536                throw new ArgumentNullException(nameof(indexExpressionAst));537            }538 539            return GetIndexedValueFromTarget(target, index);540        }541 542        public object VisitExpandableStringExpression(ExpandableStringExpressionAst expandableStringExpressionAst)543        {544            object[] safeValues = new object[expandableStringExpressionAst.NestedExpressions.Count];545            // retrieve OFS, and if it doesn't exist set it to space546            string ofs = null;547            if (t_context != null)548            {549                ofs = t_context.SessionState.PSVariable.GetValue("OFS") as string;550            }551 552            ofs ??= " ";553 554            for (int offset = 0; offset < safeValues.Length; offset++)555            {556                var result = expandableStringExpressionAst.NestedExpressions[offset].Accept(this);557                // depending on the nested expression we may retrieve a variable, or even need to558                // execute a sub-expression. The result of which may be returned559                // as a scalar, array or nested array. If the unwrap of first array doesn't contain a nested560                // array we can then pass it to string.Join. If it *does* contain an array,561                // we need to unwrap the inner array and pass *that* to string.Join.562                //563                // This means we get the same answer with GetPowerShell() as in the command-line564                // { echo "abc $true $(1) $(2,3) def" }.Invoke() gives the same answer as565                // { echo "abc $true $(1) $(2,3) def" }.GetPowerShell().Invoke()566                // abc True 1 2 3 def567                // as does { echo "abc $true $(1) $(@(1,2),@(3,4)) def"568                // which is569                // abc True 1 System.Object[] System.Object[] def570                // fortunately, at this point, we're dealing with strings, so whatever the result571                // from the ToString method of the array (or scalar) elements, that's symmetrical with572                // a standard scriptblock invocation behavior573                var resultArray = result as object[];574 575                // In this environment, we can't use $OFS as we might expect. Retrieving OFS576                // might possibly leak server side info which we don't want, so we'll577                // assign ' ' as our OFS for purposes of GetPowerShell578                // Also, this will not call any script implementations of ToString (ala types.clixml)579                // This *will* result in a different result in those cases. However, to execute some580                // arbitrary script at this stage would be opening ourselves up to an attack581                if (resultArray != null)582                {583                    object[] subExpressionResult = new object[resultArray.Length];584                    for (int subExpressionOffset = 0;585                        subExpressionOffset < subExpressionResult.Length;586                        subExpressionOffset++)587                    {588                        // check to see if there is an array in our array,589                        object[] subResult = resultArray[subExpressionOffset] as object[];590                        if (subResult != null)591                        {592                            subExpressionResult[subExpressionOffset] = string.Join(ofs, subResult);593                        }594                        else // it is a scalar, so we can just add it to our collections595                        {596                            subExpressionResult[subExpressionOffset] = resultArray[subExpressionOffset];597                        }598                    }599 600                    safeValues[offset] = string.Join(ofs, subExpressionResult);601                }602                else603                {604                    safeValues[offset] = result;605                }606            }607 608            return StringUtil.Format(expandableStringExpressionAst.FormatExpression, safeValues);609        }610 611        public object VisitStatementBlock(StatementBlockAst statementBlockAst)612        {613            ArrayList statementList = new ArrayList();614            foreach (var statement in statementBlockAst.Statements)615            {616                if (statement != null)617                {618                    var obj = statement.Accept(this);619                    var enumerator = LanguagePrimitives.GetEnumerator(obj);620                    if (enumerator != null)621                    {622                        while (enumerator.MoveNext())623                        {624                            statementList.Add(enumerator.Current);625                        }626                    }627                    else628                    {629                        statementList.Add(obj);630                    }631                }632                else633                {634                    throw PSTraceSource.NewArgumentException(nameof(statementBlockAst));635                }636            }637 638            return statementList.ToArray();639        }640 641        public object VisitPipeline(PipelineAst pipelineAst)642        {643            var expr = pipelineAst.GetPureExpression();644            if (expr != null)645            {646                return expr.Accept(this);647            }648 649            throw PSTraceSource.NewArgumentException(nameof(pipelineAst));650        }651 652        public object VisitTernaryExpression(TernaryExpressionAst ternaryExpressionAst)653        {654            if (t_context == null)655            {656                throw PSTraceSource.NewArgumentException(nameof(ternaryExpressionAst));657            }658 659            return Compiler.GetExpressionValue(ternaryExpressionAst, isTrustedInput: true, t_context, usingValues: null);660        }661 662        public object VisitBinaryExpression(BinaryExpressionAst binaryExpressionAst)663        {664            // This can be used for a denial of service665            // Write-Output (((((("AAAAAAAAAAAAAAAAAAAAAA"*2)*2)*2)*2)*2)*2)666            // Keep on going with that pattern, and we're generating gigabytes of strings.667            throw PSTraceSource.NewArgumentException(nameof(binaryExpressionAst));668        }669 670        public object VisitUnaryExpression(UnaryExpressionAst unaryExpressionAst)671        {672            if (t_context == null)673            {674                throw PSTraceSource.NewArgumentException(nameof(unaryExpressionAst));675            }676 677            return Compiler.GetExpressionValue(unaryExpressionAst, isTrustedInput: true, t_context, usingValues: null);678        }679 680        public object VisitConvertExpression(ConvertExpressionAst convertExpressionAst)681        {682            // at this point, we know we're safe because we checked both the type and the child,683            // so now we can just call the compiler and indicate that it's trusted (at this point)684            if (t_context == null)685            {686                throw PSTraceSource.NewArgumentException(nameof(convertExpressionAst));687            }688 689            return Compiler.GetExpressionValue(convertExpressionAst, isTrustedInput: true, t_context, usingValues: null);690        }691 692        public object VisitConstantExpression(ConstantExpressionAst constantExpressionAst)693        {694            return constantExpressionAst.Value;695        }696 697        public object VisitStringConstantExpression(StringConstantExpressionAst stringConstantExpressionAst)698        {699            return stringConstantExpressionAst.Value;700        }701 702        public object VisitSubExpression(SubExpressionAst subExpressionAst)703        {704            return subExpressionAst.SubExpression.Accept(this);705        }706 707        public object VisitUsingExpression(UsingExpressionAst usingExpressionAst)708        {709            // $using:true should be safe - it's silly to write that, but not harmful.710            return usingExpressionAst.SubExpression.Accept(this);711        }712 713        public object VisitVariableExpression(VariableExpressionAst variableExpressionAst)714        {715            // There are earlier checks to be sure that we are not using unreferenced variables716            // this ensures that we only use what was declared in the param block717            // other variables such as true/false/args etc have been already vetted718            string name = variableExpressionAst.VariablePath.UnqualifiedPath;719            if (variableExpressionAst.IsConstantVariable())720            {721                if (name.Equals(SpecialVariables.True, StringComparison.OrdinalIgnoreCase))722                    return true;723 724                if (name.Equals(SpecialVariables.False, StringComparison.OrdinalIgnoreCase))725                    return false;726 727                Diagnostics.Assert(name.Equals(SpecialVariables.Null, StringComparison.OrdinalIgnoreCase), "Unexpected constant variable");728                return null;729            }730 731            if (name.Equals(SpecialVariables.PSScriptRoot, StringComparison.OrdinalIgnoreCase))732            {733                var scriptFileName = variableExpressionAst.Extent.File;734                if (scriptFileName == null)735                    return null;736 737                return Path.GetDirectoryName(scriptFileName);738            }739 740            if (t_context != null)741            {742                return VariableOps.GetVariableValue(variableExpressionAst.VariablePath, t_context, variableExpressionAst);743            }744 745            throw PSTraceSource.NewArgumentException(nameof(variableExpressionAst));746        }747 748        public object VisitTypeExpression(TypeExpressionAst typeExpressionAst)749        {750            // Type expressions are not safe as they allow fingerprinting by providing751            // a set of types, you can inspect the types in the AppDomain implying which assemblies are in use752            // and their version753            throw PSTraceSource.NewArgumentException(nameof(typeExpressionAst));754        }755 756        public object VisitMemberExpression(MemberExpressionAst memberExpressionAst)757        {758            throw PSTraceSource.NewArgumentException(nameof(memberExpressionAst));759        }760 761        public object VisitArrayExpression(ArrayExpressionAst arrayExpressionAst)762        {763            // An Array expression *may* be safe, if its components are safe764            var arrayExpressionAstResult = (object[])arrayExpressionAst.SubExpression.Accept(this);765            return arrayExpressionAstResult;766        }767 768        public object VisitArrayLiteral(ArrayLiteralAst arrayLiteralAst)769        {770            // An array literal is safe771            ArrayList arrayElements = new ArrayList();772            foreach (var element in arrayLiteralAst.Elements)773            {774                arrayElements.Add(element.Accept(this));775            }776 777            return arrayElements.ToArray();778        }779 780        public object VisitHashtable(HashtableAst hashtableAst)781        {782            Hashtable hashtable = new Hashtable(StringComparer.CurrentCultureIgnoreCase);783            foreach (var pair in hashtableAst.KeyValuePairs)784            {785                var key = pair.Item1.Accept(this);786                var value = pair.Item2.Accept(this);787                hashtable.Add(key, value);788            }789 790            return hashtable;791        }792 793        public object VisitScriptBlockExpression(ScriptBlockExpressionAst scriptBlockExpressionAst)794        {795            return ScriptBlock.Create(scriptBlockExpressionAst.Extent.Text);796        }797 798        public object VisitParenExpression(ParenExpressionAst parenExpressionAst)799        {800            return parenExpressionAst.Pipeline.Accept(this);801        }802    }803}804