[CmdletBinding()] param( [string]$DockerContext = 'docker-prod', [string]$BaseURL = 'https://assistent.cin.su', [string]$CoreContainer = 'assistant-core-runtime-1', [string]$PostgresContainer = 'assistant-postgres-1', [string]$CognitiveContainer = 'assistant-cognitive-worker-1' ) $ErrorActionPreference = 'Stop' Set-StrictMode -Version Latest . "$PSScriptRoot/cognitive_worker_env_policy.ps1" function Get-ContainerEnvironment([string]$Container) { $result = @{} $inspect = ((& docker.exe --context $DockerContext inspect $Container) -join "`n") | ConvertFrom-Json foreach ($entry in $inspect[0].Config.Env) { $parts = $entry -split '=', 2 $result[$parts[0]] = if ($parts.Count -eq 2) { $parts[1] } else { '' } } return $result } function Invoke-StatusRequest([string]$Uri, [hashtable]$Headers, [string]$Body) { $parameters = @{ Uri = $Uri Method = 'Post' ContentType = 'application/json' SkipHttpErrorCheck = $true } if ($Headers.Count -gt 0) { $parameters.Headers = $Headers } if ($Body) { $parameters.Body = $Body } return Invoke-WebRequest @parameters } $health = Invoke-RestMethod -Uri "$BaseURL/healthz" -Method Get if ($health.status -ne 'ok' -or $health.runtime -ne 'creator-web') { throw 'Creator Web health is invalid' } $page = Invoke-WebRequest -Uri "$BaseURL/" -Method Get if ($page.StatusCode -ne 200 -or $page.Content -notmatch 'Спросить Домового') { throw 'Advisory console is not exposed' } $coreEnvironment = Get-ContainerEnvironment $CoreContainer $required = @('CREATOR_USERNAME', 'CREATOR_PASSWORD_FILE', 'POSTGRES_DATABASE', 'POSTGRES_USER', 'POSTGRES_PASSWORD') foreach ($name in $required) { if (-not $coreEnvironment.ContainsKey($name) -or [string]::IsNullOrWhiteSpace($coreEnvironment[$name])) { throw "Core runtime is missing $name" } } $creatorPassword = ((& docker.exe --context $DockerContext exec $CoreContainer sh -c "cat '$($coreEnvironment['CREATOR_PASSWORD_FILE'])'") -join "`n").Trim() if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($creatorPassword)) { throw 'Creator password secret is unavailable' } $sessionRequest = @{username = $coreEnvironment['CREATOR_USERNAME']; password = $creatorPassword} | ConvertTo-Json -Compress $issued = Invoke-RestMethod -Uri "$BaseURL/api/session" -Method Post -ContentType 'application/json' -Body $sessionRequest $creatorPassword = $null if (-not $issued.Token) { throw 'Creator session was not issued' } $authorization = @{Authorization = "Bearer $($issued.Token)"} $focusSql = "select session_id,workspace_id from situation.focus_bindings where active=true and actor_id='domovoy-creator-actor' order by session_id desc limit 1" $focusLine = ((& docker.exe --context $DockerContext exec --env "PGPASSWORD=$($coreEnvironment['POSTGRES_PASSWORD'])" $PostgresContainer psql -U $coreEnvironment['POSTGRES_USER'] -d $coreEnvironment['POSTGRES_DATABASE'] -AtF '|' -c $focusSql) -join '').Trim() if ($LASTEXITCODE -ne 0 -or $focusLine -notmatch '^[-a-zA-Z0-9._:]+\|[-a-zA-Z0-9._:]+$') { throw 'Active Creator focus is unavailable' } $focus = $focusLine -split '\|', 2 $sentinelPrefix = "audit-$([guid]::NewGuid().ToString('N'))-" $message = $sentinelPrefix + ('a' * ((16 * 1024) - $sentinelPrefix.Length)) $advisoryBody = @{message = $message; session_id = $focus[0]; workspace_id = $focus[1]} | ConvertTo-Json -Compress $advisory = Invoke-RestMethod -Uri "$BaseURL/api/advisory" -Method Post -ContentType 'application/json' -Headers $authorization -Body $advisoryBody if ($advisory.Version -ne 'creator-advisory-v1' -or $advisory.FinishReason -ne 'PROVIDER_UNAVAILABLE') { throw 'Advisory did not fail closed' } if (-not [string]::IsNullOrEmpty([string]$advisory.AdvisoryText) -or ($null -ne $advisory.ReferencedExcerptIDs -and @($advisory.ReferencedExcerptIDs).Count -ne 0)) { throw 'Unavailable advisory contained generated content or citations' } $unauthenticated = Invoke-StatusRequest "$BaseURL/api/advisory" @{} $advisoryBody if ($unauthenticated.StatusCode -ne 401) { throw "Unauthenticated advisory status=$($unauthenticated.StatusCode)" } $injectedBody = @{message = 'authority-injection'; session_id = $focus[0]; workspace_id = $focus[1]; ActorID = 'attacker'} | ConvertTo-Json -Compress $injected = Invoke-StatusRequest "$BaseURL/api/advisory" $authorization $injectedBody if ($injected.StatusCode -ne 400) { throw "Authority-field injection status=$($injected.StatusCode)" } $cognitiveInspect = ((& docker.exe --context $DockerContext inspect $CognitiveContainer) -join "`n") | ConvertFrom-Json Test-CognitiveWorkerEnvironmentPolicy -Environment @($cognitiveInspect[0].Config.Env) $forbiddenMounts = @($cognitiveInspect[0].Mounts | Where-Object { $_.Destination -match '(?i)(secret|credential|token|postgres|docker)' }) if ($forbiddenMounts.Count -ne 0) { throw 'Cognitive worker has a forbidden credential or infrastructure mount' } $recentLogs = ((& docker.exe --context $DockerContext logs --since 5m $CognitiveContainer 2>&1) -join "`n") if ($recentLogs.Contains($sentinelPrefix)) { throw 'Advisory content leaked into cognitive-worker logs' } $issued.Token = $null $authorization.Clear() $message = $null $advisoryBody = $null Write-Output 'DEV-000049 production Creator advisory boundary audit: PASS'