Skip to content

Commit 9b2285c

Browse files
author
James Brundage
committed
ValidateScriptBlock: Adding -AstCondition, -ExcludeType, -IncludeType (Fixes #225, Fixes #226)
1 parent b496f94 commit 9b2285c

File tree

1 file changed

+159
-1
lines changed

1 file changed

+159
-1
lines changed

Transpilers/Parameters/ValidateScriptBlock.psx.ps1

Lines changed: 159 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,48 @@ $NoBlock,
5757
[switch]
5858
$NoParameter,
5959

60+
# If set, will ensure that the script block contains types in this list.
61+
# Passing -IncludeType without -ExcludeType will make -ExcludeType default to *.
62+
[ValidateScript({
63+
$validTypeList = [System.String],[System.String[]],[System.Text.RegularExpressions.Regex],[System.Type],[System.Type[]]
64+
$thisType = $_.GetType()
65+
$IsTypeOk =
66+
$(@( foreach ($validType in $validTypeList) {
67+
if ($_ -as $validType) {
68+
$true;break
69+
}
70+
}))
71+
if (-not $isTypeOk) {
72+
throw "Unexpected type '$(@($thisType)[0])'. Must be 'string','string[]','regex','type','type[]'."
73+
}
74+
return $true
75+
})]
76+
$IncludeType,
77+
78+
# If set, will ensure that the script block does not use the types in this list.
79+
# Passing -IncludeType without -ExcludeType will make -ExcludeType default to *.
80+
[ValidateScript({
81+
$validTypeList = [System.String],[System.String[]],[System.Text.RegularExpressions.Regex],[System.Type],[System.Type[]]
82+
$thisType = $_.GetType()
83+
$IsTypeOk =
84+
$(@( foreach ($validType in $validTypeList) {
85+
if ($_ -as $validType) {
86+
$true;break
87+
}
88+
}))
89+
if (-not $isTypeOk) {
90+
throw "Unexpected type '$(@($thisType)[0])'. Must be 'string','string[]','regex','type','type[]'."
91+
}
92+
return $true
93+
})]
94+
$ExcludeType,
95+
96+
# One or more AST conditions to validate.
97+
# If no results are found or the condition throws, the script block will be considered invalid.
98+
[Alias('AstConditions', 'IfAst')]
99+
[Scriptblock[]]
100+
$AstCondition,
101+
60102
# A VariableExpression. If provided, the Validation attributes will apply to this variable.
61103
[Parameter(Mandatory,ValueFromPipeline,ParameterSetName='VariableExpressionAST')]
62104
[Management.Automation.Language.VariableExpressionAST]
@@ -118,7 +160,123 @@ $validateScripts = @(
118160
return $true
119161
})]
120162
'@
121-
}
163+
}
164+
165+
# If -IncludeType or -ExcludeType were provided
166+
if ($IncludeType -or $ExcludeType) {
167+
if (-not $ExcludeType) {
168+
$ExcludeType = '*'
169+
}
170+
171+
if (-not $IncludeType -and $ExcludeType -eq '*') {
172+
$AstCondition += {
173+
param($ast)
174+
if ($ast -is [Management.Automation.Language.TypeExpressionAst]) {
175+
throw "AST cannot contain types"
176+
}
177+
return $true}
178+
}
179+
else {
180+
$AstCondition += [ScriptBlock]::Create(@"
181+
param(`$ast)
182+
`$included = $(
183+
if (-not $IncludeType) { '$null' }
184+
@($(foreach ($incT in $IncludeType) {
185+
if ($incT -is [string]) {
186+
"'$($incT -replace "'","''")'"
187+
}
188+
elseif ($incT -is [type]) {
189+
"[$($incT.FullName -replace '^System\.')]"
190+
}
191+
elseif ($incT -is [regex]) {
192+
"[Regex]::new('$($incT.ToString().Replace("'","''"))','$($incT.Options)','$($incT.MatchTimeout)')"
193+
}
194+
})) -join ',')
195+
`$excluded = $(@(
196+
if (-not $ExcludeType) { '$null' }
197+
$(foreach ($excT in $ExcludeType) {
198+
if ($excT -is [string]) {
199+
"'$($excT -replace "'","''")'"
200+
}
201+
elseif ($excT -is [type]) {
202+
"[$($excT.FullName -replace '^System\.')]"
203+
}
204+
elseif ($excT -is [regex]) {
205+
"[Regex]::new('$($excT.ToString().Replace("'","''"))','$($excT.Options)','$($excT.MatchTimeout)')"
206+
}
207+
})) -join ',')
208+
if (`$ast -is [Management.Automation.Language.TypeExpressionAst]) {
209+
$(if ($IncludeType) {
210+
{
211+
foreach ($inc in $included) {
212+
if ($inc -is [string] -and $ast.TypeName -like $inc) {
213+
return $true
214+
}
215+
elseif ($inc -is [Regex] -and $ast.TypeName -match $inc) {
216+
return $true
217+
}
218+
elseif ($inc -is [type]){
219+
$reflectionType = $ast.TypeName.GetReflectionType()
220+
if ($inc -eq $reflectionType) { return $true}
221+
if ($inc.IsSubclassOf($reflectionType) -or $reflectionType.IsSubclassOf($inc)) {
222+
return $true
223+
}
224+
if ($inc.IsInterface -and $reflectionType.getInterFace($inc)) {
225+
return $true
226+
}
227+
if ($reflectionType.IsInterface -and $inc.getInterFace($reflectionType)) {
228+
return $true
229+
}
230+
}
231+
}
232+
}})
233+
$({
234+
$throwMessage = "[$($ast.Typename)] is not allowed"
235+
foreach ($exc in $excluded) {
236+
if ($exc -is [string] -and $ast.TypeName -like $exc) {
237+
throw $throwMessage
238+
}
239+
elseif ($exc -is [regex] -and $ast.TypeName -match $exc) {
240+
throw $throwMessage
241+
}
242+
elseif ($exc -is [type]) {
243+
$reflectionType = $ast.TypeName.GetReflectionType()
244+
if ($ecx -eq $reflectionType) {
245+
throw $throwMessage
246+
}
247+
elseif ($exc.IsSubclassOf($reflectionType) -or $reflectionType.IsSubclassOf($exc)) {
248+
throw $throwMessage
249+
}
250+
elseif ($exc.IsInterface -and $reflectionType.getInterFace($exc)) {
251+
throw $throwMessage
252+
}
253+
elseif ($reflectionType.IsInterface -and $exc.getInterFace($reflectionType)) {
254+
throw $throwMessage
255+
}
256+
}
257+
}
258+
})
259+
260+
}
261+
return `$true
262+
"@)
263+
}
264+
}
265+
266+
if ($AstCondition) {
267+
@"
268+
[ValidateScript({
269+
if (`$_ -isnot [ScriptBlock]) { return `$true }
270+
`$astConditions = {$($AstCondition -join '} , {')}
271+
`$scriptBlockAst = `$_.Ast
272+
foreach (`$astCondition in `$astConditions) {
273+
`$foundResults = `$scriptBlockAst.FindAll(`$astCondition, `$true)
274+
if (-not `$foundResults) { return `$false}
275+
}
276+
return `$true
277+
})]
278+
"@
279+
}
122280
)
123281
if (-not $validateScripts) { return }
124282

0 commit comments

Comments
 (0)