-
-
Save Jaykul/4a6bad4f73ed518dfefc37ded36f511d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function Expand-AstVariables { | |
param ( | |
[Parameter(Mandatory, ValueFromPipeline)] | |
[System.Management.Automation.Language.Ast] $Statement, | |
[Parameter(Mandatory)] | |
[ref] $result, | |
[Parameter(Mandatory)] | |
[PSObject] $Properties | |
) | |
begin { | |
} | |
process { | |
if (-not $result.Value) | |
{ | |
return | |
} | |
switch ($Statement.GetType().FullName) | |
{ | |
"System.Management.Automation.Language.ExpandableStringExpressionAst" { | |
$string = $Statement.Extent.Text | |
$nestedExpressions = $Statement.NestedExpressions | |
# iterate nested expression backwards - this way we won't need to re-calculate offsets | |
for ($i = $nestedExpressions.Count - 1; $i -ge 0; $i--){ | |
$subExpression = $nestedExpressions[$i] | |
$resolvedVariable = $subExpression | |
# Get start/end positions of sub expression | |
$startPos = $subExpression.Extent.StartScriptPosition | |
$endPos = $subExpression.Extent.EndScriptPosition | |
# Resolve Here | |
$resolvedVariable = Expand-AstVariables -Statement $subExpression -Result $result -Properties $Properties | |
if (-not $result.Value) { | |
# if ast Walking is not valid, REVERT to Default | |
return $Statement.Extent.Text | |
} | |
$resolvedValue = "{0}" -f ($resolvedVariable -join " ") | |
# Remove the variable expression | |
$string = $string.Remove(($startPos.Offset-$statement.Extent.StartOffset), $endPos.Offset - $startPos.Offset) | |
# Insert the resolved value | |
$string = $string.Insert(($startPos.Offset-$statement.Extent.StartOffset), $resolvedValue) | |
} | |
# Trim start and end as AST Adds additional Escaping | |
$string = $string.Substring(1, $string.Length - 2) | |
return $string | |
} | |
"System.Management.Automation.Language.SubExpressionAst" { | |
$item = $Statement.SubExpression | |
} | |
"System.Management.Automation.Language.StatementBlockAst" { | |
$item = $Statement.Statements | |
} | |
"System.Management.Automation.Language.PipelineAst" { | |
$item = $Statement.PipeLineElements | |
} | |
"System.Management.Automation.Language.CommandExpressionAst" { | |
$item = $Statement.Expression | |
} | |
"System.Management.Automation.Language.MemberExpressionAst" { | |
# Get Member | |
$value = Expand-AstVariables -Statement $Statement.Expression -Result $result -Properties $Properties | |
$member = Expand-AstVariables -Statement $Statement.Member -Result $result -Properties $Properties | |
if ($result -eq $false) { | |
return | |
} | |
return $value.$($member) | |
} | |
"System.Management.Automation.Language.VariableExpressionAst" { | |
return $Properties.$($Statement.VariablePath.UserPath) | |
} | |
"System.Management.Automation.Language.StringConstantExpressionAst" { | |
return $Statement.Value | |
} | |
"System.Management.Automation.Language.InvokeMemberExpressionAst" { | |
$expression = Expand-AstVariables -Statement $Statement.Expression -Result $result -Properties $Properties | |
$arguments = $Statement.Arguments | Expand-AstVariables -Result $result -Properties $Properties | |
$member = Expand-AstVariables -Statement $Statement.Member -Result $result -Properties $Properties | |
# Before we process we should try to fail fast, and only enable the functions we want to use | |
# Theres no sense trying to resolve a function if its not allowed | |
$isAllowListedAction = "SelectSingleNode", "SelectNodes" -contains $Statement.Member.Value | |
if (-not $isAllowListedAction) { | |
$result.Value = $false | |
return | |
} | |
# Validate allowed types for our AllowList | |
$isAllowedValueType = "System.Xml.XmlElement" -contains $expression.GetType().FullName | |
if (-not $isAllowedValueType) { | |
$result.Value = $false | |
return | |
} | |
if ($result -eq $false) { | |
return | |
} | |
# Cleanup arguments a little bit | |
$arguments = $arguments | ForEach-Object { | |
$_.TrimStart('"').TrimEnd('"') | |
} | |
try { | |
($expression.$Member).Invoke($arguments) | |
} | |
catch { | |
$result.value = $false | |
} | |
return | |
} | |
default { | |
$result.Value = $false | |
} | |
} | |
if ($false -eq $result.Value) { | |
return | |
} | |
$item | ForEach-Object { Expand-AstVariables -Statement $_ -Result $result -Properties $Properties } | |
} | |
end { | |
} | |
} | |
function Expand-StringVariables | |
{ | |
[CmdletBinding()] | |
param( | |
[Parameter(Mandatory, ValueFromPipeline)] | |
[string]$String, | |
[Parameter(Mandatory)] | |
[PSObject] $Properties | |
) | |
begin { | |
$scanStringMethod = [System.Management.Automation.Language.Parser].GetMethod('ScanString', ('Static','NonPublic')) | |
if($scanStringMethod -isnot [System.Reflection.MethodInfo]) { | |
throw 'Unable to hook parser' | |
} | |
} | |
process { | |
$parsedString = $scanStringMethod.Invoke($null, $String) -as [System.Management.Automation.Language.ExpandableStringExpressionAst] | |
if (($null -eq $parsedString) -or (-not $parsedString)){ | |
return $String | |
} | |
$result = $true | |
$value = Expand-AstVariables -Statement $parsedString -Result ([ref]$result) -Properties $Properties | |
if ($result) | |
{ | |
return $value | |
} | |
return $string | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment