Files
SilverMetal/windows/hardening/Verify-SilverMetalWindows.ps1
sysadmin 3a30a0421e docs(windows): add ISO-builder design + scaffold the windows/ tree
Add windows/iso-builder.md: reproducible custom-packed-ISO pipeline design for
SilverMetal Enhanced - Windows on IoT Enterprise LTSC. Covers the licensing
frame (IoT = blessed channel for preinstalled custom images; self-apply stays a
builder), 7 build stages (verify/extract/DISM-service/inject-unattend/brand/
oscdimg-repack/attest), the offline-vs-first-boot-vs-firmware control split, an
honest reproducibility scope (pinned inputs + SBOM + attestation, NOT bit-
identical on Windows), and M0-M4 milestones.

Scaffold windows/ per the planned layout:
- installer/  build.ps1 (7-stage orchestrator, stages stubbed to M2),
              inputs.manifest.json (pinned-input schema), autounattend.xml
              (local-account OOBE), oem/SetupComplete.cmd (first-boot runner)
- hardening/  shared §A-H PowerShell modules + Verify-SilverMetalWindows.ps1
              (used by BOTH the ISO first-boot path and the self-apply track).
              BitLocker module enforces TPM+PIN and blocks TPM-only.
- policies/ wdac/ debloat/ stack-installer/ drivers/ tests/  scaffolded with
  READMEs; wdac/ documents audit->enforce; debloat/ flags Tiny11/NTLite as an
  anti-pattern; rename applocker/ -> wdac/ realised.

All 11 PowerShell scripts parse clean; manifest JSON + autounattend XML valid.
Module bodies are M1 scaffold (safe: log + policy-set; interactive/firmware
steps documented, not faked).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 15:35:13 +01:00

60 lines
3.0 KiB
PowerShell

#Requires -Version 5.1
<# SilverMetal Enhanced - Windows | Verification
Asserts the hardening-spec.md verification gates and emits a report. Evidence
before assertions: each check reports the observed value, not a claim.
Spec: ../hardening-spec.md (§6) | SCAFFOLD (M1): fill remaining gates.
Exit code 0 = all pass; non-zero = count of failed gates.
#>
[CmdletBinding()] param([string]$ReportPath = "$env:ProgramData\SilverMetal\verify-report.json")
Set-StrictMode -Version Latest; $ErrorActionPreference = 'Continue'
$results = [ordered]@{}
function Test-Gate { param([string]$Name,[scriptblock]$Check)
try { $v = & $Check; $results[$Name] = @{ pass = [bool]$v.pass; detail = $v.detail } }
catch { $results[$Name] = @{ pass = $false; detail = "error: $_" } }
}
Test-Gate 'Telemetry=Security(0)' {
$v = (Get-ItemProperty 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\DataCollection' -Name AllowTelemetry -EA Stop).AllowTelemetry
@{ pass = ($v -eq 0); detail = "AllowTelemetry=$v" }
}
Test-Gate 'BitLocker XtsAes256 + TpmPin' {
$bl = Get-BitLockerVolume -MountPoint $env:SystemDrive -EA Stop
$hasPin = ($bl.KeyProtector.KeyProtectorType -contains 'TpmPin')
@{ pass = ($bl.ProtectionStatus -eq 'On' -and $bl.EncryptionMethod -eq 'XtsAes256' -and $hasPin)
detail = "$($bl.ProtectionStatus)/$($bl.EncryptionMethod)/TpmPin=$hasPin" }
}
Test-Gate 'VBS + HVCI + CredentialGuard running' {
$dg = Get-CimInstance -ClassName Win32_DeviceGuard -Namespace root\Microsoft\Windows\DeviceGuard -EA Stop
$svc = $dg.SecurityServicesRunning
@{ pass = (($svc -contains 1) -and ($svc -contains 2)); detail = "running=$($svc -join ',')" }
}
Test-Gate 'WDAC enforced' {
$dg = Get-CimInstance -ClassName Win32_DeviceGuard -Namespace root\Microsoft\Windows\DeviceGuard -EA Stop
@{ pass = ($dg.CodeIntegrityPolicyEnforcementStatus -eq 2)
detail = "enforcement=$($dg.CodeIntegrityPolicyEnforcementStatus) (2=enforced,1=audit)" }
}
Test-Gate 'Secure Boot enabled' {
@{ pass = (Confirm-SecureBootUEFI); detail = 'Confirm-SecureBootUEFI' }
}
Test-Gate 'Firewall default-deny inbound' {
$p = Get-NetFirewallProfile -EA Stop
@{ pass = -not ($p.DefaultInboundAction -contains 'Allow'); detail = ($p.DefaultInboundAction -join ',') }
}
Test-Gate 'Windows Update healthy' {
$s = Get-Service wuauserv -EA Stop
@{ pass = ($s.StartType -ne 'Disabled'); detail = "$($s.Status)/$($s.StartType)" }
}
# TODO-M1: VPN kill-switch egress test; telemetry-leak capture; Stack functional check.
$pass = ($results.Values | Where-Object { $_.pass }).Count
$fail = ($results.Values | Where-Object { -not $_.pass }).Count
New-Item (Split-Path $ReportPath) -ItemType Directory -Force -EA SilentlyContinue | Out-Null
$results | ConvertTo-Json -Depth 4 | Set-Content $ReportPath
$results.GetEnumerator() | ForEach-Object {
$c = if ($_.Value.pass) { 'Green' } else { 'Red' }
Write-Host ("[{0}] {1} - {2}" -f ($(if($_.Value.pass){'PASS'}else{'FAIL'}), $_.Key, $_.Value.detail)) -ForegroundColor $c
}
Write-Host "`n$pass passed, $fail failed. Report: $ReportPath"
exit $fail