Proxy functions generated by Crescendo currently do not add quotes around empty strings, if such value is provided for a string parameter. This leads to broken command lines, causing either syntax errors or parameter mismatches.
Context
Consider the "install" command of the Chocolatey command line executable (choco.exe).
The command, like other choco commands, accepts both named and unnamed parameters. Unnamed parameters are package names to install and at least one must be provided. Named parameters can be switches (which do not accept a value, such as --force) or regular parameters, with the value separated from the parameter name either by whitespace (e.g. --timeout 3600) or by the equals sign (e.g. --timeout=3600). For some of the latter parameters, an empty string is a valid value, for example --package-parameters "" (typed in cmd.exe) would mean that no special parameters should be passed to the package install script, same as if the --package-parameters parameter was omitted.
Reproduction
Given this simple Crescendo proxy definition:
$c = New-CrescendoCommand -Verb Test -Noun PassingParametersToChoco
$c.OriginalName = 'choco'
$c.OriginalCommandElements = @('install', '--verbose', '--debug')
$c.Parameters = @(@{ Name = 'Name'; ParameterType = 'string'; OriginalPosition = 1 }, @{ Name = 'PackageParameters'; ParameterType = 'string'; OriginalName = '--package-parameters'; OriginalPosition = 2 }, @{ Name = 'Yes'; ParameterType = 'switch'; OriginalName = '--yes'; OriginalPosition = 3 })
@{ Commands = @($c) } | ConvertTo-Json -Depth 100 | Out-File .\Test-PassingParametersToChoco.def.json
Export-CrescendoModule -ConfigurationFile .\*.def.json -ModuleName .\TestPassingParameters.psm1 -Force
(I set OriginalPosition, even though choco.exe puts no restrictions on parameter order, so that the tests are deterministic.)
I can now invoke Test-PassingParametersToChoco with various parameter combinations and observe the actual command line passed to choco.exe (it is printed by choco.exe thanks to the --debug switch and it can also be seen in Sysinternals Process Monitor). (To avoid making changes to my system, I use a non-elevated PowerShell console; choco.exe will detect the lack of admin rights and ask interactively for permission to continue, which is why it is required to type "y " after invoking Test-PassingParametersToChoco in order to see the output from choco.exe. Alternatively, the command line can be inspected in Process Monitor and the invocation terminated with Ctrl+C.)
- Passing only package name
PowerShell statement:
Test-PassingParametersToChoco -Name cpu-z
choco.exe command line in Process Monitor:
"C:\ProgramData\chocolatey\choco.exe" install --verbose --debug cpu-z
Relevant choco.exe diagnostic output:
Command line: "C:\ProgramData\chocolatey\choco.exe" install --verbose --debug cpu-z
(...)
SkipPackageInstallProvider='False'|PackageNames='cpu-z'|
- Passing empty package parameters
PowerShell statement:
Test-PassingParametersToChoco -Name cpu-z -PackageParameters ''
choco.exe command line in Process Monitor:
"C:\ProgramData\chocolatey\choco.exe" install --verbose --debug cpu-z --package-parameters
Relevant choco.exe diagnostic output:
Command line: "C:\ProgramData\chocolatey\choco.exe" install --verbose --debug cpu-z --package-parameters
(...)
Install Command
Installs a package or a list of packages (sometimes specified as a
(rest of help text for the install command)
(...)
System.ApplicationException: Package name is required. Please pass at least one package name to install.
The argument parser in choco.exe signals an error, because there is no value specified for the "--package-parameters" parameter. Due to unfortunate ordering of code inside choco.exe, a misleading error message about missing package name is printed. It can be noted that because parameter validation failed there was no need to respond to the "continue without admin rights?" prompt.
- Passing empty package parameters and an additional switch
PowerShell statement:
Test-PassingParametersToChoco -Name cpu-z -PackageParameters '' -Yes
choco.exe command line in Process Monitor:
"C:\ProgramData\chocolatey\choco.exe" install --verbose --debug cpu-z --package-parameters --yes
Relevant choco.exe diagnostic output:
Command line: "C:\ProgramData\chocolatey\choco.exe" install --verbose --debug cpu-z --package-parameters --yes
(...)
SkipPackageInstallProvider='False'|PackageNames='cpu-z'|
(...)
NotSilent='False'|PackageParameters='--yes'|
In this case, because the --package-parameters parameter with empty value was followed by a switch (--yes), the command line passed to choco.exe resulted in "--yes" being interpreted as the value of the --package-parameters parameter.
Additional experiments
- Passing a package parameter (without spaces)
PowerShell statement:
Test-PassingParametersToChoco -Name cpu-z -PackageParameters 'foo'
choco.exe command line in Process Monitor:
"C:\ProgramData\chocolatey\choco.exe" install --verbose --debug cpu-z --package-parameters foo
Relevant choco.exe diagnostic output:
Command line: "C:\ProgramData\chocolatey\choco.exe" install --verbose --debug cpu-z --package-parameters foo
(...)
SkipPackageInstallProvider='False'|PackageNames='cpu-z'|
(...)
NotSilent='False'|PackageParameters='foo'|
The --package-parameters value was passed properly.
- Passing some package parameters with a space
PowerShell statement:
Test-PassingParametersToChoco -Name cpu-z -PackageParameters 'foo bar'
choco.exe command line in Process Monitor:
"C:\ProgramData\chocolatey\choco.exe" install --verbose --debug cpu-z --package-parameters "foo bar"
Relevant choco.exe diagnostic output:
Command line: "C:\ProgramData\chocolatey\choco.exe" install --verbose --debug cpu-z --package-parameters "foo bar"
(...)
SkipPackageInstallProvider='False'|PackageNames='cpu-z'|
(...)
NotSilent='False'|PackageParameters='foo bar'|
The --package-parameters value was automatically and correctly quoted.
- Passing package parameters consisting of a space only
PowerShell statement:
Test-PassingParametersToChoco -Name cpu-z -PackageParameters ' '
choco.exe command line in Process Monitor:
"C:\ProgramData\chocolatey\choco.exe" install --verbose --debug cpu-z --package-parameters " "
Relevant choco.exe diagnostic output:
Command line: "C:\ProgramData\chocolatey\choco.exe" install --verbose --debug cpu-z --package-parameters " "
(...)
SkipPackageInstallProvider='False'|PackageNames='cpu-z'|
(...)
(no debug line with PackageParameters value)
The --package-parameters value was automatically and correctly quoted. In this case choco.exe recognized the whitespace-only value and treated it as if the --package-parameters parameter was not passed.
- Without Crescendo
These direct invocations of choco.exe behave incorrectly, in a similar manner (the empty string just disappears), if run from PowerShell:
choco install --verbose --debug cpu-z --package-parameters ''
choco install --verbose --debug cpu-z --package-parameters ([string]::Empty)
$chocoargs = @('install', '--verbose', '--debug', 'cpu-z', '--package-parameters', ''); choco @chocoargs
choco install --verbose --debug cpu-z --package-parameters ""
choco install --verbose --debug cpu-z --package-parameters "" --yes
which means that it is PowerShell itself which fails to quote those empty strings.
On the other hand, the last two lines do work correctly from cmd.exe. Example:
cmd.exe statement:
choco install --verbose --debug cpu-z --package-parameters ""
choco.exe command line in Process Monitor:
"C:\ProgramData\chocolatey\choco.exe" install --verbose --debug cpu-z --package-parameters ""
Relevant choco.exe diagnostic output:
Command line: "C:\ProgramData\chocolatey\choco.exe" install --verbose --debug cpu-z --package-parameters ""
(...)
SkipPackageInstallProvider='False'|PackageNames='cpu-z'|
(...)
(no debug line with PackageParameters value)
Conclusion
I believe the proxies generated by Crescendo should recognize empty strings provided as string parameter values and ensure those empty strings are passed as "" to the invoked executable. This would be consistent with how parameters with spaces are handled and would fix the currently broken behavior when the caller provides an empty string to the proxy.
Proxy functions generated by Crescendo currently do not add quotes around empty strings, if such value is provided for a string parameter. This leads to broken command lines, causing either syntax errors or parameter mismatches.
Context
Consider the "install" command of the Chocolatey command line executable (choco.exe).
The command, like other choco commands, accepts both named and unnamed parameters. Unnamed parameters are package names to install and at least one must be provided. Named parameters can be switches (which do not accept a value, such as
--force) or regular parameters, with the value separated from the parameter name either by whitespace (e.g.--timeout 3600) or by the equals sign (e.g.--timeout=3600). For some of the latter parameters, an empty string is a valid value, for example--package-parameters ""(typed in cmd.exe) would mean that no special parameters should be passed to the package install script, same as if the--package-parametersparameter was omitted.Reproduction
Given this simple Crescendo proxy definition:
(I set OriginalPosition, even though choco.exe puts no restrictions on parameter order, so that the tests are deterministic.)
I can now invoke Test-PassingParametersToChoco with various parameter combinations and observe the actual command line passed to choco.exe (it is printed by choco.exe thanks to the
--debugswitch and it can also be seen in Sysinternals Process Monitor). (To avoid making changes to my system, I use a non-elevated PowerShell console; choco.exe will detect the lack of admin rights and ask interactively for permission to continue, which is why it is required to type "y " after invoking Test-PassingParametersToChoco in order to see the output from choco.exe. Alternatively, the command line can be inspected in Process Monitor and the invocation terminated with Ctrl+C.)PowerShell statement:
Test-PassingParametersToChoco -Name cpu-zchoco.exe command line in Process Monitor:
"C:\ProgramData\chocolatey\choco.exe" install --verbose --debug cpu-zRelevant choco.exe diagnostic output:
PowerShell statement:
Test-PassingParametersToChoco -Name cpu-z -PackageParameters ''choco.exe command line in Process Monitor:
"C:\ProgramData\chocolatey\choco.exe" install --verbose --debug cpu-z --package-parametersRelevant choco.exe diagnostic output:
The argument parser in choco.exe signals an error, because there is no value specified for the "--package-parameters" parameter. Due to unfortunate ordering of code inside choco.exe, a misleading error message about missing package name is printed. It can be noted that because parameter validation failed there was no need to respond to the "continue without admin rights?" prompt.
PowerShell statement:
Test-PassingParametersToChoco -Name cpu-z -PackageParameters '' -Yeschoco.exe command line in Process Monitor:
"C:\ProgramData\chocolatey\choco.exe" install --verbose --debug cpu-z --package-parameters --yesRelevant choco.exe diagnostic output:
In this case, because the --package-parameters parameter with empty value was followed by a switch (--yes), the command line passed to choco.exe resulted in "--yes" being interpreted as the value of the --package-parameters parameter.
Additional experiments
PowerShell statement:
Test-PassingParametersToChoco -Name cpu-z -PackageParameters 'foo'choco.exe command line in Process Monitor:
"C:\ProgramData\chocolatey\choco.exe" install --verbose --debug cpu-z --package-parameters fooRelevant choco.exe diagnostic output:
The --package-parameters value was passed properly.
PowerShell statement:
Test-PassingParametersToChoco -Name cpu-z -PackageParameters 'foo bar'choco.exe command line in Process Monitor:
"C:\ProgramData\chocolatey\choco.exe" install --verbose --debug cpu-z --package-parameters "foo bar"Relevant choco.exe diagnostic output:
The --package-parameters value was automatically and correctly quoted.
PowerShell statement:
Test-PassingParametersToChoco -Name cpu-z -PackageParameters ' 'choco.exe command line in Process Monitor:
"C:\ProgramData\chocolatey\choco.exe" install --verbose --debug cpu-z --package-parameters " "Relevant choco.exe diagnostic output:
The --package-parameters value was automatically and correctly quoted. In this case choco.exe recognized the whitespace-only value and treated it as if the --package-parameters parameter was not passed.
These direct invocations of choco.exe behave incorrectly, in a similar manner (the empty string just disappears), if run from PowerShell:
choco install --verbose --debug cpu-z --package-parameters ''choco install --verbose --debug cpu-z --package-parameters ([string]::Empty)$chocoargs = @('install', '--verbose', '--debug', 'cpu-z', '--package-parameters', ''); choco @chocoargschoco install --verbose --debug cpu-z --package-parameters ""choco install --verbose --debug cpu-z --package-parameters "" --yeswhich means that it is PowerShell itself which fails to quote those empty strings.
On the other hand, the last two lines do work correctly from cmd.exe. Example:
cmd.exe statement:
choco install --verbose --debug cpu-z --package-parameters ""choco.exe command line in Process Monitor:
"C:\ProgramData\chocolatey\choco.exe" install --verbose --debug cpu-z --package-parameters ""Relevant choco.exe diagnostic output:
Conclusion
I believe the proxies generated by Crescendo should recognize empty strings provided as string parameter values and ensure those empty strings are passed as
""to the invoked executable. This would be consistent with how parameters with spaces are handled and would fix the currently broken behavior when the caller provides an empty string to the proxy.