diff --git a/CHANGELOG.md b/CHANGELOG.md index 99ac33f..aaebc9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # IT-ToolBox - Change History +## 3.1.0 - 2026-10-24 + +- Add Test-IsGuid from PowerShell-Functions as a supported dependency-free validator, bringing the exported API to 30 commands. +- Preserve N/D/B/P format acceptance, zero-GUID acceptance, whitespace rejection and one Boolean per pipeline record. +- Update manifest, loader, README inventory and packaged-module regression expectations for 3.1.0. +- Retain the logger fixes from 3.0.1; no new mandatory module dependencies. + ## 3.0.1 - 2026-10-24 - Share logger mutex identities for case-only path aliases on macOS, conservatively serializing distinct case-only names on case-sensitive Mac volumes. diff --git a/IT-ToolBox.psd1 b/IT-ToolBox.psd1 index 0309c8a..b4a937e 100644 --- a/IT-ToolBox.psd1 +++ b/IT-ToolBox.psd1 @@ -1,6 +1,6 @@ @{ RootModule = 'IT-ToolBox.psm1' - ModuleVersion = '3.0.1' + ModuleVersion = '3.1.0' GUID = '02c0c748-00ba-4e8f-b8ce-6aa7e4cefe08' Author = 'Daniele Catanesi - SingularVector' CompanyName = 'Community' @@ -19,6 +19,7 @@ 'Test-FileName' 'Test-IsValidPath' 'Test-IsIP' + 'Test-IsGuid' 'Test-IsDate' 'Test-IsEmail' 'Test-IsUrl' @@ -44,7 +45,7 @@ AliasesToExport = @() PrivateData = @{ PSData = @{ - ReleaseNotes = '3.0.1: Fix macOS case-alias log locking and reject non-filesystem log destinations. Retains the 29-command API. See CHANGELOG.md.' + ReleaseNotes = '3.1.0: Add dependency-free Test-IsGuid syntax validation with pipeline support. Includes 3.0.1 logger fixes; exports 30 commands. See CHANGELOG.md.' Tags = @('Infrastructure', 'Automation', 'Logging') LicenseUri = 'https://github.com/SingularVector/IT-ToolBox/blob/master/LICENSE' ProjectUri = 'https://github.com/SingularVector/IT-ToolBox' diff --git a/IT-ToolBox.psm1 b/IT-ToolBox.psm1 index 9c132ef..5e7a5d7 100644 --- a/IT-ToolBox.psm1 +++ b/IT-ToolBox.psm1 @@ -19,6 +19,7 @@ Export-ModuleMember -Function @( 'Test-FileName' 'Test-IsValidPath' 'Test-IsIP' + 'Test-IsGuid' 'Test-IsDate' 'Test-IsEmail' 'Test-IsUrl' diff --git a/Public/Test-IsGuid.ps1 b/Public/Test-IsGuid.ps1 new file mode 100644 index 0000000..515093b --- /dev/null +++ b/Public/Test-IsGuid.ps1 @@ -0,0 +1,37 @@ +function Test-IsGuid +{ + <# + .SYNOPSIS + Tests a complete GUID string using defined .NET formats. + .DESCRIPTION + Accepts N (32 digits), D (dashed), B (matched braces) and P (matched + parentheses). Rejects surrounding whitespace, extra lines, mixed optional + delimiters and the X initializer format. Null and empty input return false. + The all-zero GUID is syntactically valid; this does not test object existence. + #> + [CmdletBinding()] + [OutputType([bool])] + param ( + [Parameter(Mandatory, Position = 0, ValueFromPipeline)] + [AllowNull()] + [AllowEmptyString()] + [string]$ObjectGuid + ) + + process + { + if ([string]::IsNullOrWhiteSpace($ObjectGuid) -or $ObjectGuid -match '\s') + { + return $false + } + $parsed = [guid]::Empty + foreach ($format in @('N', 'D', 'B', 'P')) + { + if ([guid]::TryParseExact($ObjectGuid, $format, [ref]$parsed)) + { + return $true + } + } + return $false + } +} diff --git a/README.md b/README.md index 09d6ff2..4d17758 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,10 @@ [![PowerShell 7.4+](https://img.shields.io/badge/PowerShell-7.4%2B-5391FE?logo=powershell)](https://github.com/PowerShell/PowerShell) [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE) -[![Version: 3.0.1](https://img.shields.io/badge/version-3.0.1-blue.svg)](./CHANGELOG.md) +[![Version: 3.1.0](https://img.shields.io/badge/version-3.1.0-blue.svg)](./CHANGELOG.md) [![Testing wanted](https://img.shields.io/badge/testing-wanted-yellow.svg)](https://github.com/SingularVector/IT-ToolBox/issues/new/choose) -> **Testing wanted — help validate IT-ToolBox 3.0.1 in real environments.** +> **Testing wanted — help validate IT-ToolBox 3.1.0 in real environments.** > Feedback is especially useful for Windows AD report chains, remote CIM uptime, > API authentication and platform clipboard behavior. > [Report a bug](https://github.com/SingularVector/IT-ToolBox/issues/new/choose) @@ -38,17 +38,17 @@ and remote CIM commands have the platform requirements listed below. ## Release status -**IT-ToolBox 3.0.1** contains the 29-command API documented below, including the -standalone timer alignment and updated module metadata. The manifest, loader, +**IT-ToolBox 3.1.0** contains the 30-command API documented below, including GUID +syntax validation and the logger fixes from 3.0.1. The manifest, loader, command inventory and installable archive use the same supported function set. -See the [3.0.1 release notes](./CHANGELOG.md) for the changes and migration details. +See the [3.1.0 release notes](./CHANGELOG.md) for the changes and migration details. -Release builds should use the reviewed 3.0.1 commit or tag rather than a moving +Release builds should use the reviewed 3.1.0 commit or tag rather than a moving master checkout. Build and installation instructions are in [Packaging](./docs/Packaging.md). ## Status and requirements -The module has **29 exported functions**, matching the manifest and loader. +The module has **30 exported functions**, matching the manifest and loader. Automated tests cover the supported API; live AD, remote CIM and service authentication remain unverified. Requires PowerShell 7.4 or later (Core edition). Windows @@ -120,6 +120,7 @@ parameter help and the sections below for compatibility details. | `Test-FileName` | Check lexical filename rules for the host or Windows compatibility. | | `Test-IsValidPath` | Check path syntax without checking existence or permissions. | | `Test-IsIP` | Check strict IP address syntax. | +| `Test-IsGuid` | Validate N/D/B/P GUID text without looking up an object. | | `Test-IsDate` | Check dates with current-culture or explicit format/culture rules. | | `Test-IsEmail` | Check the documented bare mailbox syntax subset. | | `Test-IsUrl` | Check supported absolute URL syntax; does not fetch the URL. | @@ -214,7 +215,7 @@ elapsed duration expressed in hours. These switches are mutually exclusive. - All commands formerly retained in `Staging/v3/` now have supported implementations. Its README records the migration. Exchange/AzureAD session wrappers and the global certificate-validation bypass have been removed; their source remains in Git history. -- Only the twenty-nine listed commands are exported. Private helpers, variables and aliases +- Only the thirty listed commands are exported. Private helpers, variables and aliases are not exported. Do not treat v3 as a drop-in replacement for every v2 script: removed commands and documented parameter, output and encryption changes require migration. No restoration of removed service wrappers is promised. @@ -247,6 +248,16 @@ See [CHANGELOG.md](./CHANGELOG.md) for history. Released under the [MIT License] ## Validation semantics +`Test-IsGuid` accepts complete N (32 hexadecimal digits), D (dashed), B (braced) +and P (parenthesized) GUID strings. It rejects surrounding whitespace, mixed +brackets and the X initializer format. Null and empty inputs return false; the +all-zero GUID is syntactically valid. One Boolean is returned per pipeline input. +This checks syntax only, not whether a directory object or resource exists. + +```powershell +'00000000-0000-0000-0000-000000000000', 'invalid' | Test-IsGuid +``` + Filename and path checks are lexical checks, not guarantees of existence, permissions or filesystem limits. Filename rules default to the host platform; use `-WindowsCompatible` to validate Windows names on another platform. Path checks do diff --git a/Tests/Guid.Tests.ps1 b/Tests/Guid.Tests.ps1 new file mode 100644 index 0000000..0e432b1 --- /dev/null +++ b/Tests/Guid.Tests.ps1 @@ -0,0 +1,28 @@ +BeforeAll { Import-Module (Join-Path $PSScriptRoot '../IT-ToolBox.psd1') -Force -ErrorAction Stop } +Describe 'GUID syntax contract' { + It 'accepts ' -ForEach @( + @{Value='7761bf39-9a9f-42c8-869f-7c6e2689811a'} + @{Value='7761BF39-9A9F-42C8-869F-7C6E2689811A'} + @{Value='7761bf399a9f42c8869f7c6e2689811a'} + @{Value='{7761bf39-9a9f-42c8-869f-7c6e2689811a}'} + @{Value='(7761bf39-9a9f-42c8-869f-7c6e2689811a)'} + @{Value='00000000-0000-0000-0000-000000000000'} + ) { Test-IsGuid $Value | Should -BeTrue } + It 'rejects malformed or unsupported input: ' -ForEach @( + @{Value=$null};@{Value=''};@{Value=' '};@{Value='not-a-guid'} + @{Value='7761bf39-9a9f-42c8-869f-7c6e2689811a}'} + @{Value='{7761bf39-9a9f-42c8-869f-7c6e2689811a)'} + @{Value='{7761bf399a9f42c8869f7c6e2689811a}'} + @{Value='7761bf39-9a9f42c8-869f-7c6e2689811a'} + @{Value="junk`n7761bf39-9a9f-42c8-869f-7c6e2689811a`njunk"} + @{Value="7761bf39-9a9f-42c8-869f-7c6e2689811a`n"} + @{Value=' 7761bf39-9a9f-42c8-869f-7c6e2689811a'} + @{Value='{0x7761bf39,0x9a9f,0x42c8,{0x86,0x9f,0x7c,0x6e,0x26,0x89,0x81,0x1a}}'} + ) { Test-IsGuid -ObjectGuid $Value | Should -BeFalse } + It 'returns one Boolean per pipeline record' { + $r=@('bad','','00000000-0000-0000-0000-000000000000') | Test-IsGuid + $r.Count | Should -Be 3 + ($r -join ',') | Should -BeExactly 'False,False,True' + foreach($v in $r){$v | Should -BeOfType ([bool])} + } +} diff --git a/Tests/IntegrationBoundary.Tests.ps1 b/Tests/IntegrationBoundary.Tests.ps1 index dfc3c27..fc3a426 100644 --- a/Tests/IntegrationBoundary.Tests.ps1 +++ b/Tests/IntegrationBoundary.Tests.ps1 @@ -67,7 +67,7 @@ finally { $result.ErrorPreference | Should -Be 'Continue' $result.WarningPreference | Should -Be 'Continue' $result.LocationPreserved | Should -BeTrue - $result.ExportCount | Should -Be 29 + $result.ExportCount | Should -Be 30 $result.HistoricalExports | Should -Be 0 } } diff --git a/Tests/Module.Tests.ps1 b/Tests/Module.Tests.ps1 index 0f2cf37..3b41c75 100644 --- a/Tests/Module.Tests.ps1 +++ b/Tests/Module.Tests.ps1 @@ -6,11 +6,11 @@ BeforeAll { Describe 'IT-ToolBox module boundary' { It 'has a valid manifest' { $manifest = Test-ModuleManifest $manifestPath -ErrorAction Stop - $manifest.Version | Should -Be ([version]'3.0.1') + $manifest.Version | Should -Be ([version]'3.1.0') } It 'exports exactly the supported commands' { - $expected = @('New-LogEntry', 'New-Timer', 'Get-TimerStatus', 'Stop-Timer', 'Get-ElapsedTime', 'Get-ScriptDirectory', 'Get-ScriptName', 'Test-FileName', 'Test-IsValidPath', 'Test-IsIP', 'Test-IsDate', 'Test-IsEmail', 'Test-IsUrl', 'Convert-LogonTimestamp', 'Get-OsUpTime', 'Remove-SpecialCharacters', 'Test-RegistryValue', 'Test-IsValidDn', 'Test-IsValidUpn', 'Get-ReportChain', 'New-StringEncryption', 'New-StringDecryption', 'New-RandomString', 'New-RandomPassword', 'New-PhoneticPassword', 'New-ApiRequest', 'New-StringConversion', 'Get-StringCheckSum', 'Get-StringHashCode') | Sort-Object + $expected = @('New-LogEntry', 'New-Timer', 'Get-TimerStatus', 'Stop-Timer', 'Get-ElapsedTime', 'Get-ScriptDirectory', 'Get-ScriptName', 'Test-FileName', 'Test-IsValidPath', 'Test-IsIP', 'Test-IsGuid', 'Test-IsDate', 'Test-IsEmail', 'Test-IsUrl', 'Convert-LogonTimestamp', 'Get-OsUpTime', 'Remove-SpecialCharacters', 'Test-RegistryValue', 'Test-IsValidDn', 'Test-IsValidUpn', 'Get-ReportChain', 'New-StringEncryption', 'New-StringDecryption', 'New-RandomString', 'New-RandomPassword', 'New-PhoneticPassword', 'New-ApiRequest', 'New-StringConversion', 'Get-StringCheckSum', 'Get-StringHashCode') | Sort-Object $actual = @($module.ExportedFunctions.Keys | Sort-Object) ($actual -join ',') | Should -Be ($expected -join ',') $module.ExportedVariables.Count | Should -Be 0 diff --git a/Tests/Packaging.Tests.ps1 b/Tests/Packaging.Tests.ps1 index 46f5107..fa88f59 100644 --- a/Tests/Packaging.Tests.ps1 +++ b/Tests/Packaging.Tests.ps1 @@ -4,13 +4,13 @@ BeforeAll { $package = & $buildScript -OutputDirectory (Join-Path $TestDrive 'output [literal]') $extractRoot = Join-Path $TestDrive 'extracted' [IO.Compression.ZipFile]::ExtractToDirectory($package.ArchivePath, $extractRoot) - $moduleRoot = Join-Path $extractRoot 'IT-ToolBox/3.0.1' + $moduleRoot = Join-Path $extractRoot 'IT-ToolBox/3.1.0' } Describe 'Installable module packaging' { It 'uses the manifest release version and conventional module layout' { - $package.Version | Should -Be '3.0.1' - [IO.Path]::GetFileName($package.ArchivePath) | Should -Be 'IT-ToolBox-3.0.1.zip' + $package.Version | Should -Be '3.1.0' + [IO.Path]::GetFileName($package.ArchivePath) | Should -Be 'IT-ToolBox-3.1.0.zip' Test-ModuleManifest (Join-Path $moduleRoot 'IT-ToolBox.psd1') -ErrorAction Stop | Should -Not -BeNullOrEmpty foreach ($name in @('LICENSE', 'CONTRIBUTING.md', 'SECURITY.md')) { Test-Path -LiteralPath (Join-Path $moduleRoot $name) | Should -BeTrue @@ -45,7 +45,7 @@ Describe 'Installable module packaging' { $code = @" `$ErrorActionPreference = 'Stop' `$env:PSModulePath = '$escapedRoot' + [IO.Path]::PathSeparator + `$env:PSModulePath -`$module = Import-Module IT-ToolBox -RequiredVersion 3.0.1 -Force -PassThru +`$module = Import-Module IT-ToolBox -RequiredVersion 3.1.0 -Force -PassThru New-LogEntry -LogMessage 'Packaged logger' -BufferOnly [pscustomobject]@{ ExportCount = `$module.ExportedFunctions.Count @@ -59,7 +59,7 @@ New-LogEntry -LogMessage 'Packaged logger' -BufferOnly $LASTEXITCODE | Should -Be 0 $result = $output | ConvertFrom-Json $result.ModuleBase | Should -Be $moduleRoot - $result.ExportCount | Should -Be 29 + $result.ExportCount | Should -Be 30 $result.Buffer.Count | Should -Be 1 $result.Buffer[0] | Should -Match '\[INFO\]: Packaged logger$' } diff --git a/docs/Packaging.md b/docs/Packaging.md index 018f906..c71cede 100644 --- a/docs/Packaging.md +++ b/docs/Packaging.md @@ -1,7 +1,7 @@ # Build and install the module -Build from the reviewed 3.0.1 release commit or tag using PowerShell 7.4 or later. -The manifest version is 3.0.1, with no prerelease suffix. Preserve that source +Build from the reviewed 3.1.0 release commit or tag using PowerShell 7.4 or later. +The manifest version is 3.1.0, with no prerelease suffix. Preserve that source revision alongside the archive when distributing it. Run from the repository root: @@ -11,12 +11,12 @@ Invoke-Pester ./Tests ./scripts/Build-Module.ps1 ``` -The script creates artifacts/IT-ToolBox-3.0.1.zip and returns its path, +The script creates artifacts/IT-ToolBox-3.1.0.zip and returns its path, version and file count. It takes the version from the manifest without modifying it. Use -OutputDirectory to choose another local destination. An existing archive causes a terminating error; use a different destination for another build. -The ZIP contains IT-ToolBox/3.0.1/ with the manifest, loader, Public and Private +The ZIP contains IT-ToolBox/3.1.0/ with the manifest, loader, Public and Private implementation files, license, README, changelog, contributing guide, security policy and Markdown documents in docs/. Tests, build scripts, Legacy, Staging, Git metadata and external binaries are @@ -26,8 +26,8 @@ in a separate session; the old decoder is not distributed in this archive. Extract into a new directory, then verify or import the extracted manifest: ```powershell -Expand-Archive -LiteralPath './artifacts/IT-ToolBox-3.0.1.zip' -DestinationPath './module-install' -Import-Module './module-install/IT-ToolBox/3.0.1/IT-ToolBox.psd1' -Force +Expand-Archive -LiteralPath './artifacts/IT-ToolBox-3.1.0.zip' -DestinationPath './module-install' +Import-Module './module-install/IT-ToolBox/3.1.0/IT-ToolBox.psd1' -Force Get-Command -Module IT-ToolBox ```