Passing a registry key with an asterisk to Test-Path - powershell

I want to run this registry path by Test-Path in PowerShell but it contains an asterisk, which is valid in the registry but not in Windows paths.
The problem is, when I pass it, Test-Path treats the asterisk as a wild card, so this takes a very, very long time because it checks all the sub paths of Classes and is not anything like what I want to test anyway.
Is there any way I can correctly pass that asterisk? Some escape mechanism?
Write-Host "Begin"
Test-Path "HKLM:\SOFTWARE\Classes\*\shell\Some shell extension"
Write-Host "End"

Use the -LiteralPath parameter to prevent globbing/wildcard matching.
-LiteralPath<String[]>
Specifies a path to be tested. Unlike Path, the value of the LiteralPath parameter is used exactly as it is typed. No characters are interpreted as wildcards. If the path includes escape characters, enclose it in single quotation marks. Single quotation marks tell Windows PowerShell not to interpret any characters as escape sequences.
Write-Host "Begin"
Test-Path -LiteralPath "HKLM:\SOFTWARE\Classes\*\shell\Some shell extension"
Write-Host "End"

Related

How to list contents of a specific directory in powershell?

I'm rather baffled by Powershell in general. Very weird. Anyway, I've read:
Powershell's equivalent to Linux's: ls -al
so I now know how to list the contents of the current directory. But how can I list the contents of an arbitrary directory?
Specifically,
How do I type in the path I want to check?
in Windows, \ is the directory separator; but it's also an escape char in most languages. What do I do with it?
Do I need single-quotes? Double-quotes? No-quotes?
Where do I place the argument relative to the switches? After, like I'm used to, or rather before?
If I want to use an environment variable, or a powershell variable, as part of the path to list - how do I do that?
General PowerShell information and examples:
PowerShell-native commands, including user-authored ones that opt in, have standardized parameter syntax and binding rules, so the following, focused on Get-ChildItem, applies generally:
See the help topic for Get-ChildItem, which describes the command's purpose, syntax, and individual parameters, along with showing examples.
If you have offline help installed (true by default in Windows PowerShell; installable on demand via Update-Help in PowerShell (Core) 7+), you can also print the examples with Get-Help -Example Get-ChildItem | more
As for how to generally read the notation describing the supported parameters listed under the SYNTAX heading of cmdlet help topics, which PowerShell calls syntax diagrams, see the conceptual about_Command_Syntax help topic.
Offline, you can print the syntax diagrams for PowerShell-native commands with standard switch -? or by passing the command name to Get-Command -Syntax (Get-ChildItem -? or Get-Command -Syntax Get-ChildItem). To also access the help text offline, you may have to install local help first, as described above.
Examples:
# The following commands all pass "\" (the [current drive's] root dir)
# to the -Path parameter.
# Note:
# * "\" is NOT special in PowerShell, so it needs no escaping.
# PowerShell allows use of "/" instead even on Windows.
# * -Path arguments are interpreted as wildcard patterns, whether
# quoted or not. You may pass *multiple* paths / patterns.
# * Switch -Force - which, as all switches - can be placed anywhere
# among the arguments, requests that *hidden* items be listed too.
Get-ChildItem -Path \ -Force
Get-ChildItem \ -Force # ditto, with *positional* param. binding
'\' | Get-ChildItem # ditto, via the pipeline.
Get-ChildItem / -Force # ditto, with "/" as alternative to "\"
# To force interpretation as a *literal* path - which matters for
# paths that contain "[" chars. - use -LiteralPath.
# Here too you may pass *multiple* paths.
Get-ChildItem -LiteralPath \ -Force
# Quoting is needed for paths with spaces, for instance.
# Single-quoting treats the string *verbatim*:
Get-ChildItem 'C:\path\to\dir with spaces'
# Double-quoting *expands* (interpolates) variable references
# and subexpressions.
Get-ChildItem "$HOME\dir with spaces"
# A variable alone can be used as-is, without double-quoting, even
# if its value contains spaces.
Get-ChildItem $HOME
To answer your specific questions, for readers who come from POSIX-compatible shells such as Bash:
How do I type in the path I want to check?
Get-ChildItem offers two ways to specify one or more input paths, as most file-processing cmdlets in PowerShell do:
-Path accepts one or more names or paths that are interpreted as a wildcard pattern.
Note that - unlike in POSIX-compatible shells such as Bash - it does not matter whether the path is unquoted or not; e.g., Get-ChildItem -Path *.txt, Get-ChildItem "*.txt", and Get-ChildItem '*.txt' are all equivalent; more on that below (note the incidental omission of -Path in the latter two calls, which makes "*.txt" and '*.txt' bind positionally to the first positional parameter, -Path).
-LiteralPath accepts one or more names or paths that are assumed to refer to existing file-system items literally (verbatim).
Given that literal paths on Unix-like platforms usually do not contain * and ? characters and on Windows cannot, use of -LiteralPath for disambiguation is usually only necessary for paths that contain [ (and possibly also ]), given that PowerShell's wildcard pattern language also supports character sets and ranges (e.g. [ab] and [0-9]).
Binding to -LiteralPath via an argument requires explicit use of -LiteralPath, i.e. use of a named argument; e.g., Get-ChildItem -LiteralPath foo
However, supplying System.IO.FileInfo and/or System.IO.DirectoryInfo instances (such as output by (another) Get-ChildItem or Get-Item call) via the pipeline DOES bind to -LiteralPath, as explained in this answer.
in Windows, \ is the directory separator; but it's also an escape char in most languages. What do I do with it?
In PowerShell \ is not an escape character, so \ instances are treated as literals and do not require escaping; it is `, the so-called backtick that serves as the escape character in PowerShell - see the conceptual about_Special_Characters help topic.
Note, however, that PowerShell generally allows you to use \ and / in paths interchangeably, so that, e.g., Get-ChildItem C:/Windows works just fine.
Where do I place the argument relative to the switches? After, like I'm used to, or rather before?
Note:
All parameters have names in PowerShell - that is, there is no POSIX-like distinction between options (e.g. -l and operands (value-only arguments, such as the / in ls -l /).
A command may declare parameters that may also be passed by value only, positionally, meaning that prefixing the value with the parameter name is then optional (e.g., Get-Path / as shorthand for Get-Path -Path /).
Only parameters that require a value (argument) can potentially be passed as values only - depending on the parameter declaration of the target command - in which case their order matters:
Value-only arguments are called positional arguments, and they bind in order to those parameters of the target command that are declared as positional, if any - see this answer for how to discover which of a given command's parameters are positional ones.
Prefixing a value by its target parameter (e.g., -LiteralPath /some/path) makes it a named argument.
A switch (flag) in PowerShell, such as -Force, is a special parameter type - Boolean in nature - that by definition requires passing it as a named argument, typically without a value (though you can attach a value, e.g. -Force:$true or -Force:$false - note that : is then required to separate the parameter name from its value; see this answer for details).
As an aside: Unlike POSIX-compliant utilities, PowerShell does not support parameters with optional values of any other type - see this answer.
Since PowerShell allows named arguments to be specified in any order, the implication is that you're free to place by-definition-named switch arguments such as -Force anywhere on the command line.
In short: Order only matters among positional (unnamed) arguments in PowerShell.
See the conceptual about_Parameters help topic for more information.
Do I need single-quotes? Double-quotes? No-quotes?
A path (parameter value in general) needs quoting:
if it contains PowerShell metacharacters, notably spaces; e.g. C:\path\to\foo needs no quoting, whereas C:\path with spaces\to\foo does.
if it starts with a subexpression ($(...)), in which case you need double-quoting, i.e. "..." (see below) - though you may choose to always use "..."-quoting for paths involving subexpressions or variable references.
Note that PowerShell has no concept that is the equivalent of the so-called shell expansions in POSIX-compatible shells such as Bash; therefore, whether a given argument is quoted or not makes no semantic difference (assuming it doesn't require quoting); as noted above, *.txt, '*.txt' and "*.txt" are all equivalent, and it is the target command, not PowerShell itself, that interprets the pattern - see this answer for more information.
If quoting is needed:
Verbatim (single-quoted) strings ('...') treat their content verbatim
Expandable (double-quoted) strings ("...") perform string interpolation ("expand" embedded variables and subexpressions, i.e replace them with their values).
If I want to use an environment variable, or a powershell variable, as part of the path to list - how do I do that?
To use such variables as-is, no quoting is needed (even if the values contain spaces):
# PowerShell variable:
Get-ChildItem -LiteralPath $HOME
# Environment variable, e.g. on Windows:
Get-ChildItem -LiteralPath $env:USERPROFILE
To make a variable (or expression) part of a larger string, embed it in an expandable (double-quoted) string ("..."); e.g.:
Get-ChildItem -LiteralPath "$HOME/Desktop"
Note:
Embedding the output from expressions or commands requires use of $(...), the subexpression operator; e.g. Get-ChildItem "$(Get-Variable -ValueOnly Home)/Desktop"; for a complete overview of PowerShell's expandable strings (string interpolation), see this answer.
Situationally, such as in the example above, omitting the "..." quoting would work too - see this answer for more information.
Additionally and alternatively, PowerShell allows you to use (...), the grouping operator to pass the result of arbitrary expressions and commands as arguments; the following is the equivalent of the command above:
Get-ChildItem -LiteralPath ($HOME + '/Desktop')
Alternatively, using the Join-Path cmdlet:
Get-ChildItem -LiteralPath (Join-Path $HOME Desktop)

PowerShell safe expansion of non printing characters in arbitrary strings

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.

How to escape an asterisk?

When I try to do anything with the path HKCR:\* it just takes forever. So I assume it takes the asterisk as wildcard.
Test-Path -Path HKCR:\*
What I've tried
HKCR:\\*
HKCR:\`*
'HKCR:\*'
Use the LiteralPath parameter rather than the Path parameter to prevent characters from being interpreted as wildcards.
Test-Path -LiteralPath HKCR:\*

Meaning of Powershell statement

I am completely new to Powershell and I am trying to understand what this fragment of code does:
$OwnFunctionsDir = "$env:USERPROFILE\Documents\WindowsPowerShell\Functions"
Write-Host "Loading own PowerShell functions from:" -ForegroundColor Green
Write-Host "$OwnFunctionsDir" -ForegroundColor Yellow
Get-ChildItem "$OwnFunctionsDir\*.ps1" | %{.$_}
Write-Host ''
In particular I cannot interpret what the line Get-Children … does. This code is intended to be added to your Powershell profile to load commonly used functions at Powershell startup.
Short Answer
This command loads the contents of all ".ps1" files in "<yourHomeDirecotry>\Documents\WindowsPowerShell\Functions" into your working session.
Long Answer
First, $env:USERPROFILE is an environment variable that corresponds to your home directory. So in my case it is "c:\users\jboyd"
The first interesting bit of code is:
$OwnFunctionsDir = "$env:USERPROFILE\Documents\WindowsPowerShell\Functions"
This assigns a string to a new variable named OwnFunctionsDir. What's interesting about this string is that it is double quoted and it contains the variable $env:USERPROFILE. PowerShell expands variables in double quoted strings (this is not the case for single quoted strings). So if this were running on my machine the value of $OwnFunctionsDir would be "c:\users\jboyd\Documents\WindowsPowerShell\Functions".
Skipping the Write-host functions (because I think they are pretty self explanatory) takes us to:
Get-ChildItem "$OwnFunctionsDir\*.ps1" | %{.$_}
Get-ChildItem is interesting because its behavior depends on the PowerShell provider (don't worry about what that is) but in this case Get-ChildItem is the equivalent of dir or ls. $OwnFunctionsDir\*.ps1 is the path being enumerated. Using my machine as an example, this is equivalent to listing all files with names matching the wildcard pattern "*.ps1" (essentially all PowerShell files) in the directory "c:\users\jboyd\Documents\WindowsPowerShell\Functions".
The | character pipes the results of the command on the left to the command on the right.
The % character is an alias for the ForEach-Object command. The left and right curly braces are a scriptblock, this is the body of the foreach command. So each item from Get-ChildItem is piped into the scriptblock.
In the scriptblock of the ForEach-Object command the $_ variable represents the current item being processed. In this case $_ will be a PowerShell file with an extension ".ps1". When we invoke a PowerShell file with a period in front of it that is called dot sourcing. Dot sourcing loads the content of a file into your working session. So any variables or functions in the file are loaded.

Powershell get path value from xml file

I'm a beginner of powershell and I'm having trouble reading the path value of an xml file.
Settings.xml
<?xml version="1.0"?>
<Configuration>
<EmailSettings>
<SMTPServer>blabla</SMTPServer>
<SMTPPort>blabla</SMTPPort>
<Path>Repository\Excel</Path>
</EmailSettings>
</Configuration>
To read the data in the XML file I do this way
$ScriptPath = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
[xml]$ConfigFile = Get-Content "$ScriptPath\Settings.xml"
The problem is that if I display the extracted value it is shown correctly, while if I concatenate it with another value, for example to obtain a complete path, I get this
Write-Host $ScriptPath
--> c:\script
write-host $ConfigFile.Configuration.EmailSettings.Path
--> Repository\Excel
write-host $ScriptPath\$ConfigFile.Configuration.EmailSettings.Path
--> c:\script\System.Xml.XmlDocument.Configuration.EmailSettings.Path
How do we do to get the value (in string format??) to be able to concatenate to other variables?
Thank you
You need $(...), the subexpression operator, in order to evaluate an expression such as $ConfigFile.Configuration.EmailSettings.Path inside an expandable string.
By contrast, a simple variable reference such as $ScriptPath is fine without the $(...):
Write-Host "$ScriptPath\$($ConfigFile.Configuration.EmailSettings.Path)"
Note that I've additionally added explicit double-quoting, to make the command more robust - see below for details.
As an aside: Write-Host is generally the wrong tool to use, unless the intent is explicitly to write to the display only, bypassing PowerShell's output streams.
Generally, unquoted tokens that aren't single expressions are implicitly treated like expandable strings, i.e., as if they had been passed in double quotes, and by and large the same rules apply.
However, in the absence of explicit quoting there are additional pitfalls, such as tokens that start with - or the need to escape additional characters, so always double-quoting expandable tokens is a good habit to form.
How PowerShell parses unquoted tokens in argument mode in general is a complex topic - see this answer.