Tests/Certificate-LifecycleMonitor.Tests.ps1
|
BeforeAll { $ModuleRoot = Split-Path -Parent $PSScriptRoot Import-Module "$ModuleRoot\Certificate-LifecycleMonitor.psd1" -Force } Describe 'Certificate-LifecycleMonitor Module' { Context 'Module Loading' { It 'Should import without errors' { $module = Get-Module Certificate-LifecycleMonitor $module | Should -Not -BeNullOrEmpty } It 'Should export exactly 5 public functions' { $exported = (Get-Module Certificate-LifecycleMonitor).ExportedFunctions.Keys $exported.Count | Should -Be 5 } It 'Should export Invoke-CertificateAudit' { (Get-Module Certificate-LifecycleMonitor).ExportedFunctions.Keys | Should -Contain 'Invoke-CertificateAudit' } It 'Should export Get-ExpiringCertificates' { (Get-Module Certificate-LifecycleMonitor).ExportedFunctions.Keys | Should -Contain 'Get-ExpiringCertificates' } It 'Should export Get-IISCertificateReport' { (Get-Module Certificate-LifecycleMonitor).ExportedFunctions.Keys | Should -Contain 'Get-IISCertificateReport' } It 'Should export Get-CertificateStoreReport' { (Get-Module Certificate-LifecycleMonitor).ExportedFunctions.Keys | Should -Contain 'Get-CertificateStoreReport' } It 'Should export Get-WeakCertificateReport' { (Get-Module Certificate-LifecycleMonitor).ExportedFunctions.Keys | Should -Contain 'Get-WeakCertificateReport' } } Context 'Manifest Validation' { It 'Should have a valid manifest' { $manifestPath = Join-Path (Split-Path -Parent $PSScriptRoot) 'Certificate-LifecycleMonitor.psd1' { Test-ModuleManifest -Path $manifestPath -ErrorAction Stop } | Should -Not -Throw } It 'Should have the correct GUID' { $manifest = Test-ModuleManifest -Path (Join-Path (Split-Path -Parent $PSScriptRoot) 'Certificate-LifecycleMonitor.psd1') $manifest.Guid.ToString() | Should -Be 'f4a0b5c3-7d69-4e9f-c034-1b5e8f0d9a67' } It 'Should require PowerShell 5.1' { $manifest = Test-ModuleManifest -Path (Join-Path (Split-Path -Parent $PSScriptRoot) 'Certificate-LifecycleMonitor.psd1') $manifest.PowerShellVersion | Should -Be '5.1' } It 'Should have a description' { $manifest = Test-ModuleManifest -Path (Join-Path (Split-Path -Parent $PSScriptRoot) 'Certificate-LifecycleMonitor.psd1') $manifest.Description | Should -Not -BeNullOrEmpty } It 'Should list the correct author' { $manifest = Test-ModuleManifest -Path (Join-Path (Split-Path -Parent $PSScriptRoot) 'Certificate-LifecycleMonitor.psd1') $manifest.Author | Should -BeLike '*Larry Roberts*' } } Context 'Parameter Validation' { It 'Get-ExpiringCertificates should have -ComputerName parameter' { (Get-Command Get-ExpiringCertificates).Parameters.Keys | Should -Contain 'ComputerName' } It 'Get-ExpiringCertificates should have -DaysUntilExpiration parameter' { (Get-Command Get-ExpiringCertificates).Parameters.Keys | Should -Contain 'DaysUntilExpiration' } It 'Get-CertificateStoreReport -StoreName should validate against known stores' { $param = (Get-Command Get-CertificateStoreReport).Parameters['StoreName'] $validateSet = $param.Attributes | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] } $validateSet | Should -Not -BeNullOrEmpty $validateSet.ValidValues | Should -Contain 'My' $validateSet.ValidValues | Should -Contain 'Root' $validateSet.ValidValues | Should -Contain 'CA' $validateSet.ValidValues | Should -Contain 'TrustedPeople' } It 'Get-CertificateStoreReport -StoreLocation should validate against known locations' { $param = (Get-Command Get-CertificateStoreReport).Parameters['StoreLocation'] $validateSet = $param.Attributes | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] } $validateSet | Should -Not -BeNullOrEmpty $validateSet.ValidValues | Should -Contain 'LocalMachine' $validateSet.ValidValues | Should -Contain 'CurrentUser' } It 'Invoke-CertificateAudit should have -DaysWarning parameter defaulting to 30' { $param = (Get-Command Invoke-CertificateAudit).Parameters['DaysWarning'] $param | Should -Not -BeNullOrEmpty } It 'Invoke-CertificateAudit should have -SendEmail switch parameter' { $param = (Get-Command Invoke-CertificateAudit).Parameters['SendEmail'] $param.SwitchParameter | Should -Be $true } It 'Invoke-CertificateAudit should have -IncludeIIS switch parameter' { $param = (Get-Command Invoke-CertificateAudit).Parameters['IncludeIIS'] $param.SwitchParameter | Should -Be $true } It 'Get-WeakCertificateReport should have -MinimumKeyLength parameter' { (Get-Command Get-WeakCertificateReport).Parameters.Keys | Should -Contain 'MinimumKeyLength' } } Context 'Get-ExpiringCertificates -Finding Categorization' { BeforeAll { # Mock Invoke-Command is not needed for localhost -we mock the cert # PSDrive results by mocking Get-ChildItem inside the scriptblock. # Instead, we call the function against a controlled set of objects. $now = Get-Date $mockCerts = @( # Expired 10 days ago [PSCustomObject]@{ Subject = 'CN=expired.contoso.com' Thumbprint = 'AAAA1111BBBB2222CCCC3333DDDD4444EEEE5555' Issuer = 'CN=Contoso CA' NotBefore = $now.AddYears(-2) NotAfter = $now.AddDays(-10) HasPrivateKey = $true PublicKey = $null SignatureAlgorithm = [PSCustomObject]@{ FriendlyName = 'sha256RSA' } } # Critical -expires in 3 days [PSCustomObject]@{ Subject = 'CN=critical.contoso.com' Thumbprint = 'BBBB2222CCCC3333DDDD4444EEEE5555FFFF6666' Issuer = 'CN=Contoso CA' NotBefore = $now.AddYears(-1) NotAfter = $now.AddDays(3) HasPrivateKey = $true PublicKey = $null SignatureAlgorithm = [PSCustomObject]@{ FriendlyName = 'sha256RSA' } } # Warning -expires in 15 days [PSCustomObject]@{ Subject = 'CN=warning.contoso.com' Thumbprint = 'CCCC3333DDDD4444EEEE5555FFFF6666AAAA7777' Issuer = 'CN=Contoso CA' NotBefore = $now.AddYears(-1) NotAfter = $now.AddDays(15) HasPrivateKey = $true PublicKey = $null SignatureAlgorithm = [PSCustomObject]@{ FriendlyName = 'sha256RSA' } } # OK -expires in 200 days [PSCustomObject]@{ Subject = 'CN=healthy.contoso.com' Thumbprint = 'DDDD4444EEEE5555FFFF6666AAAA7777BBBB8888' Issuer = 'CN=Contoso CA' NotBefore = $now.AddYears(-1) NotAfter = $now.AddDays(200) HasPrivateKey = $true PublicKey = $null SignatureAlgorithm = [PSCustomObject]@{ FriendlyName = 'sha256RSA' } } ) Mock -ModuleName Certificate-LifecycleMonitor Invoke-Command { param($ComputerName, $ScriptBlock, $ArgumentList) $StoreName = $ArgumentList[0] $StoreLocation = $ArgumentList[1] $DaysUntilExpiration = $ArgumentList[2] $warningDate = (Get-Date).AddDays($DaysUntilExpiration) $nowInner = Get-Date $mockCerts | ForEach-Object { $daysRemaining = ($_.NotAfter - $nowInner).Days $finding = if ($_.NotAfter -lt $nowInner) { 'EXPIRED' } elseif ($daysRemaining -le 7) { 'CRITICAL' } elseif ($_.NotAfter -le $warningDate) { 'WARNING' } else { 'OK' } [PSCustomObject]@{ Subject = $_.Subject Thumbprint = $_.Thumbprint Issuer = $_.Issuer NotBefore = $_.NotBefore NotAfter = $_.NotAfter DaysRemaining = $daysRemaining Store = "$StoreLocation\$StoreName" ComputerName = $ComputerName Finding = $finding } } } } It 'Should flag expired certificates as EXPIRED' { $results = Get-ExpiringCertificates -ComputerName 'MOCKSERVER' -DaysUntilExpiration 30 $expired = $results | Where-Object Subject -eq 'CN=expired.contoso.com' $expired.Finding | Should -Be 'EXPIRED' } It 'Should flag certificates expiring within 7 days as CRITICAL' { $results = Get-ExpiringCertificates -ComputerName 'MOCKSERVER' -DaysUntilExpiration 30 $critical = $results | Where-Object Subject -eq 'CN=critical.contoso.com' $critical.Finding | Should -Be 'CRITICAL' } It 'Should flag certificates expiring within threshold as WARNING' { $results = Get-ExpiringCertificates -ComputerName 'MOCKSERVER' -DaysUntilExpiration 30 $warning = $results | Where-Object Subject -eq 'CN=warning.contoso.com' $warning.Finding | Should -Be 'WARNING' } It 'Should flag healthy certificates as OK' { $results = Get-ExpiringCertificates -ComputerName 'MOCKSERVER' -DaysUntilExpiration 30 $ok = $results | Where-Object Subject -eq 'CN=healthy.contoso.com' $ok.Finding | Should -Be 'OK' } It 'Should return correct DaysRemaining for expired cert (negative)' { $results = Get-ExpiringCertificates -ComputerName 'MOCKSERVER' -DaysUntilExpiration 30 $expired = $results | Where-Object Subject -eq 'CN=expired.contoso.com' $expired.DaysRemaining | Should -BeLessOrEqual -1 } It 'Should return all four certificates' { $results = Get-ExpiringCertificates -ComputerName 'MOCKSERVER' -DaysUntilExpiration 30 $results.Count | Should -Be 4 } } Context 'Get-WeakCertificateReport -Weakness Detection' { BeforeAll { $now = Get-Date Mock -ModuleName Certificate-LifecycleMonitor Invoke-Command { # Return pre-built weak certificate findings @( [PSCustomObject]@{ Subject = 'CN=sha1-cert.contoso.com' Thumbprint = 'SHA1AAAA1111BBBB2222CCCC3333DDDD4444' KeyLength = 2048 SignatureAlgorithm = 'sha1RSA' SelfSigned = $false Store = 'LocalMachine\My' ComputerName = 'MOCKSERVER' Finding = 'SHA1_SIGNATURE' } [PSCustomObject]@{ Subject = 'CN=weak-key.contoso.com' Thumbprint = 'WEAK1111AAAA2222BBBB3333CCCC4444' KeyLength = 1024 SignatureAlgorithm = 'sha256RSA' SelfSigned = $false Store = 'LocalMachine\My' ComputerName = 'MOCKSERVER' Finding = 'WEAK_KEY (1024-bit < 2048-bit)' } [PSCustomObject]@{ Subject = 'CN=self-signed.contoso.com' Thumbprint = 'SELF1111AAAA2222BBBB3333CCCC4444' KeyLength = 2048 SignatureAlgorithm = 'sha256RSA' SelfSigned = $true Store = 'LocalMachine\My' ComputerName = 'MOCKSERVER' Finding = 'SELF_SIGNED_IN_MY' } [PSCustomObject]@{ Subject = 'CN=Expired Root CA' Thumbprint = 'ROOT1111AAAA2222BBBB3333CCCC4444' KeyLength = 2048 SignatureAlgorithm = 'sha256RSA' SelfSigned = $true Store = 'LocalMachine\Root' ComputerName = 'MOCKSERVER' Finding = 'EXPIRED_ROOT_CA' } ) } } It 'Should flag SHA-1 signatures' { $results = Get-WeakCertificateReport -ComputerName 'MOCKSERVER' $sha1 = $results | Where-Object Subject -eq 'CN=sha1-cert.contoso.com' $sha1.Finding | Should -BeLike '*SHA1*' } It 'Should flag weak key lengths' { $results = Get-WeakCertificateReport -ComputerName 'MOCKSERVER' $weak = $results | Where-Object Subject -eq 'CN=weak-key.contoso.com' $weak.Finding | Should -BeLike '*WEAK_KEY*' $weak.KeyLength | Should -Be 1024 } It 'Should flag self-signed certificates in My store' { $results = Get-WeakCertificateReport -ComputerName 'MOCKSERVER' $selfSigned = $results | Where-Object Subject -eq 'CN=self-signed.contoso.com' $selfSigned.Finding | Should -BeLike '*SELF_SIGNED*' $selfSigned.SelfSigned | Should -Be $true } It 'Should flag expired root CA certificates' { $results = Get-WeakCertificateReport -ComputerName 'MOCKSERVER' $expiredRoot = $results | Where-Object Subject -eq 'CN=Expired Root CA' $expiredRoot.Finding | Should -BeLike '*EXPIRED_ROOT*' } It 'Should return all four weak certificate findings' { $results = Get-WeakCertificateReport -ComputerName 'MOCKSERVER' $results.Count | Should -Be 4 } } } |