geenforce: harden allowlist + fix share-less kiosk client and display scope
- allowlist auth uses remote_addr, not the spoofable first X-Forwarded-For hop (adds _trusted_client_ip + a regression test); rate-limit path unchanged - client psm1: fix Set-StrictMode crashes reading absent keys in Get-ShopdbConfig (token-less mode) and Resolve-ShopdbPayloads (no-payload entries); validate the manifest response is JSON before overwriting the last-known-good cache - runner: pass the engine its required -InstallerRoot/-LogFile; create the log directory so enforce logging is not silently lost on a fresh kiosk - display scope: dispatcher writes an all-users Startup shortcut instead of Start-Process (SYSTEM cannot show a window in session 0), resolves the base URL from HKLM, and adds an always-on power/no-lock entry; tests updated for the 6-entry scope
This commit is contained in:
@@ -55,7 +55,13 @@ param(
|
||||
function Write-Log {
|
||||
param([string]$Message, [string]$Level = 'INFO')
|
||||
$line = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') [$Level] $Message"
|
||||
try { Add-Content -LiteralPath $LogFile -Value $line -ErrorAction SilentlyContinue } catch {}
|
||||
try {
|
||||
$logDir = Split-Path -Parent $LogFile
|
||||
if ($logDir -and -not (Test-Path $logDir)) {
|
||||
New-Item -ItemType Directory -Path $logDir -Force -ErrorAction SilentlyContinue | Out-Null
|
||||
}
|
||||
Add-Content -LiteralPath $LogFile -Value $line -ErrorAction SilentlyContinue
|
||||
} catch {}
|
||||
Write-Host $line
|
||||
}
|
||||
|
||||
@@ -164,8 +170,23 @@ try {
|
||||
# code, or emit several objects. ConvertTo-ShopdbSummary adapts whatever it
|
||||
# returns into a well-formed summary hashtable so the report stage always
|
||||
# gets clean input (we do NOT assume the engine was fixed).
|
||||
# The engine requires -InstallerRoot (base for any relative Source/Installer
|
||||
# path) and -LogFile. Shadow runs off the share, so relative paths resolve
|
||||
# against the share scope dir. Cutover rewrites payloads to ABSOLUTE local
|
||||
# paths, so InstallerRoot is only a harmless fallback base (the payload cache).
|
||||
if ($ShadowMode -and $ShareManifestPath) {
|
||||
$installerRoot = Split-Path -Parent $ShareManifestPath
|
||||
} else {
|
||||
$installerRoot = Join-Path (Split-Path -Parent $manifestToRun) 'payloads'
|
||||
}
|
||||
if ($installerRoot -and -not (Test-Path $installerRoot)) {
|
||||
New-Item -ItemType Directory -Path $installerRoot -Force -ErrorAction SilentlyContinue | Out-Null
|
||||
}
|
||||
$engineLog = $LogFile -replace '\.log$', '-engine.log'
|
||||
|
||||
Write-Log "Running engine against $manifestToRun"
|
||||
$engineResult = & $EnginePath -ManifestPath $manifestToRun -PCType $Scope
|
||||
$engineResult = & $EnginePath -ManifestPath $manifestToRun -PCType $Scope `
|
||||
-InstallerRoot $installerRoot -LogFile $engineLog
|
||||
$summary = ConvertTo-ShopdbSummary -EngineResult $engineResult
|
||||
|
||||
# Report the result (best-effort).
|
||||
|
||||
@@ -63,8 +63,10 @@ function Get-ShopdbConfig {
|
||||
$regPath = 'HKLM:\SOFTWARE\GE\ShopDB'
|
||||
if ((-not $BaseUrl -or -not $ApiToken) -and (Test-Path $regPath)) {
|
||||
$props = Get-ItemProperty -Path $regPath -ErrorAction SilentlyContinue
|
||||
if (-not $BaseUrl -and $props.BaseUrl) { $BaseUrl = $props.BaseUrl }
|
||||
if (-not $ApiToken -and $props.ApiToken) { $ApiToken = $props.ApiToken }
|
||||
$regBase = Get-ShopdbProperty $props 'BaseUrl'
|
||||
$regTok = Get-ShopdbProperty $props 'ApiToken'
|
||||
if (-not $BaseUrl -and $regBase) { $BaseUrl = [string]$regBase }
|
||||
if (-not $ApiToken -and $regTok) { $ApiToken = [string]$regTok }
|
||||
}
|
||||
# ApiToken is OPTIONAL: on a vaulted network the server may authorize by
|
||||
# source-IP allowlist, so a BaseUrl alone is a valid config. When a token
|
||||
@@ -107,6 +109,10 @@ function Sync-ShopdbManifest {
|
||||
$response = Invoke-WebRequest -Uri $uri -Headers $headers -UseBasicParsing `
|
||||
-TimeoutSec 30 -ErrorAction Stop
|
||||
if ($response.StatusCode -eq 200) {
|
||||
# Validate JSON before overwriting the last-known-good cache: a proxy
|
||||
# or IIS error page served as 200 must not clobber the fallback.
|
||||
try { $null = ($response.Content | ConvertFrom-Json) }
|
||||
catch { throw "manifest response for $Scope was not valid JSON" }
|
||||
[System.IO.File]::WriteAllText($manifestPath, $response.Content)
|
||||
# PowerShell 7 returns header values as string arrays; 5.1 as scalars.
|
||||
# @(...)[0] yields a clean scalar in both.
|
||||
@@ -122,7 +128,8 @@ function Sync-ShopdbManifest {
|
||||
}
|
||||
} catch {
|
||||
$status = $null
|
||||
if ($_.Exception.Response) { $status = [int]$_.Exception.Response.StatusCode }
|
||||
$exResponse = Get-ShopdbProperty $_.Exception 'Response'
|
||||
if ($exResponse) { $status = [int]$exResponse.StatusCode }
|
||||
if ($status -eq 304 -and (Test-Path $manifestPath)) {
|
||||
return @{ Path = $manifestPath; Version = (Read-CachedVersion $CacheDir $Scope); Source = 'cache-304' }
|
||||
}
|
||||
@@ -298,12 +305,13 @@ function Resolve-ShopdbPayloads {
|
||||
INF='Installer'; PS1='Script'; File='Source' }
|
||||
$changed = $false
|
||||
foreach ($entry in @($json.Applications)) {
|
||||
$src = [string]$entry.PayloadSource
|
||||
$sha = [string]$entry.PayloadSha256
|
||||
$src = [string](Get-ShopdbProperty $entry 'PayloadSource')
|
||||
$sha = [string](Get-ShopdbProperty $entry 'PayloadSha256')
|
||||
if (-not $sha -or ($src -ne 'http' -and $src -ne 'inline')) { continue }
|
||||
$field = $pathField[[string]$entry.Type]
|
||||
$field = $pathField[[string](Get-ShopdbProperty $entry 'Type')]
|
||||
if (-not $field) { continue }
|
||||
$local = Get-ShopdbPayload -Sha256 $sha -Config $Config -Filename $entry.PayloadRef -CacheDir $CacheDir
|
||||
$ref = Get-ShopdbProperty $entry 'PayloadRef'
|
||||
$local = Get-ShopdbPayload -Sha256 $sha -Config $Config -Filename $ref -CacheDir $CacheDir
|
||||
if (-not $local) { throw "payload $sha for '$($entry.Name)' could not be fetched/verified" }
|
||||
if ($entry.PSObject.Properties.Name -contains $field) { $entry.$field = $local }
|
||||
else { $entry | Add-Member -NotePropertyName $field -NotePropertyValue $local }
|
||||
|
||||
Reference in New Issue
Block a user