Skip to content

New Rule: Calling Start-Process without checking ExitCode #1062

Description

Summary of the new feature
I recently got burned by a library not handling the exit code for Microsoft.PowerShell.Management\Start-Process.

This could be broken into two rules:

  • Always assign Start-Process to a local variable, and check the result. Caveat: How would this work if invoked from Start-Job and deliberately intended to be asynchronous?

Proposed technical implementation details (optional)

  1. Find calls to Microsoft.PowerShell.Management\Start-Process
    There are several ways this could be called:

Arguments not known at caller. No local way to detect $args sets the -Wait and -NoNewWindow switches on

function Do
{
param(
 [hashtable]$StartProcess_params
)
  Microsoft.PowerShell.Management\Start-Process @StartProcess_params
}

Trivial case: Direct arguments

Microsoft.PowerShell.Management\Start-Process -FilePath "cmd.exe" -ArgumentList "/C" -Wait -NoNewWindow

Middle case: arguments constructed in same scope - I believe this is called $script scope

$StartProcess_params = @{
  FilePath = "cmd.exe"
  ArgumentList = "/C"
  RedirectStandardError = $tempErrorFile
  RedirectStandardOutput = $tempOutputFile
  NoNewWindow = $true
  Wait = $true
}
Microsoft.PowerShell.Management\Start-Process @StartProcess_params

Using Start-Job

Start-Job -Name DoSomething -ScriptBlock {
    & cmd.exe /C
    Write-Output $LASTEXITCODE
}
#Do other stuff here
Get-Job -Name DoSomething | Wait-Job | Receive-Job

A clear and concise description of what you want to happen.
Flag as warnings with suggestion to assign call to a PS variable, e.g.:
Microsoft.PowerShell.Management\Start-Process @StartProcess_params
would become:

$process = Microsoft.PowerShell.Management\Start-Process @StartProcess_params
if (-not $process.ExitCode)
{
  Write-Error $(Microsoft.PowerShell.Management\Get-Content $tempErrorFile
}

Alternatively, if the user truly wishes to suppress the result, the UI could offer a "No, I really don't care and want to explicitly say so" command, which would re-write the code to be:

$(Microsoft.PowerShell.Management\Start-Process @StartProcess_params) | Out-Null

or

[void]Microsoft.PowerShell.Management\Start-Process @StartProcess_params

According to StackOverflow, Out-Null adds a 60% overhead and therefore is slower than [void], so we should probably suggest [void]Microsoft.PowerShell.Management\Start-Process @StartProcess_params

What is the latest version of PSScriptAnalyzer at the point of writing
1.17.1

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions