2

With writing custom PSSA rules and just parsing PowerShell scripts, I find myself often in the same use case where I would like to resolve command name (which is not that difficult) and the parameters of a CommandAst.
For my current particular case, I would like the write a rule to Avoid New-Object Cmdlet. To prevent any false positives, I would like to exclude any command with New-Object -ComObject parameters, and to write an automated -Fix I would like to retrieve parameters along with the -TypeName parameter. The "problem" is that the parameters could be provided several ways: named, unnamed (by position) or elastic (e.g. -Type) or an alias as -Args.
I presume that the PowerShell engine somehow needs to deal with the same command line resolving and I wonder whether this could be invoked from PowerShell itself.

In other words, I have the following command line:

New-Object -TypeName System.Version -ArgumentList "1.2.3.4"

which I can parse with the Abstract Syntax Tree (AST) class.
To resolve the command name (regardsless I might use an alias), I might do this:

$Ast = [System.Management.Automation.Language.Parser]::ParseInput(
    { New-Object -TypeName System.Version -ArgumentList "1.2.3.4" },
    [ref]$null, [ref]$null
)
$CommandAst = $Ast.Endblock.Statements.PipelineElements
$CommandAst.GetCommandName()
New-Object

But how do I (easily) resolve the parameters (regardless they are provided named, unnamed, elastic or via an alias)?

0

1 Answer 1

2

You can use StaticParameterBinder.BindCommand, this of course works as long as the command exist / can be resolved:

using namespace System.Management.Automation.Language

$command = { New-Object System.Version '1.2.3.4' }
$commandAst = $command.Ast.Find({ $args[0] -is [CommandAst] }, $false)
$bindingResult = [StaticParameterBinder]::BindCommand($commandAst)

foreach ($result in $bindingResult.BoundParameters.GetEnumerator()) {
    [pscustomobject]@{
        Parameter = $result.Key
        Argument  = $result.Value.Value.SafeGetValue()
    }
}

There is a caveat with .SafeGetValue(), if the dynamic expression cannot be resolved, you will get an error, so for example if your command was:

$command = {
    $foo = '1.2.3.4'
    New-Object System.Version $foo
}

Then, it would fail, if you want to resolve the value of $foo, in PowerShell 7+ you can use .SafeGetvalue($true), however this overload doesn't exist in in older versions, there you will require a more manual approach, an example here.

In this case the output you'd get from the above would be:

Parameter    Argument
---------    --------
TypeName     System.Version
ArgumentList 1.2.3.4

If however you want to see the argument as-is, without resolving it, then you can use .ToString() instead of .SafeGetValue(), in which case the output would be:

Parameter    Argument
---------    --------
TypeName     System.Version
ArgumentList $foo
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.