Home > Software design >  Are these alternatives to Invoke-Expression really any safer? And why?
Are these alternatives to Invoke-Expression really any safer? And why?

Time:10-06

I'm not understanding if Invoke-Expression is internally flawed, making it more dangerous. Or is the problem that it incorporates text to code, code to execution, and maybe execution in the local scope, all in a single command.

What I'm wanting to do is create a class in C# with a public event EventHandler MyEvent; event via Add-Type, and then inherit from that class in PowerShell by writing the PowerShell in a string @'class psMessages: csMessages{<code for clas>}'@, converting the string into a script block, and then executing it.

I found these methods for creating the script block will work:

$ScriptBlock = ([System.Management.Automation.Language.Parser]::ParseInput($psMessages, [ref]$null, [ref]$null)).GetScriptBlock()
# or
$ScriptBlock = [scriptblock]::Create($psMessages)

And either of these commands will execute the the script block in the current scope:

. $ScriptBlock
# or
Invoke-Command -NoNewScope $ScriptBlock

Additional info: These commands fail, I believe because they execute the script block in a new scope - please correct me if I'm wrong:

& $ScriptBlock
# or
$ScriptBlock.Invoke()
# or
Invoke-Command $ScriptBlock

So, are any of these methods safer to use than Invoke-Expression? Or are they all just as dangerous? And, if any are safer, why?

CodePudding user response:

  • What makes any command dangerous is the blind execution of source code from an unknown / untrusted source.

    • As such, the execution mechanism is incidental to the problem.
  • Conversely, this means that if you full control or implicitly trust a given piece of source code, use of Invoke-Expression - which is generally to be avoided - is acceptable.

    • Note that the code executed by Invoke-Expression invariably runs in the current scope; you could wrap the input string in & { ... } in order to execute it in a child scope.
  • As Santiago Squarzon points out, [scriptblock]::Create() enables a middle ground:

    • As demonstrated in this answer of his, it is possible to constrain what may be executed in terms of permissible commands, read access to specific variables, and whether read access to environment variables is allowed.

    • Additionally, a script block instance returned by [scriptblock]::Create() allows potentially reusable invocation on demand, with the choice to either execute it in the current scope, with ., the dot-sourcing operator, or a child scope, with &, the call operator.


As for the commands listed under "Additional info:"

  • They should not fail; they should all execute the script block in a child scope.

  • However:

    • Using the .Invoke() method on script blocks should be avoided, because it changes the semantics of the call in several respects - see this answer.

    • Similarly, there is no good reason to use Invoke-Command for (local) invocation of script blocks - see this answer.

  • Related