TM-RandomUtility.psm1
using namespace System using namespace System.IO using namespace System.Collections.Generic using namespace Microsoft.PowerShell.Commands # Ensure we're using the primary write commands from the Microsoft.PowerShell.Utility module. Set-Alias -Name 'Write-Progress' -Value 'Microsoft.PowerShell.Utility\Write-Progress' -Scope Script Set-Alias -Name 'Write-Debug' -Value 'Microsoft.PowerShell.Utility\Write-Debug' -Scope Script Set-Alias -Name 'Write-Verbose' -Value 'Microsoft.PowerShell.Utility\Write-Verbose' -Scope Script Set-Alias -Name 'Write-Host' -Value 'Microsoft.PowerShell.Utility\Write-Host' -Scope Script Set-Alias -Name 'Write-Information' -Value 'Microsoft.PowerShell.Utility\Write-Information' -Scope Script Set-Alias -Name 'Write-Warning' -Value 'Microsoft.PowerShell.Utility\Write-Warning' -Scope Script Set-Alias -Name 'Write-Error' -Value 'Microsoft.PowerShell.Utility\Write-Error' -Scope Script Set-Alias -Name 'New-TemporaryFile' -Value 'Microsoft.PowerShell.Utility\New-TemporaryFile' -Scope Script Set-Alias -Name 'Get-FileHash' -Value 'Microsoft.PowerShell.Utility\Get-FileHash' -Scope Script function Get-ExceptionTypeName { <# .SYNOPSIS Returns the exception type name that occurred when executing a script block or command. .DESCRIPTION This function executes the provided command or script block and returns the full name of the exception type that occurred during execution. Only use this function if you know the exception will not cause further damage by running again. .PARAMETER Command The command to execute .PARAMETER ScriptBlock The script block to execute .EXAMPLE Get-HelpFromType -TypeNames (Get-ExceptionTypeName -Command 'Write-Error "test"') .OUTPUTS If the provided command causes an exception then the exception's typename will be returned. If the provided command does NOT cause an exception then nothing is returned. #> [CmdletBinding(DefaultParameterSetName = 'string')] [OutputType([Void], [string])] param ( [Parameter( Mandatory, ValueFromPipeline, ValueFromRemainingArguments, ParameterSetName = 'string' )] [ValidateNotNullOrEmpty()] [string]$Command, [Parameter( Mandatory, ValueFromPipeline, ValueFromRemainingArguments, ParameterSetName = 'ScriptBlock' )] [scriptblock]$ScriptBlock ) [ScriptBlock]$SB = switch ($PSCmdlet.ParameterSetName) { 'string' { [ScriptBlock]::Create($Command) } 'ScriptBlock' { $ScriptBlock } } $ErrorActionPreference = 'Stop' try { $SB.Invoke() | Out-Null } catch { return $_.Exception.GetType().FullName } } function Get-HelpFromType { <# .SYNOPSIS Opens the documentation for .NET types in a web browser. .DESCRIPTION This function opens the official Microsoft documentation for .NET types in the default web browser. .PARAMETER Objects Array of objects to determine the type of and fetch the documentation for. .EXAMPLE Get-HelpFromType -TypeNames @($Object1.GetType().FullName, ($Error[-1].Exception.GetType().FullName)) .EXAMPLE Get-HelpFromType -Types $Error[-1].Exception.GetType() #> [CmdletBinding(DefaultParameterSetName = 'TypeNames')] [OutputType([Void])] param ( [Parameter( Mandatory, ValueFromPipeline, ValueFromRemainingArguments, ParameterSetName = 'TypeNames' )] [string[]]$TypeNames, [Parameter( Mandatory, ValueFromPipeline, ValueFromRemainingArguments, ParameterSetName = 'Types' )] [Type[]]$Types ) process { [string[]]$InputTypes = switch ($PSCmdlet.ParameterSetName) { 'Types' { [List[string]]$typeList = [List[string]]::new() foreach ($Type in $Types) { $typeList.Add($Type.FullName) } $typeList.ToArray() } 'TypeNames' { $TypeNames } } foreach ($typeName in $InputTypes) { Start-Process -FilePath "https://docs.microsoft.com/en-us/dotnet/api/$typeName" } } } function Get-StringHash { <# .SYNOPSIS This function generates the hash value for a given string using a specified algorithm and text encoding. .DESCRIPTION Get-StringHash is a function that takes an input string, converts it to a byte array in a specified encoding (defaulting to UTF8), and then computes the hash of these bytes using the specified algorithm (defaulting to SHA256). The function uses PowerShell's Get-FileHash cmdlet on a MemoryStream object to calculate the hash value. The function returns a FileHashInfo object. .PARAMETER InputString The string to be hashed. .PARAMETER Algorithm The hashing algorithm to use. The options are SHA1, SHA256, SHA384, SHA512, and MD5. The default is SHA256. .PARAMETER Encoding The encoding to be used for transforming the input string into bytes. The options are ASCII, BigEndianUnicode, Default, Latin1, Unicode, UTF7, UTF8, and UTF32. The "Default" option uses the dotnet system default encoding. If no argument is provided the function will default to using UTF8. .EXAMPLE # This example hashes the string 'TestString' using the SHA512 algorithm, and using the default encoding (UTF8). 'TestString' | Get-StringHash -Algorithm sha512 #> [CmdletBinding()] [OutputType([string])] param( [Parameter(Position = 0, Mandatory, ValueFromPipeline)] [string]$InputString, [Parameter(Position = 1, Mandatory = $false)] [ValidateSet('SHA1', 'SHA256', 'SHA384', 'SHA512', 'MD5')] [string]$Algorithm = 'SHA256', [Parameter(Position = 2, Mandatory = $false)] [ValidateSet('ASCII', 'BigEndianUnicode', 'Default', 'Latin1', 'Unicode', 'UTF32', 'UTF8', 'UTF7')] [string]$Encoding = 'UTF8' ) # Retrieve the bytearray for the string in the specified encoding.. [byte[]]$byteArray = switch ($Encoding) { 'ASCII' { [System.Text.Encoding]::ASCII.GetBytes($InputString) } 'BigEndianUnicode' { [System.Text.Encoding]::BigEndianUnicode.GetBytes($InputString) } 'Default' { [System.Text.Encoding]::Default.GetBytes($InputString) } 'Latin1' { [System.Text.Encoding]::Latin1.GetBytes($InputString) } 'Unicode' { [System.Text.Encoding]::Unicode.GetBytes($InputString) } 'UTF32' { [System.Text.Encoding]::UTF32.GetBytes($InputString) } 'UTF8' { [System.Text.Encoding]::UTF8.GetBytes($InputString) } 'UTF7' { [System.Text.Encoding]::UTF7.GetBytes($InputString) } } # Turn the bytes into a memorystream and then get the hash of the stream. try { [MemoryStream]$stream = [MemoryStream]::new($byteArray) [FileHashInfo]$hash = Get-FileHash -InputStream $Stream -Algorithm $Algorithm } catch { throw # Re-throw the error after finally. } finally { # Ensure we close the stream if it exists. if ($null -ne $stream) { $stream.Close() } } return $hash } function Get-SubResourceIntegrityHash { <# .SYNOPSIS This function generates a Subresource Integrity (SRI) hash, using openssl, for a URI or local file using the specified hashing algorithm. .DESCRIPTION Get-SubResourceIntegrityHash is a function that generates a Subresource Integrity (SRI) hash. The function can operate on a file located at a given URI (by downloading it to a temporary file) or a local file. It uses openssl (which must be installed on the system and accessible from the system path) to generate the hash. The hash algorithm defaults to SHA256, but can be specified. .PARAMETER Uri The URI of the file to generate an SRI hash for. Either this parameter or 'Path' must be specified. .PARAMETER Path The local file path of the file to generate an SRI hash for. Either this parameter or 'Uri' must be specified. .PARAMETER Algorithm The hashing algorithm to use. The options are sha1, sha256, sha384, sha512, and MD5. The default is sha256. .EXAMPLE # This example generates an SRI hash for Uri 'https://code.jquery.com/jquery-3.7.0.js' using the SHA512 algorithm. Get-SubResourceIntegrityHash -Uri 'https://code.jquery.com/jquery-3.7.0.js' -Algorithm sha512 .EXAMPLE # This example generates an SRI hash for the file located at 'C:\Temp\jquery-3.7.0.js' using the SHA384 algorithm. Get-SubResourceIntegrityHash -Path 'C:\Temp\jquery-3.7.0.js' -Algorithm sha384 #> [CmdletBinding()] [OutputType([string],[Void])] param( [Parameter( Position = 0, Mandatory, ValueFromPipeline, ParameterSetName = 'Uri' )] [string]$Uri, [Parameter( Position = 0, Mandatory, ValueFromPipeline, ParameterSetName = 'Path' )] [Validation.ValidatePathExists('File')] [string]$Path, [Parameter(Position = 1, Mandatory = $false)] [ValidateSet('sha1', 'sha256', 'sha384', 'sha512', 'MD5')] [string]$Algorithm = 'sha256' ) if ((Test-ApplicationExistsInPath -ApplicationName 'openssl') -eq $false) { Write-Error 'openssl does not exist in the Path. Cannot retrieve subresource integrity hash.' return } try { if ($PSCmdlet.ParameterSetName -eq 'Uri') { $TempFile = New-TemporaryFile Invoke-RestMethod -Uri $Uri -Method Get -OutFile $TempFile.FullName $FilePath = $TempFile.FullName } else { $FilePath = $Path } $OpensslCmd = "openssl dgst -$Algorithm -binary $FilePath | openssl base64 -A" $Result = switch ($true) { $IsLinux { bash -c $OpensslCmd } $IsMacOS { zsh -c $OpensslCmd } default { & cmd.exe /c $OpensslCmd } } } catch { throw # Re-throw the error after finally. } finally { if ($TempFile -ne [string]::Empty) { Remove-Item -Path $TempFile.FullName -Force } } return $Result } function New-Timer { <# .SYNOPSIS Starts a new timer. .DESCRIPTION This function creates and starts a new stopwatch timer. #> [CmdletBinding()] [OutputType([Diagnostics.Stopwatch])] param () return [Diagnostics.Stopwatch]::StartNew() } function Test-ApplicationExistsInPath { <# .SYNOPSIS Uses Get-Command to see whether the $ApplicationName exists in the path. #> [CmdletBinding()] [OutputType([boolean])] param( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string]$ApplicationName ) return ($null -ne (Get-Command $ApplicationName -CommandType Application -ErrorAction Ignore)) } function Write-RandomBytesToFile { <# .SYNOPSIS Writes random bytes to a file. .DESCRIPTION This function writes random bytes to a file until that file has reached the specified fileByteSize. If the file is already greater than fileByteSize then the function exits early. .PARAMETER fileByteSize The desired file size in bytes .PARAMETER fileName The name of the file to write to, default is 'temp.rnd' .PARAMETER directoryPath The directory path where the file will be created or updated, default is the current directory #> [CmdletBinding()] [OutputType([Void])] param ( [Parameter(Mandatory)] [long]$fileByteSize, [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$fileName = 'temp.rnd', [Parameter(Mandatory = $false)] [Validation.ValidatePathExists('Folder')] [string]$directoryPath = [Environment]::CurrentDirectory ) [string]$filePath = Join-Path -Path $directoryPath -ChildPath $fileName [Random]$rnd = [Random]::New() [IO.FileInfo]$file = [IO.FileInfo]::New($filePath); if ($file.Length -ge $fileByteSize) { Write-Host "File size for '$($file.FullName)' already meets or exceeds $fileByteSize" return } [IO.FileStream]$outputStream try { $outputStream = [IO.File]::Open($file.FullName, [IO.FileMode]::OpenOrCreate) $outputStream.Seek(0, [IO.SeekOrigin]::End) | Out-Null [long]$bytesWritten = $file.Length do { [byte[]]$bytes = [byte[]]::New( (. { if ($fileByteSize -gt ($bytesWritten + 1024)) { 1024 } else { [int]($fileByteSize - $bytesWritten) } }) ) $rnd.NextBytes($bytes) $outputStream.Write($bytes, 0, $bytes.Length) $bytesWritten += $bytes.Length } while ($bytesWritten -lt $fileByteSize) Write-Host "Wrote $bytesWritten bytes to '$($file.FullName)'. FileSize: $fileByteSize" } finally { if ($null -ne $outputStream) { $outputStream.Dispose() } } } |