|
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() }
|
|
}
|
|
}
|