diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/GetHash.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/GetHash.cs
index 336c4829d42..88f2f694486 100644
--- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/GetHash.cs
+++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/GetHash.cs
@@ -8,11 +8,15 @@
namespace Microsoft.PowerShell.Commands
{
///
- /// This class implements Get-FileHash
+ /// This class implements Get-Hash
///
- [Cmdlet(VerbsCommon.Get, "FileHash", DefaultParameterSetName = PathParameterSet, HelpUri = "https://go.microsoft.com/fwlink/?LinkId=517145")]
- [OutputType(typeof(FileHashInfo))]
- public class GetFileHashCommand : HashCmdletBase
+ [Cmdlet(VerbsCommon.Get, "Hash", DefaultParameterSetName = PathParameterSet, HelpUri = "https://go.microsoft.com/fwlink/?LinkId=517145")]
+ [OutputType(typeof(FileHashInfo), ParameterSetName = new[] { PathParameterSet,
+ LiteralPathParameterSet,
+ StreamParameterSet
+ })]
+ [OutputType(typeof(StringHashInfo), ParameterSetName = new[] { StringHashParameterSet })]
+ public class GetHashCommand : HashCmdletBase
{
///
/// Path parameter
@@ -20,8 +24,8 @@ public class GetFileHashCommand : HashCmdletBase
/// Resolved wildcards
///
///
- [Parameter(Mandatory = true, ParameterSetName = PathParameterSet, Position = 0, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)]
- public String[] Path
+ [Parameter(Mandatory = true, ParameterSetName = PathParameterSet, Position = 0, ValueFromPipelineByPropertyName = true)]
+ public string[] Path
{
get
{
@@ -41,7 +45,7 @@ public String[] Path
///
[Parameter(Mandatory = true, ParameterSetName = LiteralPathParameterSet, Position = 0, ValueFromPipelineByPropertyName = true)]
[Alias("PSPath")]
- public String[] LiteralPath
+ public string[] LiteralPath
{
get
{
@@ -53,7 +57,7 @@ public String[] LiteralPath
}
}
- private String[] _paths;
+ private string[] _paths;
///
/// InputStream parameter
@@ -63,6 +67,38 @@ public String[] LiteralPath
[Parameter(Mandatory = true, ParameterSetName = StreamParameterSet, Position = 0)]
public Stream InputStream { get; set; }
+ ///
+ /// InputString parameter
+ /// The strings to calculate a hash
+ /// We allow `null` and `empty` strings because we can get it from pipeline.
+ ///
+ ///
+ [Parameter(Mandatory = true, ParameterSetName = StringHashParameterSet, Position = 0, ValueFromPipeline = true)]
+ [AllowNull()]
+ [AllowEmptyString()]
+ public string[] InputString { get; set; }
+
+ ///
+ /// Encoding parameter
+ /// The Encoding of the 'InputString'
+ ///
+ ///
+ [Parameter(Mandatory = false, ParameterSetName = StringHashParameterSet, Position = 2)]
+ [ValidateSetAttribute(new string[] {
+ EncodingConversion.Unknown,
+ EncodingConversion.String,
+ EncodingConversion.Unicode,
+ EncodingConversion.BigEndianUnicode,
+ EncodingConversion.Utf8,
+ EncodingConversion.Utf7,
+ EncodingConversion.Utf32,
+ EncodingConversion.Ascii,
+ EncodingConversion.Default,
+ EncodingConversion.OEM })]
+ public string Encoding { get; set; } = EncodingConversion.Default;
+
+
+
///
/// BeginProcessing() override
/// This is for hash function init
@@ -78,6 +114,34 @@ protected override void BeginProcessing()
///
protected override void ProcessRecord()
{
+ if ( ParameterSetName == StringHashParameterSet)
+ {
+ if (InputString == null)
+ {
+ WriteStringHashInfo(Algorithm, null, null, Encoding);
+ }
+ else
+ {
+ foreach (string str in InputString)
+ {
+ if (str == null)
+ {
+ WriteStringHashInfo(Algorithm, string.Empty, string.Empty, Encoding);
+ Exception exception = new Exception("Hash for 'null' string is 'null'");
+ WriteError(new ErrorRecord(exception, "GetHashInvalidData", ErrorCategory.InvalidData, null));
+ }
+ else
+ {
+ byte[] bytehash = hasher.ComputeHash(EncodingConversion.Convert(this, Encoding).GetBytes(str));
+ string hash = BitConverter.ToString(bytehash).Replace("-","");
+ WriteStringHashInfo(Algorithm, hash, str, Encoding);
+ }
+ }
+ }
+
+ return;
+ }
+
List pathsToProcess = new List();
ProviderInfo provider = null;
@@ -119,17 +183,15 @@ protected override void ProcessRecord()
foreach (string path in pathsToProcess)
{
- byte[] bytehash = null;
- String hash = null;
Stream openfilestream = null;
try
{
openfilestream = File.OpenRead(path);
- bytehash = hasher.ComputeHash(openfilestream);
+ byte[] bytehash = hasher.ComputeHash(openfilestream);
- hash = BitConverter.ToString(bytehash).Replace("-","");
- WriteHashResult(Algorithm, hash, path);
+ String hash = BitConverter.ToString(bytehash).Replace("-","");
+ WriteFileHashInfo(Algorithm, hash, path);
}
catch (FileNotFoundException ex)
{
@@ -160,14 +222,14 @@ protected override void EndProcessing()
bytehash = hasher.ComputeHash(InputStream);
hash = BitConverter.ToString(bytehash).Replace("-","");
- WriteHashResult(Algorithm, hash, "");
+ WriteFileHashInfo(Algorithm, hash, "");
}
}
///
/// Create FileHashInfo object and output it
///
- private void WriteHashResult(string Algorithm, string hash, string path)
+ private void WriteFileHashInfo(string Algorithm, string hash, string path)
{
FileHashInfo result = new FileHashInfo();
result.Algorithm = Algorithm;
@@ -176,12 +238,26 @@ private void WriteHashResult(string Algorithm, string hash, string path)
WriteObject(result);
}
+ ///
+ /// Create StringHashInfo object and output it
+ ///
+ private void WriteStringHashInfo(string Algorithm, string hash, string HashedString, string Encoding)
+ {
+ StringHashInfo result = new StringHashInfo();
+ result.Algorithm = Algorithm;
+ result.Hash = hash;
+ result.HashedString = HashedString;
+ result.Encoding = Encoding;
+ WriteObject(result);
+ }
+
///
/// Parameter set names
///
private const string PathParameterSet = "Path";
private const string LiteralPathParameterSet = "LiteralPath";
private const string StreamParameterSet = "StreamParameterSet";
+ private const string StringHashParameterSet = "StringHashParameterSet";
}
@@ -289,4 +365,31 @@ public class FileHashInfo
///
public string Path { get; set;}
}
+
+ ///
+ /// StringHashInfo class contains information about a String hash
+ ///
+ public class StringHashInfo
+ {
+ ///
+ /// Hash algorithm name
+ ///
+ public string Algorithm { get; set;}
+
+ ///
+ /// Hash value of the 'HashedString' string
+ ///
+ public string Hash { get; set;}
+
+ ///
+ /// Encoding of the 'HashedString' string
+ ///
+ public string Encoding { get; set;}
+
+ ///
+ /// HashedString value which 'Hash' calculated for
+ ///
+ public string HashedString { get; set;}
+ }
+
}
diff --git a/src/Modules/Unix/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 b/src/Modules/Unix/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1
index 65f4c80fc5b..edeae0d53d3 100644
--- a/src/Modules/Unix/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1
+++ b/src/Modules/Unix/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1
@@ -20,7 +20,7 @@ CmdletsToExport= "Format-List", "Format-Custom", "Format-Table", "Format-Wide",
"Clear-Variable", "Export-Clixml", "Import-Clixml", "Import-PowerShellDataFile", "ConvertTo-Xml", "Select-Xml", "Write-Debug",
"Write-Verbose", "Write-Warning", "Write-Error", "Write-Information", "Write-Output", "Set-PSBreakpoint",
"Get-PSBreakpoint", "Remove-PSBreakpoint", "Enable-PSBreakpoint", "Disable-PSBreakpoint", "Get-PSCallStack",
- "Send-MailMessage", "Get-TraceSource", "Set-TraceSource", "Trace-Command", "Get-FileHash",
+ "Send-MailMessage", "Get-TraceSource", "Set-TraceSource", "Trace-Command", "Get-Hash",
"Get-Runspace", "Debug-Runspace", "Enable-RunspaceDebug", "Disable-RunspaceDebug",
"Get-RunspaceDebug", "Wait-Debugger" , "Get-Uptime", "New-TemporaryFile", "Get-Verb", "Format-Hex", "Remove-Alias"
FunctionsToExport= "Import-PowerShellDataFile"
diff --git a/src/Modules/Windows-Core/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 b/src/Modules/Windows-Core/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1
index 6aa5b03d76a..2fe7376fe66 100644
--- a/src/Modules/Windows-Core/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1
+++ b/src/Modules/Windows-Core/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1
@@ -20,7 +20,7 @@ CmdletsToExport= "Format-List", "Format-Custom", "Format-Table", "Format-Wide",
"Clear-Variable", "Export-Clixml", "Import-Clixml", "Import-PowerShellDataFile","ConvertTo-Xml", "Select-Xml", "Write-Debug",
"Write-Verbose", "Write-Warning", "Write-Error", "Write-Information", "Write-Output", "Set-PSBreakpoint",
"Get-PSBreakpoint", "Remove-PSBreakpoint", "New-TemporaryFile", "Enable-PSBreakpoint", "Disable-PSBreakpoint", "Get-PSCallStack",
- "Send-MailMessage", "Get-TraceSource", "Set-TraceSource", "Trace-Command", "Get-FileHash",
+ "Send-MailMessage", "Get-TraceSource", "Set-TraceSource", "Trace-Command", "Get-Hash",
"Unblock-File", "Get-Runspace", "Debug-Runspace", "Enable-RunspaceDebug", "Disable-RunspaceDebug",
"Get-RunspaceDebug", "Wait-Debugger" , "Get-Uptime", "Get-Verb", "Format-Hex", "Remove-Alias"
FunctionsToExport= "ConvertFrom-SddlString"
diff --git a/src/Modules/Windows-Full/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 b/src/Modules/Windows-Full/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1
index 4a48f4ec1e7..0b50ffce5f2 100644
--- a/src/Modules/Windows-Full/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1
+++ b/src/Modules/Windows-Full/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1
@@ -22,7 +22,7 @@ CmdletsToExport= "Format-List", "Format-Custom", "Format-Table", "Format-Wide",
"Clear-Variable", "Export-Clixml", "Import-Clixml", "Import-PowerShellDataFile", "ConvertTo-Xml", "Select-Xml", "Write-Debug",
"Write-Verbose", "Write-Warning", "Write-Error", "Write-Information", "Write-Output", "Set-PSBreakpoint", "Get-PSBreakpoint",
"Remove-PSBreakpoint", "Enable-PSBreakpoint", "Disable-PSBreakpoint", "Get-PSCallStack",
- "Send-MailMessage", "Get-TraceSource", "Set-TraceSource", "Trace-Command", "Show-Command", "Unblock-File", "Get-FileHash",
+ "Send-MailMessage", "Get-TraceSource", "Set-TraceSource", "Trace-Command", "Show-Command", "Unblock-File", "Get-Hash",
"Get-Runspace", "Debug-Runspace", "Enable-RunspaceDebug", "Disable-RunspaceDebug", "Get-RunspaceDebug", "Wait-Debugger",
"ConvertFrom-String", "Convert-String" , "Get-Uptime", "New-TemporaryFile", "Get-Verb", "Format-Hex"
FunctionsToExport= "ConvertFrom-SddlString"
diff --git a/src/System.Management.Automation/engine/InitialSessionState.cs b/src/System.Management.Automation/engine/InitialSessionState.cs
index f39ed212529..574af75692b 100644
--- a/src/System.Management.Automation/engine/InitialSessionState.cs
+++ b/src/System.Management.Automation/engine/InitialSessionState.cs
@@ -4605,6 +4605,7 @@ internal static SessionStateAliasEntry[] BuiltInAliases
new SessionStateAliasEntry("ghy", "Get-History", "", ReadOnly),
new SessionStateAliasEntry("gi", "Get-Item", "", ReadOnly),
new SessionStateAliasEntry("gl", "Get-Location", "", ReadOnly),
+ new SessionStateAliasEntry("Get-FileHash", "Get-Hash", "", ReadOnly),
new SessionStateAliasEntry("gm", "Get-Member", "", ReadOnly),
new SessionStateAliasEntry("gmo", "Get-Module", "", ReadOnly),
new SessionStateAliasEntry("gp", "Get-ItemProperty", "", ReadOnly),
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Get-Hash.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Get-Hash.Tests.ps1
new file mode 100644
index 00000000000..bff646605e0
--- /dev/null
+++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Get-Hash.Tests.ps1
@@ -0,0 +1,122 @@
+Describe "Get-Hash tests for files" -Tags "CI" {
+
+ BeforeAll {
+ $testDocument = Join-Path -Path $PSScriptRoot -ChildPath assets testablescript.ps1
+ }
+
+ Context "Default result tests" {
+ It "Should default to correct algorithm, hash and path" {
+ $result = Get-Hash $testDocument
+
+ $result | Should BeOfType 'Microsoft.PowerShell.Commands.FileHashInfo'
+ $result.Algorithm | Should Be "SHA256"
+ $result.Hash | Should Be "4A6DA9F1C0827143BB19FC4B0F2A8057BC1DF55F6D1F62FA3B917BA458E8F570"
+ $result.Path | Should Be $testDocument
+ }
+ }
+
+ Context "Algorithm tests" {
+ BeforeAll {
+ # Keep "sHA1" below! It is for testing that the cmdlet accept a hash algorithm name in any case!
+ $testcases =
+ @{ algorithm = "sHA1"; hash = "01B865D143E07ECC875AB0EFC0A4429387FD0CF7" },
+ @{ algorithm = "SHA256"; hash = "4A6DA9F1C0827143BB19FC4B0F2A8057BC1DF55F6D1F62FA3B917BA458E8F570" },
+ @{ algorithm = "SHA384"; hash = "656215B6A07011E625206F43E57873F49AD7B36DFCABB70F6CDCE2303D7A603E55D052774D26F339A6D80A264340CB8C" },
+ @{ algorithm = "SHA512"; hash = "C688C33027D89ACAC920545471C8053D8F64A54E21D0415F1E03766DDCDA215420E74FAFD1DC399864C6B6B5723A3358BD337339906797A39090B02229BF31FE" },
+ @{ algorithm = "MD5"; hash = "7B09811D1631C9FD46B39D1D35522F0A" }
+ }
+
+ It "Should be able to get the correct hash by Path from algorithm" -TestCases $testCases {
+ param($algorithm, $hash)
+ $algorithmResult = Get-Hash -Path $testDocument -Algorithm $algorithm
+
+ $algorithmResult | Should BeOfType 'Microsoft.PowerShell.Commands.FileHashInfo'
+ $algorithmResult.Algorithm | Should Be $algorithm
+ $algorithmResult.Hash | Should Be $hash
+ $algorithmResult.Path | Should Be $testDocument
+ }
+
+ It "Should be able to get the correct hash by InputStream from algorithm" -TestCases $testCases {
+ param($algorithm, $hash)
+ $testFileStream = [System.IO.File]::OpenRead($testDocument)
+ $algorithmResult = Get-Hash -InputStream $testFileStream -Algorithm $algorithm
+
+ $algorithmResult | Should BeOfType 'Microsoft.PowerShell.Commands.FileHashInfo'
+ $algorithmResult.Algorithm | Should Be $algorithm
+ $algorithmResult.Hash | Should Be $hash
+ }
+
+ It "Should be able to get the correct hash by String from algorithm" -TestCases $testCases {
+ param($algorithm, $hash)
+ # Simple trick needed to get a test string from byte sequence because the test file contains BOM.
+ # It allows to reuse the file hashes.
+ $testBytes = Get-Content $testDocument -Raw -Encoding Byte
+ $testString = [System.Text.Encoding]::UTF8.GetString($testBytes)
+ $algorithmResult = Get-Hash -InputString $testString -Algorithm $algorithm -Encoding UTF8
+ $algorithmResultFromPipe = $testString | Get-Hash -Algorithm $algorithm -Encoding UTF8
+
+ $algorithmResult | Should BeOfType 'Microsoft.PowerShell.Commands.StringHashInfo'
+ $algorithmResult.Algorithm | Should Be $algorithm
+ $algorithmResult.Hash | Should Be $hash
+ $algorithmResult.Encoding | Should Be 'UTF8'
+ $algorithmResult.HashedString | Should Be $testString
+
+ $algorithmResultFromPipe | Should BeOfType 'Microsoft.PowerShell.Commands.StringHashInfo'
+ $algorithmResultFromPipe.Algorithm | Should Be $algorithm
+ $algorithmResultFromPipe.Hash | Should Be $hash
+ $algorithmResultFromPipe.Encoding | Should Be 'UTF8'
+ $algorithmResultFromPipe.HashedString | Should Be $testString
+ }
+
+ It "Should be able to get the correct hash for 'null' String" {
+ $result = Get-Hash -InputString $null
+
+ $result | Should BeOfType 'Microsoft.PowerShell.Commands.StringHashInfo'
+ $result.Algorithm | Should Be 'SHA256'
+ $result.Hash | Should Be $null
+ $result.Encoding | Should Be 'Default'
+ $result.HashedString | Should Be $null
+ }
+
+ It "Should be throw for wrong algorithm name" {
+ { Get-Hash $testDocument -Algorithm wrongAlgorithm } | ShouldBeErrorId "ParameterArgumentValidationError,Microsoft.PowerShell.Commands.GetHashCommand"
+ }
+ }
+
+ Context "Paths tests" {
+ It "With '-Path': no file exist" {
+ { Get-Hash -Path nofileexist.ttt -ErrorAction Stop } | ShouldBeErrorId "FileNotFound,Microsoft.PowerShell.Commands.GetHashCommand"
+ }
+
+ It "With '-LiteralPath': no file exist" {
+ { Get-Hash -LiteralPath nofileexist.ttt -ErrorAction Stop } | ShouldBeErrorId "FileNotFound,Microsoft.PowerShell.Commands.GetHashCommand"
+ }
+
+ It "With '-Path': file exist" {
+ $result = Get-Hash -Path $testDocument
+
+ $result | Should BeOfType 'Microsoft.PowerShell.Commands.FileHashInfo'
+ $result.Algorithm | Should Be "SHA256"
+ $result.Hash | Should Be "4A6DA9F1C0827143BB19FC4B0F2A8057BC1DF55F6D1F62FA3B917BA458E8F570"
+ $result.Path | Should Be $testDocument
+ }
+
+ It "With '-LiteralPath': file exist" {
+ $result = Get-Hash -LiteralPath $testDocument
+
+ $result | Should BeOfType 'Microsoft.PowerShell.Commands.FileHashInfo'
+ $result.Algorithm | Should Be "SHA256"
+ $result.Hash | Should Be "4A6DA9F1C0827143BB19FC4B0F2A8057BC1DF55F6D1F62FA3B917BA458E8F570"
+ $result.Path | Should Be $testDocument
+ }
+
+ It "With '-Path': using a pipe" {
+ $result = Get-ChildItem $testDocument | Get-Hash
+
+ $result | Should BeOfType 'Microsoft.PowerShell.Commands.FileHashInfo'
+ $result.Algorithm | Should Be "SHA256"
+ $result.Hash | Should Be "4A6DA9F1C0827143BB19FC4B0F2A8057BC1DF55F6D1F62FA3B917BA458E8F570"
+ $result.Path | Should Be $testDocument
+ }
+ }
+}
diff --git a/test/powershell/engine/Basic/DefaultCommands.Tests.ps1 b/test/powershell/engine/Basic/DefaultCommands.Tests.ps1
index e2584780e70..d40a70b24ff 100644
--- a/test/powershell/engine/Basic/DefaultCommands.Tests.ps1
+++ b/test/powershell/engine/Basic/DefaultCommands.Tests.ps1
@@ -60,6 +60,7 @@ Describe "Verify approved aliases list" -Tags "CI" {
"Alias", "gcm", "Get-Command", $($FullCLR -or $CoreWindows -or $CoreUnix), "ReadOnly", ""
"Alias", "gcs", "Get-PSCallStack", $($FullCLR -or $CoreWindows -or $CoreUnix), "ReadOnly", ""
"Alias", "gdr", "Get-PSDrive", $($FullCLR -or $CoreWindows -or $CoreUnix), "ReadOnly", ""
+"Alias", "Get-FileHash", "Get-Hash", $( $CoreWindows -or $CoreUnix), "ReadOnly", "AllScope"
"Alias", "ghy", "Get-History", $($FullCLR -or $CoreWindows -or $CoreUnix), "ReadOnly", ""
"Alias", "gi", "Get-Item", $($FullCLR -or $CoreWindows -or $CoreUnix), "ReadOnly", ""
"Alias", "gin", "Get-ComputerInfo", $($FullCLR -or $CoreWindows ), "", ""
@@ -256,7 +257,7 @@ Describe "Verify approved aliases list" -Tags "CI" {
"Cmdlet", "Get-EventLog", , $($FullCLR )
"Cmdlet", "Get-EventSubscriber", , $($FullCLR -or $CoreWindows -or $CoreUnix)
"Cmdlet", "Get-ExecutionPolicy", , $($FullCLR -or $CoreWindows -or $CoreUnix)
-"Cmdlet", "Get-FileHash", , $( $CoreWindows -or $CoreUnix)
+"Cmdlet", "Get-Hash", , $( $CoreWindows -or $CoreUnix)
"Cmdlet", "Get-FormatData", , $($FullCLR -or $CoreWindows -or $CoreUnix)
"Cmdlet", "Get-Help", , $($FullCLR -or $CoreWindows -or $CoreUnix)
"Cmdlet", "Get-History", , $($FullCLR -or $CoreWindows -or $CoreUnix)