1

I have the following scenario, running on Powershell v5:

A Powershell script pulls several bits of information from an API call to a 3rd party system in a bulk for-each loop and assigns them to Variables. Amongst the information that is pulled is Passwords (this is being done to get rid of said 3rd party system and to migrate it to something that doesn't allow you to retrieve passwords in plain text):

$userset = Invoke-WebRequest -Method Post -Uri "https://$Url/path/to/api.asmx" -Headers $Headers -Body $usercall

$xmluserset = [xml] $userset.Content

$userset2 = $xmluserset.Envelope.Body.UserSettingsResult.settingValues.string

$userpasstemp = $userset2[1].trimstart("password")
$userpass = $userpasstemp.trimstart("=")

These passwords are then used elsewhere in the Script.

For example, they are passed to a different API and need to be in a URL compatible format and so I run this:

$urlescapeduserpass = [uri]::EscapeDataString($userpass)

which works for that section of the Script

The problem is that these passwords can contain any of the special characters: !"#$%&'()*+,-./:;<=>?@[]^_`{|}~

And when I call another part of the script, the special characters in the password string cause a failure and the script to exit. This occurs when using either the call command:

& .\application.exe --option1 $option1 --user1 $user --password1 $userpass

or when using invoke-expression

$command = "$path\application.exe  --option1 $option1 --user1 $user --password1  $userpass"
Invoke-Expression $command

I've tried using Regex, using the -replace cmdlet:

$escapedpass = $userpass -replace ' !"#$%&()*+,-./:;<=>?@[\]^_`{|}~', '`$&'

But no luck, I know similar to the [uri]escapedatastring, there's a similar one for Regex, but there doesn't appear to be one native for Powershell. I'm sure there is either a [contenttype] that will have a native function to escape the special characters or some way to achieve the end-result.

TheDemonLord
  • 327
  • 1
  • 2
  • 15
  • 1
    Does this answer your question? [How to escape special characters in PowerShell?](https://stackoverflow.com/a/57966347/1701026) – iRon Mar 18 '20 at 07:01
  • Nope. I referenced the 2 solutions proposed in that answer - using the -replace argument and switching from invoke-expression to call command. Neither of which worked. – TheDemonLord Mar 18 '20 at 09:08
  • 3
    The regex pattern should be `'[ !"#$%&()*+,-./:;<=>?@[\]^_{|}~]'`. You want to replace each character out of this group. The pattern that you used would instead replace this exact **sequence** of characters... And it wouldn't work because of the special meaning of some of these characters in regex. – Manuel Batsching Mar 18 '20 at 09:20
  • As an aside: [`Invoke-Expression` should generally be avoided](https://blogs.msdn.microsoft.com/powershell/2011/06/03/invoke-expression-considered-harmful/); definitely [don't use it to invoke an external program](https://stackoverflow.com/a/57966347/45375). – mklement0 Mar 18 '20 at 14:27
  • @iRon, the linked post only partially applies: it recommends avoiding `Invoke-Expression`, because it is _its use_ that introduces escaping headaches. With direct invocation - which is always the preferable method - you generally need _no_ escaping - except, sadly, due to PowerShell's broken handling of `"` characters embedded in arguments passed to _external executables_. – mklement0 Mar 18 '20 at 14:33

2 Answers2

2

Because PowerShell's handling of embedded " characters in argument passed to external programs is broken (as of PowerShell 7) - see this answer - you need to manually \-escape " characters embedded in your string:

$escapedpass = $userpass -replace , '"', '\"'

In the context of your command:

& .\application.exe --option1 $option1 --user1 $user --password1 ($userpass -replace , '"', '\"')
mklement0
  • 382,024
  • 64
  • 607
  • 775
  • 1
    I thought it was the powershell specific characters like $ that was causing my pain - I've just done some testing and can confirm that this was the problem. I've marked this as the solution, since it solved my actual problem, but the other comment against my question with the correct Regex sequence I've also upvoted, since that is the answer to the question I asked. Thanks heaps. – TheDemonLord Mar 18 '20 at 20:52
  • Glad to hear it was helpful, @TheDemonLord. Yes, Manuel's comment was helpful, and it's always worth (also) up-voting answers and comments that are helpful (too). – mklement0 Mar 18 '20 at 20:57
0

Let's create a Minimal, Reproducible Example with a round trip based on the answer How to escape special characters in PowerShell? from @mklement0 for this:
(Also take the comment in account that I just made on spaces)

Unfortunately, PowerShell creates an extra handicap as it requires to single quote the string otherwise it will interpret other characters along with the dollar sign ($). I have placed comments in the code where you might leave this out.

Password.ps1

Param (
    [String]$Password
)
Write-Host $Password

Password check:

$Input = @'
 !"'#$%&()*+,-./:;<=>?@[\]^_`{|}~
'@

 

Write-Host 'Input:' $Input
$Escape = $Input.Replace('"', '\"')
$Escape = $Escape.Replace("'", "''") # Only required for PowerShell
Write-Host 'Command:' PowerShell .\Password.ps1 "'$Escape'"
# Omit the inner single quotes if it doesn't concern PowerShell
# But note that your .\application.exe might have its own syntax
# to support spaces and special characters from the command line,
# like double quotes: """$Escape"""
$Output = & PowerShell .\Password.ps1 "'$Escape'"
Write-Host 'Output:' $Output
Write-Host 'Input and output are equal?' ($Input -eq $Output)

Results:

Input:  !"'#$%&()*+,-./:;<=>?@[\]^_`{|}~
Command: PowerShell .\Password.ps1 "' !\"''#$%&()*+,-./:;<=>?@[\]^_`{|}~'"
Output:  !"'#$%&()*+,-./:;<=>?@[\]^_`{|}~
Input and output are equal? True
iRon
  • 20,463
  • 10
  • 53
  • 79