# PrutCMS v10 — quick smoke test against a running dev server. # Usage: # 1. In one terminal: npm run dev # 2. In another: .\deploy\verify.ps1 # # Hits public + auth-gated endpoints, checks status codes + key strings in # response bodies. Doesn't try to upload — just verifies routing, rendering, # CSP, and signed-URL surface. param( [string]$BaseUrl = 'http://localhost:3000' ) $ErrorActionPreference = 'Stop' $script:pass = 0 $script:fail = 0 function Check { param([string]$Name, [bool]$Ok, [string]$Detail = '') $msg = if ($Detail) { "$Name -- $Detail" } else { $Name } if ($Ok) { Write-Host "[OK] $msg" -ForegroundColor Green $script:pass++ } else { Write-Host "[FAIL] $msg" -ForegroundColor Red $script:fail++ } } function Get-Url { param([string]$Path) try { return Invoke-WebRequest -Uri ($BaseUrl + $Path) -UseBasicParsing -SkipHttpErrorCheck -MaximumRedirection 0 -ErrorAction SilentlyContinue } catch { Write-Host "Request failed: $Path -- $($_.Exception.Message)" -ForegroundColor Yellow return $null } } Write-Host "`n=== PrutCMS verify @ $BaseUrl ===`n" -ForegroundColor Cyan # ── Server up at all? ───────────────────────────────────────────── $home = Get-Url '/' $serverUp = ($home -ne $null) -and ($home.StatusCode -eq 200 -or $home.StatusCode -eq 302) Check 'Server responds on /' $serverUp if (-not $serverUp) { Write-Host "`nServer not reachable. Is 'npm run dev' running?" -ForegroundColor Red exit 1 } # ── Manifest & feeds ────────────────────────────────────────────── $mf = Get-Url '/manifest.webmanifest' Check 'Manifest responds 200' ($mf -ne $null -and $mf.StatusCode -eq 200) $mfJson = $null if ($mf -and $mf.Content) { try { $mfJson = $mf.Content | ConvertFrom-Json } catch {} } Check 'Manifest has scope field' ($mfJson -ne $null -and $mfJson.scope -ne $null) Check 'Manifest id starts prutcms-' ($mfJson -ne $null -and $mfJson.id -like 'prutcms-*') Check 'Manifest scope ends with /' ($mfJson -ne $null -and $mfJson.scope.EndsWith('/')) $feed = Get-Url '/feed.xml' Check 'RSS /feed.xml: 200 + xml' ($feed -ne $null -and $feed.StatusCode -eq 200 -and $feed.Content -like '*20 KB, not the loader stub)' ` ($htmx -ne $null -and $htmx.Content.Length -gt 20000) # ── CSP / security headers ──────────────────────────────────────── $csp = if ($home) { $home.Headers.'Content-Security-Policy' } else { $null } Check 'CSP present' ($csp -ne $null) Check 'CSP no longer references unpkg.com' ($csp -ne $null -and $csp -notlike '*unpkg*') $nosniff = if ($home) { $home.Headers.'X-Content-Type-Options' } else { $null } Check 'Helmet active (X-Content-Type-Options: nosniff)' ($nosniff -eq 'nosniff') # ── Summary ─────────────────────────────────────────────────────── Write-Host "`n────────────────────────────────────────" $summary = "$($script:pass) passed, $($script:fail) failed." if ($script:fail -eq 0) { Write-Host $summary -ForegroundColor Green } else { Write-Host $summary -ForegroundColor Red exit 1 }