MegaBites-AI/Windows-powershell
0308
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 