Merge pull request 'DEV-000049: audit production advisory boundary' (#22) from codex/post-advisory-production-audit-v1 into main
CI / test (push) Successful in 11s

This commit was merged in pull request #22.
This commit is contained in:
m
2026-08-16 17:31:43 +00:00
4 changed files with 115 additions and 4 deletions
+3 -3
View File
@@ -1,5 +1,5 @@
{
"task_file": "dev/tasks/DEV-000048-creator-advisory-console.json",
"task_id": "DEV-000048",
"task_revision": 2
"task_file": "dev/tasks/DEV-000049-post-advisory-production-audit.json",
"task_id": "DEV-000049",
"task_revision": 1
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000049",
"task_revision": 1,
"architecture_revision": "ADR-0006",
"base_repo_revision": "4de34ad8f8389d28b1120a3c6203ea32b8c7ff22",
"risk_tier": "E",
"intent": "Add and execute a repeatable read-only production audit for the authenticated Creator advisory boundary after DEV-000048 promotion, proving contract limits, authority-field rejection, explicit degraded behavior and cognitive-worker isolation without selecting or calling an external provider.",
"requirement_ids": ["ADR-0006", "DEV-000046", "DEV-000048", "INV-AUTH-001", "INV-EPI-001"],
"allowed_change_scope": {"modules": [], "paths": ["tests/cognitive_advisory/**", "docs/PROJECT_STATUS.md", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000049-post-advisory-production-audit.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "ci/**", ".gitea/**", "contracts/**", "modules/**", "adapters/**", "cmd/**", "database/**", "deploy/**", "security/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0},
"contracts": {"consume": ["creator-advisory-v1", "CreatorSession", "Situation FocusBinding"], "may_change": []},
"semantic_owner": "none-audit-only",
"canonical_state": [],
"state_transitions": [],
"side_effects_and_privileges": ["read production runtime/container metadata", "issue an ephemeral Creator session and advisory request", "no database mutation, external provider egress or credential persistence"],
"persistence_and_migrations": [],
"failure_retry_restart": ["audit fails closed on missing focus or credential", "audit never prints credentials or session tokens", "advisory content remains ephemeral and is not retried"],
"assumptions": ["production contains at least one active Creator-owned FocusBinding", "Creator credential remains mounted as a Docker secret", "docker-test remains prohibited"],
"acceptance_scenarios": ["authenticated 16 KiB advisory returns only PROVIDER_UNAVAILABLE with empty text and citations", "unauthenticated advisory is denied", "browser authority-field injection is rejected", "the audit sentinel is absent from cognitive-worker logs", "cognitive worker has no database, Commit/Executor, Creator or provider credential environment/mount", "Creator console exposes the advisory UI", "production revision and health remain valid"],
"required_tests": ["PowerShell parse", "architecture preflight", "exact diff scope", "production advisory audit", "production artifact confirmation", "production health", "CI"],
"terminal_gap_states": ["BLOCKED_BY_ARCHITECTURE_GAP", "BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_CONFLICT"]
}
+3 -1
View File
@@ -1,6 +1,6 @@
# Assistant project status
Audit date: 2026-08-16. Active audit: DEV-000044 revision 3.
Audit date: 2026-08-16. Active audit: DEV-000049 revision 1.
## Delivered
@@ -28,4 +28,6 @@ DEV-000045 closes the DEV-000044 artifact-provenance finding for new promotions
ADR-0006 and DEV-000046 establish the provider-neutral, non-canonical Creator advisory boundary. DEV-000048 connects it to authenticated Creator Web and adds the advisory console; until a provider/data-egress decision is accepted, it deliberately reports `PROVIDER_UNAVAILABLE` and sends nothing outside the private runtime topology.
DEV-000049 adds and executes a repeatable production audit for that boundary. It proves that an authenticated 16 KiB message reaches only the deterministic unavailable binding, unauthenticated and browser-supplied authority fields fail closed, the request sentinel is absent from cognitive-worker logs, and the cognitive worker has no database, Commit/Executor, Creator or provider credential environment/mount.
These are explicit scope decisions, not hidden incomplete steps in DEV-000043. No production secret is stored in the repository.
@@ -0,0 +1,86 @@
[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
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
$forbiddenEnvironment = @($cognitiveInspect[0].Config.Env | Where-Object { ($_ -split '=', 2)[0] -match '(?i)(POSTGRES|DATABASE|COMMIT|EXECUTOR|CREATOR|PASSWORD|TOKEN|PROVIDER|API_KEY)' })
if ($forbiddenEnvironment.Count -ne 0) { throw 'Cognitive worker has forbidden privileged/provider environment' }
$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'