Public/Test-KritHardenPrereqs.ps1

function Test-KritHardenPrereqs {
    <#
    .SYNOPSIS
        7-gate prereq check for the Kritical Hardening toolkit. Read-only.

    .DESCRIPTION
        Gates:
          P1 OS family supported (Windows for v1; macOS/Linux deferred to v1.1)
          P2 PowerShell version >= 7.4 (recommended for HotCakeX module compatibility)
          P3 Process is elevated / running as Administrator
          P4 Microsoft Defender service Running + signature recent
          P5 TPM 2.0 present + ready (warning only; not a hard fail)
          P6 SecureBoot enabled (warning only)
          P7 WinRM / PSRemoting reachable (info only)

        Returns a structured object so a supervisor / scheduled task / CI runner
        can branch on .Ok and per-gate detail.

    .EXAMPLE
        Test-KritHardenPrereqs

    .EXAMPLE
        $r = Test-KritHardenPrereqs -Quiet
        if (-not $r.Ok) { throw "Hardening prereqs failed" }

    .NOTES
        Author: Joshua Finley - Kritical Pty Ltd
    #>

    [CmdletBinding()]
    [OutputType([pscustomobject])]
    param([switch] $Quiet, [switch] $NoBanner)

    if (-not $NoBanner.IsPresent -and -not $Quiet.IsPresent) {
        Write-KritHardenBanner -Title 'Hardening Prereq Probe' -Compact
    }

    $gates = [System.Collections.Generic.List[pscustomobject]]::new()
    $add = { param($n,$p,$severity,$d) $gates.Add([pscustomobject]@{ Gate=$n; Pass=[bool]$p; Severity=$severity; Detail=$d }) }

    # P1 Windows (v1 only)
    $rti = [System.Runtime.InteropServices.RuntimeInformation]
    $os  = [System.Runtime.InteropServices.OSPlatform]
    $isWindows = $rti::IsOSPlatform($os::Windows)
    $p1Detail = if ($isWindows) { 'Windows detected' } else { 'macOS/Linux deferred to v1.1' }
    & $add 'P1.Windows' $isWindows 'Critical' $p1Detail

    # P2 PS version >= 7.4 recommended
    $psv = $PSVersionTable.PSVersion
    $psOk = ($psv.Major -gt 7) -or ($psv.Major -eq 7 -and $psv.Minor -ge 4)
    & $add 'P2.PSVersion' $psOk 'Warning' ("PS $psv (>= 7.4 recommended for HotCakeX module)")

    # P3 Admin
    $admin = $false
    if ($isWindows) {
        try {
            $cur = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
            $admin = $cur.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
        } catch { }
    }
    $p3Detail = if ($admin) { 'elevated' } else { 'not elevated - hardening probes need admin' }
    & $add 'P3.Admin' $admin 'Critical' $p3Detail

    # P4 Defender running + recent sigs
    $defOk = $false; $defDetail = 'not probed (non-Windows)'
    if ($isWindows) {
        try {
            $svc = Get-Service -Name 'WinDefend' -ErrorAction Stop
            $sigAge = $null
            try {
                $mp = Get-MpComputerStatus -ErrorAction Stop
                $sigAge = if ($mp.AntivirusSignatureAge) { [int]$mp.AntivirusSignatureAge } else { $null }
            } catch { }
            $defOk = ($svc.Status -eq 'Running') -and ($sigAge -ne $null -and $sigAge -le 7)
            $defDetail = "WinDefend=$($svc.Status); signature age days=$sigAge"
        } catch { $defDetail = "Defender probe failed: $($_.Exception.Message)" }
    }
    & $add 'P4.Defender' $defOk 'Warning' $defDetail

    # P5 TPM 2.0 (warning)
    $tpmOk = $false; $tpmDetail = 'not probed'
    if ($isWindows) {
        try {
            $tpm = Get-Tpm -ErrorAction Stop
            $tpmOk = ($tpm.TpmPresent -and $tpm.TpmReady -and ($tpm.ManufacturerVersionInfo -or $tpm.LockoutCount -ne $null))
            $tpmDetail = "Present=$($tpm.TpmPresent); Ready=$($tpm.TpmReady)"
        } catch { $tpmDetail = "Get-Tpm failed: $($_.Exception.Message)" }
    }
    & $add 'P5.Tpm' $tpmOk 'Warning' $tpmDetail

    # P6 SecureBoot (warning)
    $sbOk = $false; $sbDetail = 'not probed'
    if ($isWindows) {
        try {
            $sb = Confirm-SecureBootUEFI -ErrorAction Stop
            $sbOk = [bool]$sb; $sbDetail = "SecureBoot=$sb"
        } catch { $sbDetail = "Confirm-SecureBootUEFI failed: $($_.Exception.Message)" }
    }
    & $add 'P6.SecureBoot' $sbOk 'Warning' $sbDetail

    # P7 WinRM (info only)
    $wsOk = $false; $wsDetail = 'not probed'
    if ($isWindows) {
        try { Test-WSMan -ComputerName localhost -ErrorAction Stop | Out-Null; $wsOk = $true; $wsDetail = 'WinRM reachable' }
        catch { $wsDetail = "Test-WSMan failed: $($_.Exception.Message)" }
    }
    & $add 'P7.WinRM' $wsOk 'Info' $wsDetail

    $criticalFails = @($gates | Where-Object { $_.Severity -eq 'Critical' -and -not $_.Pass })
    $warningFails  = @($gates | Where-Object { $_.Severity -eq 'Warning'  -and -not $_.Pass })
    $ok = ($criticalFails.Count -eq 0)

    if (-not $Quiet.IsPresent) {
        $gates | Format-Table -AutoSize | Out-String | Write-Host
        if ($ok) {
            Write-Host ("Prereqs OK (Critical=0 fail, Warning=$($warningFails.Count) fail, Info-only items as reported).") -ForegroundColor Green
        } else {
            Write-Host ("Prereqs FAILED ($($criticalFails.Count) critical gate(s) failed).") -ForegroundColor Red
        }
    }
    $platform = $null
    if (Get-Command Get-KritPlatform -ErrorAction SilentlyContinue) { $platform = Get-KritPlatform }
    [pscustomobject]@{
        Ok            = $ok
        CriticalFails = $criticalFails.Count
        WarningFails  = $warningFails.Count
        Gates         = @($gates)
        Platform      = $platform
    }
}