@@ -266,6 +266,46 @@ function next($er) {
266266 Write-Host $er - ForegroundColor DarkRed
267267}
268268
269+ function Invoke-SanitizeScriptBlock {
270+ param (
271+ [Parameter (Mandatory = $true )]
272+ [string ] $ScriptBlock
273+ )
274+
275+ $tokens = $null
276+ $errors = $null
277+ $ast = [System.Management.Automation.Language.Parser ]::ParseInput($ScriptBlock , [ref ]$tokens , [ref ]$errors )
278+
279+ if ($errors.Count -gt 0 -or -not $ast ) {
280+ $message = if ($errors ) { $errors [0 ].Message } else { ' unknown parse error' }
281+ return $true , $message
282+ }
283+
284+ # Restrict the capability of accessing outer variables via `$using:`
285+ if ($ast.Find ({ param ($node ) $node -is [System.Management.Automation.Language.UsingExpressionAst ] }, $true )) {
286+ return $true , " '`$ using:' may not be used"
287+ }
288+
289+ return $false , $null
290+ }
291+
292+ function Get-CheckverScriptPrelude () {
293+ return @ (
294+ ' $ErrorActionPreference = "Stop"' ,
295+ ' $ProgressPreference = "SilentlyContinue"' ,
296+ # Set allowed env vars, `SystemRoot` is required for irm/iwr
297+ ' $allowedEnvs = @("SystemRoot","TEMP","TMP")' ,
298+ ' Get-ChildItem Env: | Where-Object { $allowedEnvs -notcontains $_.Name } | Remove-Item' ,
299+ # Use `UseBasicParsing` for Invoke-WebRequest by default (WindowsPowerShell)
300+ ' $PSDefaultParameterValues["Invoke-WebRequest:UseBasicParsing"] = $true' ,
301+ # Use ConstrainedLanguage language mode to restrict checkver.script capabilities
302+ ' $ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"' ,
303+ # Available variables/functions for checkver.script
304+ # - `$page`: the retrieved webpage content
305+ ' $page = $using:page'
306+ )
307+ }
308+
269309# wait for all to complete
270310while ($in_progress -gt 0 ) {
271311 $ev = Wait-Event
@@ -318,12 +358,26 @@ while ($in_progress -gt 0) {
318358 $page = (New-Object System.IO.StreamReader($ms , (Get-Encoding $wc ))).ReadToEnd()
319359 }
320360
361+ # checkver.script evaluation
321362 if ($script ) {
322- $page = Invoke-Command ([scriptblock ]::Create($script -join " `r`n " ))
323- $source = ' the output of script'
363+ $script = @ ($script ) -join " `n "
364+ $isBadScript , $errorMessage = Invoke-SanitizeScriptBlock $script
365+ if ($isBadScript ) {
366+ next " invalid checkver.script: $errorMessage "
367+ continue
368+ }
369+
370+ $prelude = Get-CheckverScriptPrelude
371+
372+ $script = $prelude + $script
373+ $scriptBlock = [scriptblock ]::Create($script -join " `n " )
374+
375+ $page = (Start-Job - ScriptBlock $scriptBlock | Receive-Job - Wait - AutoRemoveJob | Out-String ).Trim()
376+ Write-Verbose " Output from evaluating checkver.script:`r`n $page "
377+ $source = ' the output of checkver.script'
324378 }
325379
326- if ($null -eq $page ) {
380+ if (! $page ) {
327381 next " couldn't retrieve content from $source "
328382 continue
329383 }
0 commit comments