Here is my sub-expression:
"""$((($l -split " """)[1] -split """ ")[0])"""
I have checked and found no unpaired parentheses. However powershell insists in saying "Missing closing ')' in expression."
Interestingly, the expression
$((($l -split " """)[1] -split """ ")[0])
works fine.
Has anyone had similiar experience before? Is it a bug of Powershell?
This.. is really interesting, and yes I would consider it a bug at least so far.
Here's a much simpler repro:
"$("`"")"
"$("""")"
It seems to be caused by using either form of double quote escaping (backtick or double double quote), inside a sub-expression, inside an expandable string.
It also appears that this is error comes right down to the parser itself:
$sb = #'
"$("`"")"
'#
$tokens = $null
$er = $null
$ast = [System.Management.Automation.Language.Parser]::ParseInput($sb, [ref]$tokens, [ref]$er)
$ast.EndBlock.Statements[0].PipelineElements[0].Expression.NestedExpressions
$tokens[0].NestedTokens
The nested tokens/expressions it finds just aren't correct.
I tested with Windows PowerShell 5.1 and PowerShell Core 6.0.0-rc.2 on WLS.
Relevant Issues
Two double-quotes in inline expressions
String in Sub-Expression incorrectly parsed
Powershell: Doubled double quotes in inline expressions
Related
I'm trying to get the following command into a program I am developing in PowerShell studio, I'm having some issues figuring out the escaping sequence here, and testing takes forever since I have to import the PST just to view the data :(
Consider the following command which works correctly from the shell...
New-MailboxExportRequest -Mailbox user#domain.com -Name JobName -IncludeFolders "#Inbox#/*","#SentItems#" -ContentFilter {(Received -gt "06/10/2022") -and (Sent -gt "06/10/2022")} -FilePath "\\Server\Folder\MyPST.pst"
I am using variables inside program designer, so my actual code looks like this...
New-MailboxExportRequest -Mailbox $mailbox -Name $jobname -IncludeFolders $IncludeFolders -ContentFilter {(Received -gt "$RecDate") -and (Sent -gt "$SentDate")} -FilePath "$FilePath"
Basically I need to get the first code sample working using variables, however the -IncludeFolders and -ContentFilter parameters require some escaping I can't seem to figure out. Any and all help is much appreciated.
tl;dr:
You need to enclose your -ContentFilter argument in "..." overall in order to support embedding variable values, which you can themselves enclose in embedded '...' quoting:
-ContentFilter "(Received -gt '$RecDate') -and (Sent -gt '$SentDate')"
No special syntax considerations apply with respect to using a variable with -IncludeFolders: Just make sure that variable $IncludeFolders contains an array of strings identifying the target folders; applied to your example:
$IncludeFolders = '#Inbox#/*', '#SentItems#'
If the list of folders must be parsed from user input provided as a single string ($textbox5.text in this example) do the following to convert this single string to an array of names:
# If $textbox5.text contains string '#Inbox#/*, #SentItems#',
# the result is the same as above.
$IncludeFolders = ($textbox5.text -split ',').Trim()
Read on for background information and caveats re -ContentFilter.
Note:
The -ContentFilter information below also applies to other Exchange cmdlets that accept filters, such as Get-Recipient with its -Filter parameter.
It also applies analogously to the -Filter parameter used with AD (Active Directory) cmdlets such as Get-ADUser, although the situation there is complicated by the fact that the AD provider does support evaluation of PowerShell variables, but only in simple cases - see this answer.
New-MailboxExportRequest's -ContentFilter expects a string argument, not a script block ({ ... }), which you've used in your question.
While you can situationally get away with a script block, it is best avoided for two reasons:
When a script block is used as a string (converted to one), string interpolation (embedding the values of variables in the string) is not supported - a script block stringifies to its verbatim content (sans { and }).
Conceptually, use of a script block can give the mistaken impression that an arbitrary piece of PowerShell code may be passed, which is not the case: -ContentFilter supports a domain-specific syntax only that only emulates a limited set of PowerShell features, called OPath filter syntax.
Notably, evaluation of PowerShell variables is not supported - their values must be embedded in the string up front, by PowerShell, using string interpolation.
Therefore:
It's best to use a string argument with -ContentFilter to begin with.
Since you do need to embed variable values, you need string interpolation, via an expandable (double-quoted) string ("..."), inside of which you may quote values with '...' for syntactical convenience.
In case there is no need to embed variable values, use a verbatim (single-quoted) string ('...'), inside of which you may quote values with "...", as in your question. (In effect, this is the equivalent of the (ill-advised) use of { ... }).
Applied to your example: The following embeds the (stringified) values of variables $RecDate and $SentDate in your -ContentFilter argument:
# Note the use of "..." for the outer quoting, and
# '...' for the embedded quoting.
-ContentFilter "(Received -gt '$RecDate') -and (Sent -gt '$SentDate')"
Caveats:
If the values of the variables to embed themselves contain ', use escaped embedded "..." quoting instead; e.g., `"$var`"
If a value could contain ' and/or " and you don't know which, you'd have to use an extra layer of up-front string interpolation on the PowerShell side to escape one of them, depending on the embedded quoting character chosen, using $(...), the subexpression operator, using a -replace operation:
In this escaping you must technically satisfy the OPath filter syntax requirements; the linked help OPath topic suggests that, like in PowerShell, '' can be used to escape ' inside a '...' string; e.g.: -ContentFilter "Body -like '$($var -replace "'", "''")*'"
While, as stated, OPath filters do not support evaluating PowerShell variable references in general, the following automatic variables - which are conceptually constants - are recognized (the linked help topic calls them system values):
$true, $false
$null
Therefore, if you use "..." for the outer quoting (for string interpolation), the $ in these variables must be escaped with ` (the so-called backtick, PowerShell's escape character), to prevent PowerShell from replacing these variable references with their values up front:
`$true, `$false
`$null
I have data in an XML file, that will eventually be used as a Registry path, which MAY contain non printing characters (for example when copying the path from a web site into the XML). I want to validate the data and throw a specific error if non printing characters are found.
In Powershell, if I define a variable with non printing characters in single quotes and then test-Path it tests as a valid path as the non printing character is handled as a literal.
Test-Path 'HKEY_LOCAL_MACHINE\SOFTWARE\Test\`n#microsoft.com/GENUINE\#microsoft.com/GENUINE' -isValid
The same thing with double quotes will "expand" the non printing characters and return false, which is what I need.
Test-Path "HKEY_LOCAL_MACHINE\SOFTWARE\Test\`n#microsoft.com/GENUINE\#microsoft.com/GENUINE" -isValid
I have found reference to [string]::Format(() being used to expand the non printing characters, but
$invalidPath = 'HKEY_LOCAL_MACHINE\SOFTWARE\Test\`n#microsoft.com/GENUINE\#microsoft.com/GENUINE'
[string]::Format("{0}",$invalidPath)
does not expand the non printing character as expected.
I have also seen reference to using Invoke-Expression but that is NOT safe, and not an option.
Finally I found $ExecutionContext.InvokeCommand.ExpandString(), which seems to work,
$ExecutionContext.InvokeCommand.ExpandString('HKEY_LOCAL_MACHINE\SOFTWARE\Test\`n#microsoft.com/GENUINE\#microsoft.com/GENUINE')
returns a multiline string to the console, while
$ExecutionContext.InvokeCommand.ExpandString('Write-Host "Screwed"') returns the actual string to the console, rather than actually executing the Write-Host and only returning Screwed to the console.
Finally,
$invalidPath = 'HKEY_LOCAL_MACHINE\SOFTWARE\Test\`n#microsoft.com/GENUINE\#microsoft.com/GENUINE'
Test-Path ($ExecutionContext.InvokeCommand.ExpandString($invalidPath)) -isValid
returns false as expected. Which has me thinking this is the correct approach to pursue, but given all the gotchas elsewhere, I want to be 100% sure there is no way for this approach to be used as a security weak point. Am I on the right track, or are there gotchas my Google-Fu hasn't turned up yet?
Like Invoke-Expression, $ExecutionContext.InvokeCommand.ExpandString() is vulnerable to injection of unwanted commands, except that in the latter case such commands are only recognized if enclosed in $(...), the subexpression operator, as that is the only way to embed commands in expandable strings (which the method's argument is interpreted as).
For instance:
$ExecutionContext.InvokeCommand.ExpandString('a $(Write-Host -Fore Red Injected!) b')
A simple way to prevent this is to categorically treat all embedded $ chars. verbatim, by escaping them with `:
'a $(Write-Host -Fore Red Injected!) b',
'There''s no place like $HOME',
'Too `$(Get-Date) clever by half' |
ForEach-Object {
$ExecutionContext.InvokeCommand.ExpandString(($_ -replace '(`*)\$', '$1$1`$$'))
}
Note: It is sufficient to escape verbatim $ in the input string. A Unicode escape-sequence representation of $ (or ( / )) (`u{24} (or `u{28} / `u{29}), supported in PowerShell (Core) v6+ only), is not a concern, because PowerShell treats the resulting characters verbatim.
Of course, you may choose to report an error if there's a risk of command (or variable-value) injection, which can be as simple as:
$path = 'There''s no place like $HOME'
if ($path -match '\$') { Throw 'Unsupported characters in path.' }
However, this also prevents legitimate use of a verbatim $ in paths.
Taking a step back:
You state that the paths may have been copy-pasted from a web site.
Such a pasted string may indeed contain (perhaps hidden) control characters, but they would be contained verbatim rather than as PowerShell_escape sequences.
As such, it may be sufficient to test for / quietly remove control characters from the string's literal content (before calling Test-Path -IsValid):
# Test for control characters.
if ($path -match '\p{C}') { throw 'Path contains control characters.' }
# Quietly remove them.
$sanitizedPath = $path -replace '\p{C}'
I was initially running PS Version 7.1.3, in which all of these methods bore the same results.
When I ran:
$invalidPath = 'HKEY_LOCAL_MACHINE\SOFTWARE\Test\`n#microsoft.com/GENUINE\#microsoft.com/GENUINE'
Test-Path ($ExecutionContext.InvokeCommand.ExpandString($invalidPath)) -isValid
True is returned
Same as:
Test-Path ([regex]::Escape($invalidPath)) -IsValid
As well as the other methods you mentioned.
In testing in 5.1 I saw the same results as you.
In your XML, would you be seeing something like `n or \n or the actual non-printing characters? IE
'HKEY_LOCAL_MACHINE\SOFTWARE\Test\`n#microsoft.com/GENUINE\#microsoft.com/GENUINE'
OR
'HKEY_LOCAL_MACHINE\SOFTWARE\Test\
#microsoft.com/GENUINE\#microsoft.com/GENUINE'
If the latter is true you should be able to just pass the string to test-path in a variable like Test-Path "$invalidPath" -IsValid to get what you're looking for. In 7.1.3 (maybe earlier) it seems that PS is smart enough to parse those escape sequences and such -- or there is no simple way of doing what you're looking for that I can find.
i am trying to parse a website for specific data.
$strings = $body.split(";")
$strings2 = $strings.Where({$_ -like ("*recordData[`"key`"]*")})
i figured out that the square brackets are to blame, i use
$strings2 = $strings.Where({$_ -like ("*recordData*")})
and it works fine, albeit returning way more results then i need.
is there a way i can just search for "recordData[key]"
$body is just the entirety of a returned webpage
thanks.
EDIT:
as requested the input data is like this
rs.currentColumn = 0;
recordData["dataGridExtraRow"] = 0;
recordData["rownum"] = "0";
recordData["key"] = '3354087';
recordData["factory"] = "cr";
in the end i need the 3354087 number, but just picking out the lines i needed was the issue, after that i can pick apart the string fine.
however, i ended up using the .contains, thanks for the suggestion.
sort of facepalmed after i saw it though.
If you are using the like operator then you are going to be having an issue with the open square bracket which is a wildcard character in PowerShell. See About_Wildcards
...
[] - Matches range of characters
...
A use case would be something like this which would return true.
"recordFata" -like "record[DF]ata"
So if you are going to be using -like you need to escape the brackets using backticks in a single quoted sting. You can avoid that by using other methods that function in the same way you intended.
Other options
String .Contains() Method
"sdfafdrecordData[key]asdfasdfas".Contains("recordData[key]")
Fairly basic and no need to worry about special characters for the most part.
Regex
"sdfafdrecordData[key]asdfasdfas" -match "recordData\[key]"
Note that the square braces are also regex metacharacters that need to be escaped as well.
Try using single quotes and escaping with backticks.
You can also simplify using PowerShell syntax:
$strings = $body -split ';' -like '*recordData`[key`]*'
this is part of my bigger code.
I have not added any assemblies for this. I want to get '20161207' separately in a different variable. This approach however is failing with: "parsing "*" - Quantifier {x,y} following nothing."
[string]$filter = '20161207*'
$pathPart = $filter -split '*'
echo $pathPart[0]
please help. I am using powershell version 4.0.
I am not sure what is the escape character in my version of powershell. i Have tried '/'
I want to echo out: '20161207'
The -split operator uses regular expressions, not literal strings. Since * is a quantifier in regexes what you got there is invalid. You need to escape it:
$filter -split '\*'
or use the string.Split method instead:
$filter.Split('*')
which splits on single characters.
I want to unescape PHP code that has been escaped with backslashes. I'm using Powershell to do this.
Backslash characters can be used to escape a number of things, but in my sample data I only see examples of double-quotes being escaped in this manner. Ideally my code would support the other cases, but this is all I have for now.
$test = 'some-text.'
$test -replace '\"', '"'
The output of this is not what I expected:
some-text.
Expected output:
some-text.
I've also tried
$test -replace "`\`"", "`""
But the result was the same.
OK it seems backslashes are also used for escaping backslashes in powershell. Therefore the correct code is
$test -replace '\\"', '"'