diff --git a/agent99-contract-check.ps1 b/agent99-contract-check.ps1 index 46f850946..b004017e5 100644 --- a/agent99-contract-check.ps1 +++ b/agent99-contract-check.ps1 @@ -177,6 +177,17 @@ $signozMetadataExecutorContract = [bool]( $signozMetadataExecutor.Contains('$CredentialFile = "/etc/awoooi/secrets/signoz-metadata-api-key"') -and $signozMetadataExecutor.Contains('windows99_agent99_to_host110_fixed_metadata_toolchain') -and $signozMetadataExecutor.Contains('credential_reference_absent') -and + $signozMetadataExecutor.Contains('credential_reference_readback_unverified') -and + $signozMetadataExecutor.Contains('preflight_transport_unverified') -and + $signozMetadataExecutor.Contains('agent99_credential:present:%u:%a:%s') -and + $signozMetadataExecutor.Contains('agent99_path_state:$ExpectedKind') -and + $signozMetadataExecutor.Contains('agent99_hashes_begin') -and + $signozMetadataExecutor.Contains('agent99_hashes_end') -and + $signozMetadataExecutor.Contains('agent99_image:') -and + $signozMetadataExecutor.Contains('agent99_runtime:') -and + $signozMetadataExecutor.Contains('agent99_health:') -and + $signozMetadataExecutor.Contains('agent99_active:') -and + $signozMetadataExecutor.Contains('$RemoteResult.completed -ne $true') -and $signozMetadataExecutor.Contains('Get-Agent99RuntimeSourceBinding') -and $signozMetadataExecutor.Contains('$result.fileCount -eq 18') -and $signozMetadataExecutor.Contains('pass_export_verified_restore_pending') -and @@ -186,6 +197,7 @@ $signozMetadataExecutorContract = [bool]( $signozMetadataExecutor.Contains('secretValueTransmitted = $false') -and $signozMetadataExecutor.Contains('arbitraryCommandAllowed = $false') -and $signozMetadataExecutor.Contains('controlledApplyPhases = [pscustomobject]$controlledApplyPhases') -and + -not $signozMetadataExecutor.Contains('$process.ExitCode') -and -not $signozMetadataExecutor.Contains('Invoke-Expression') -and -not $signozMetadataExecutor.Contains('Get-Content -LiteralPath $CredentialFile') ) diff --git a/agent99-signoz-metadata-executor.ps1 b/agent99-signoz-metadata-executor.ps1 index b193592d5..57d328b27 100644 --- a/agent99-signoz-metadata-executor.ps1 +++ b/agent99-signoz-metadata-executor.ps1 @@ -92,16 +92,21 @@ function Invoke-Agent99SignozFixedRemote { -RedirectStandardError $stderrPath -PassThru if (-not $process.WaitForExit($TimeoutSeconds * 1000)) { try { & taskkill.exe /PID $process.Id /T /F 2>&1 | Out-Null } catch {} - return [pscustomobject]@{ ok = $false; exitCode = $null; errorCode = "remote_timeout"; stdout = "" } + return [pscustomobject]@{ + completed = $false + timedOut = $true + errorCode = "remote_timeout" + stdout = "" + } } $process.WaitForExit() | Out-Null $stdout = if (Test-Path -LiteralPath $stdoutPath) { ([IO.File]::ReadAllText($stdoutPath, [Text.Encoding]::UTF8)).Trim() } else { "" } return [pscustomobject]@{ - ok = [bool]($process.ExitCode -eq 0) - exitCode = [int]$process.ExitCode - errorCode = if ($process.ExitCode -eq 0) { "" } else { "fixed_remote_command_failed" } + completed = $true + timedOut = $false + errorCode = "" stdout = $stdout } } finally { @@ -112,9 +117,40 @@ function Invoke-Agent99SignozFixedRemote { } } +function Test-Agent99SignozTransportOutcome { + param([object]$Result) + + return [bool]( + $Result -and + $Result.completed -eq $true -and + -not $Result.timedOut + ) +} + +function New-Agent99SignozPathStateCommand { + param( + [Parameter(Mandatory = $true)][string]$Path, + [ValidateSet("directory", "file")][string]$ExpectedKind + ) + + $kindTest = if ($ExpectedKind -eq "directory") { "-d" } else { "-f" } + return ( + "sudo -n /bin/bash -c 'if [ -L `"$Path`" ]; then " + + "/usr/bin/printf `"agent99_path_state:symlink`"; elif [ $kindTest `"$Path`" ]; then " + + "/usr/bin/printf `"agent99_path_state:$ExpectedKind`"; elif [ -e `"$Path`" ]; then " + + "/usr/bin/printf `"agent99_path_state:other`"; else " + + "/usr/bin/printf `"agent99_path_state:absent`"; fi'" + ) +} + function Convert-Agent99JsonReceipt { param([object]$RemoteResult) - if (-not $RemoteResult -or -not [string]$RemoteResult.stdout) { return $null } + if ( + -not $RemoteResult -or + $RemoteResult.completed -ne $true -or + $RemoteResult.timedOut -or + -not [string]$RemoteResult.stdout + ) { return $null } try { return ([string]$RemoteResult.stdout | ConvertFrom-Json) } catch { @@ -219,14 +255,35 @@ function Get-Agent99SignozPreflight { [bool]$RequireCredential ) - $credential = Invoke-Agent99SignozFixedRemote ( - "sudo -n /usr/bin/stat -c %u:%a:%s $CredentialFile" + $credentialCommand = ( + "sudo -n /bin/bash -c 'if [ -L `"$CredentialFile`" ]; then " + + "/usr/bin/printf `"agent99_credential:symlink`"; elif [ -f `"$CredentialFile`" ]; then " + + "/usr/bin/stat -c `"agent99_credential:present:%u:%a:%s`" `"$CredentialFile`"; " + + "elif [ -e `"$CredentialFile`" ]; then /usr/bin/printf `"agent99_credential:other`"; " + + "else /usr/bin/printf `"agent99_credential:absent`"; fi'" ) + $credential = Invoke-Agent99SignozFixedRemote $credentialCommand + $credentialOutcomeAccepted = Test-Agent99SignozTransportOutcome $credential + $credentialTypedOutput = if ($credentialOutcomeAccepted) { [string]$credential.stdout } else { "" } + $credentialState = if ($credentialTypedOutput -match "^agent99_credential:(absent|symlink|other)$") { + [string]$Matches[1] + } elseif ($credentialTypedOutput -match "^agent99_credential:(present:[0-9]+:[0-9]+:[0-9]+)$") { + [string]$Matches[1] + } else { "unverified" } + $credentialReadbackVerified = [bool]( + ( + $credentialState -in @("absent", "symlink", "other") -or + $credentialState -match "^present:[0-9]+:[0-9]+:[0-9]+$" + ) + ) + $credentialReferencePresent = if ($credentialReadbackVerified) { + [bool]($credentialState -ne "absent") + } else { $null } $credentialOwner = $null $credentialMode = $null $credentialSize = $null $credentialMetadataValid = $false - if ($credential.ok -and [string]$credential.stdout -match "^([0-9]+):([0-9]+):([0-9]+)$") { + if ($credentialReadbackVerified -and $credentialState -match "^present:([0-9]+):([0-9]+):([0-9]+)$") { $credentialOwner = [int]$Matches[1] $credentialMode = [string]$Matches[2] $credentialSize = [int64]$Matches[3] @@ -238,17 +295,43 @@ function Get-Agent99SignozPreflight { ) } - $hashCommand = "sudo -n /usr/bin/sha256sum " + (($ToolchainFiles.Keys) -join " ") + $hashCommand = ( + "sudo -n /bin/bash -c 'set -euo pipefail; " + + "/usr/bin/echo agent99_hashes_begin; " + + "/usr/bin/sha256sum " + (($ToolchainFiles.Keys) -join " ") + "; " + + "/usr/bin/echo agent99_hashes_end'" + ) $hashReadback = Invoke-Agent99SignozFixedRemote $hashCommand $actualHashes = @{} - if ($hashReadback.ok) { - foreach ($line in @(([string]$hashReadback.stdout) -split "`r?`n")) { + $hashOutcomeAccepted = Test-Agent99SignozTransportOutcome $hashReadback + $hashLines = if ($hashOutcomeAccepted) { + @(([string]$hashReadback.stdout -split "`r?`n") | Where-Object { [string]$_ -ne "" }) + } else { @() } + $hashShapeValid = [bool]( + $hashLines.Count -eq $ToolchainFiles.Count + 2 -and + [string]$hashLines[0] -eq "agent99_hashes_begin" -and + [string]$hashLines[$hashLines.Count - 1] -eq "agent99_hashes_end" + ) + if ($hashShapeValid) { + foreach ($line in @($hashLines[1..($hashLines.Count - 2)])) { if ($line -match "^([0-9a-f]{64})\s+(.+)$") { - $actualHashes[[string]$Matches[2]] = [string]$Matches[1] + $path = [string]$Matches[2] + if ($actualHashes.ContainsKey($path) -or -not $ToolchainFiles.Contains($path)) { + $hashShapeValid = $false + break + } + $actualHashes[$path] = [string]$Matches[1] + } else { + $hashShapeValid = $false + break } } } - $toolchainExact = [bool]($actualHashes.Count -eq $ToolchainFiles.Count) + $toolchainReadbackVerified = [bool]( + $hashShapeValid -and + $actualHashes.Count -eq $ToolchainFiles.Count + ) + $toolchainExact = $toolchainReadbackVerified foreach ($entry in $ToolchainFiles.GetEnumerator()) { if (-not $actualHashes.ContainsKey([string]$entry.Key) -or $actualHashes[[string]$entry.Key] -ne [string]$entry.Value) { $toolchainExact = $false @@ -256,17 +339,29 @@ function Get-Agent99SignozPreflight { } $image = Invoke-Agent99SignozFixedRemote ( - "docker image inspect --format={{.Id}} $SignozImage" + "sudo -n /bin/bash -c 'set -euo pipefail; " + + "/usr/bin/printf `"agent99_image:`"; " + + "/usr/bin/docker image inspect --format={{.Id}} $SignozImage'" ) - $imageExact = [bool]($image.ok -and [string]$image.stdout -eq $SignozImageId) + $imageOutcomeAccepted = Test-Agent99SignozTransportOutcome $image + $imageId = if ($imageOutcomeAccepted -and [string]$image.stdout -match "^agent99_image:(sha256:[0-9a-f]{64})$") { + [string]$Matches[1] + } else { "" } + $imageReadbackVerified = [bool]($imageId -match "^sha256:[0-9a-f]{64}$") + $imageExact = [bool]($imageReadbackVerified -and $imageId -eq $SignozImageId) $runtime = Invoke-Agent99SignozFixedRemote ( - "docker inspect --format='{{.Id}}|{{.Image}}|{{.Config.Image}}|{{.State.Running}}' $SignozContainer" + "sudo -n /bin/bash -c 'set -euo pipefail; " + + "/usr/bin/printf agent99_runtime:; " + + "/usr/bin/docker inspect --format={{.Id}},{{.Image}},{{.Config.Image}},{{.State.Running}} $SignozContainer'" ) + $runtimeOutcomeAccepted = Test-Agent99SignozTransportOutcome $runtime $runtimeContainerId = $null $runtimeImageId = $null $runtimeImageRef = $null $runtimeRunning = $false - if ($runtime.ok -and [string]$runtime.stdout -match "^([^|]+)[|]([^|]+)[|]([^|]+)[|](true|false)$") { + $runtimeReadbackVerified = $false + if ($runtimeOutcomeAccepted -and [string]$runtime.stdout -match "^agent99_runtime:([0-9a-f]{64}),(sha256:[0-9a-f]{64}),([A-Za-z0-9./:_-]+),(true|false)$") { + $runtimeReadbackVerified = $true $runtimeContainerId = [string]$Matches[1] $runtimeImageId = [string]$Matches[2] $runtimeImageRef = [string]$Matches[3] @@ -278,12 +373,19 @@ function Get-Agent99SignozPreflight { $runtimeImageRef -eq $SignozImage ) $health = Invoke-Agent99SignozFixedRemote ( - "curl -fsS --max-time 15 $BaseUrl/api/v1/health" + "sudo -n /bin/bash -c 'set -euo pipefail; " + + "/usr/bin/printf `"agent99_health:`"; " + + "/usr/bin/curl -fsS --max-time 15 $BaseUrl/api/v1/health'" ) $healthOk = $false - if ($health.ok) { + $healthReadbackVerified = $false + if ( + (Test-Agent99SignozTransportOutcome $health) -and + [string]$health.stdout -match "^agent99_health:(\{.+\})$" + ) { try { - $healthPayload = [string]$health.stdout | ConvertFrom-Json + $healthPayload = [string]$Matches[1] | ConvertFrom-Json + $healthReadbackVerified = [bool]($null -ne $healthPayload.PSObject.Properties["status"]) $healthOk = [bool]([string]$healthPayload.status -eq "ok") } catch { $healthOk = $false @@ -291,37 +393,67 @@ function Get-Agent99SignozPreflight { } $active = Invoke-Agent99SignozFixedRemote ( - "sudo -n /usr/bin/pgrep -fc '[s]ignoz-metadata-export.py.*--apply'" + "sudo -n /bin/bash -c 'set -euo pipefail; set +e; " + + "active_output=`$(/usr/bin/pgrep -fc `"[s]ignoz-metadata-export.py.*--apply`" ); " + + "active_rc=`$?; set -e; " + + "[ `"`$active_rc`" -eq 0 ] || [ `"`$active_rc`" -eq 1 ]; " + + "/usr/bin/printf `"%s`" `"`$active_output`" | /usr/bin/grep -Eq `"^[0-9]+$`"; " + + "/usr/bin/printf `"agent99_active:%s`" `"`$active_output`"'" ) $activeOperationCount = $null - if ([string]$active.stdout -match "^[0-9]+$") { - $activeOperationCount = [int]$active.stdout + $activeOutcomeAccepted = Test-Agent99SignozTransportOutcome $active + if ($activeOutcomeAccepted -and [string]$active.stdout -match "^agent99_active:([0-9]+)$") { + $activeOperationCount = [int]$Matches[1] } + $activeReadbackVerified = [bool]($null -ne $activeOperationCount) $activeOperationsClear = [bool]( - $active.exitCode -in @(0, 1) -and - $null -ne $activeOperationCount -and + $activeReadbackVerified -and $activeOperationCount -eq 0 ) $outputState = Invoke-Agent99SignozFixedRemote ( - "sudo -n /usr/bin/test -e $OutputDir" + New-Agent99SignozPathStateCommand $OutputDir "directory" ) $receiptState = Invoke-Agent99SignozFixedRemote ( - "sudo -n /usr/bin/test -e $RemoteReceipt" + New-Agent99SignozPathStateCommand $RemoteReceipt "file" ) - $outputPresent = [bool]($outputState.exitCode -eq 0) - $receiptPresent = [bool]($receiptState.exitCode -eq 0) + $outputStateValue = if ( + (Test-Agent99SignozTransportOutcome $outputState) -and + [string]$outputState.stdout -match "^agent99_path_state:(absent|directory|other|symlink)$" + ) { [string]$Matches[1] } else { "unverified" } + $receiptStateValue = if ( + (Test-Agent99SignozTransportOutcome $receiptState) -and + [string]$receiptState.stdout -match "^agent99_path_state:(absent|file|other|symlink)$" + ) { [string]$Matches[1] } else { "unverified" } + $outputStateReadbackVerified = [bool]($outputStateValue -ne "unverified") + $receiptStateReadbackVerified = [bool]($receiptStateValue -ne "unverified") + $outputPresent = if ($outputStateReadbackVerified) { + [bool]($outputStateValue -ne "absent") + } else { $null } + $receiptPresent = if ($receiptStateReadbackVerified) { + [bool]($receiptStateValue -ne "absent") + } else { $null } $stateReadable = [bool]( - $outputState.exitCode -in @(0, 1) -and - $receiptState.exitCode -in @(0, 1) + $outputStateReadbackVerified -and + $receiptStateReadbackVerified ) $artifactStateReady = if ($ExpectedArtifactState -eq "present") { - [bool]($outputPresent -and $receiptPresent) + [bool]($outputStateValue -eq "directory" -and $receiptStateValue -eq "file") } else { - [bool](-not $outputPresent -and -not $receiptPresent) + [bool]($outputStateValue -eq "absent" -and $receiptStateValue -eq "absent") } + $transportReadbackVerified = [bool]( + $credentialReadbackVerified -and + $toolchainReadbackVerified -and + $imageReadbackVerified -and + $runtimeReadbackVerified -and + $healthReadbackVerified -and + $activeReadbackVerified -and + $stateReadable + ) $credentialReady = [bool](-not $RequireCredential -or $credentialMetadataValid) $ready = [bool]( + $transportReadbackVerified -and $credentialReady -and $toolchainExact -and $imageExact -and @@ -334,7 +466,10 @@ function Get-Agent99SignozPreflight { return [pscustomobject]@{ ready = $ready - credentialReferencePresent = [bool]$credential.ok + transportReadbackVerified = $transportReadbackVerified + credentialReadbackVerified = $credentialReadbackVerified + credentialState = $credentialState + credentialReferencePresent = $credentialReferencePresent credentialMetadataValid = $credentialMetadataValid credentialOwnerUid = $credentialOwner credentialMode = $credentialMode @@ -343,21 +478,30 @@ function Get-Agent99SignozPreflight { credentialValueOutput = $false toolchainBundleId = $BundleId toolchainFileCount = $ToolchainFiles.Count + toolchainReadbackVerified = $toolchainReadbackVerified toolchainExact = $toolchainExact signozImage = $SignozImage - signozImageId = if ($image.ok) { [string]$image.stdout } else { $null } + signozImageId = if ($imageReadbackVerified) { $imageId } else { $null } + signozImageReadbackVerified = $imageReadbackVerified signozImageExact = $imageExact signozContainer = $SignozContainer signozRuntimeContainerId = $runtimeContainerId signozRuntimeImageId = $runtimeImageId signozRuntimeImageRef = $runtimeImageRef signozRuntimeRunning = $runtimeRunning + signozRuntimeReadbackVerified = $runtimeReadbackVerified signozRuntimeImageExact = $runtimeImageExact + apiHealthReadbackVerified = $healthReadbackVerified apiHealthOk = $healthOk activeOperationCount = $activeOperationCount + activeOperationReadbackVerified = $activeReadbackVerified activeOperationsClear = $activeOperationsClear expectedArtifactState = $ExpectedArtifactState + outputState = $outputStateValue + outputStateReadbackVerified = $outputStateReadbackVerified outputPresent = $outputPresent + receiptState = $receiptStateValue + receiptStateReadbackVerified = $receiptStateReadbackVerified receiptPresent = $receiptPresent artifactStateReady = $artifactStateReady } @@ -476,7 +620,7 @@ function Write-Agent99SignozTerminal { credentialReferenceDispatchAttempted = $script:ApplyDispatchAttempted credentialReferenceConsumedOnTarget = if (-not $script:ApplyDispatchAttempted) { $false - } elseif ($ApplyReceipt) { + } elseif (Test-Agent99SignozReceiptIdentity $ApplyReceipt "terminal" @("partial_degraded")) { $true } else { $null @@ -572,6 +716,9 @@ if (-not $runtimeSourceBinding.ok) { if ($Mode -eq "Verify") { $preflight = Get-Agent99SignozPreflight -ExpectedArtifactState "present" -RequireCredential $false + if (-not $preflight.transportReadbackVerified) { + Stop-Agent99SignozRun "verification_failed" "preflight_transport_unverified" $preflight $null $null $null $false 1 + } if (-not $preflight.ready) { Stop-Agent99SignozRun "verification_failed" "verified_bundle_preflight_failed" $preflight $null $null $null $false 1 } @@ -584,10 +731,13 @@ if ($Mode -eq "Verify") { $verifyCommand = New-Agent99SignozLockedRemoteCommand $verifyProgram $CheckVerifyTargetTimeoutSeconds "present" $verify = Invoke-Agent99SignozFixedRemote $verifyCommand $CheckVerifyControllerWaitSeconds $verifyReceipt = Convert-Agent99JsonReceipt $verify - if (-not $verify.ok -or -not (Test-Agent99SignozReceiptIdentity $verifyReceipt "independent_export_verifier" @("pass_export_verified_restore_pending"))) { + if (-not (Test-Agent99SignozTransportOutcome $verify) -or -not (Test-Agent99SignozReceiptIdentity $verifyReceipt "independent_export_verifier" @("pass_export_verified_restore_pending"))) { Stop-Agent99SignozRun "verification_failed" "independent_export_verifier_failed" $preflight $null $null $verifyReceipt $false 1 } $postflight = Get-Agent99SignozPreflight -ExpectedArtifactState "present" -RequireCredential $false + if (-not $postflight.transportReadbackVerified) { + Stop-Agent99SignozRun "verification_failed" "post_verifier_transport_unverified" $postflight $null $null $verifyReceipt $false 1 + } if (-not $postflight.ready) { Stop-Agent99SignozRun "verification_failed" "post_verifier_runtime_or_artifact_drift" $postflight $null $null $verifyReceipt $false 1 } @@ -595,12 +745,18 @@ if ($Mode -eq "Verify") { } $preflight = Get-Agent99SignozPreflight -ExpectedArtifactState "absent" -RequireCredential $true +if (-not $preflight.credentialReadbackVerified) { + Stop-Agent99SignozRun "blocked_with_safe_next_action" "credential_reference_readback_unverified" $preflight $null $null $null $false 2 +} if (-not $preflight.credentialReferencePresent) { Stop-Agent99SignozRun "blocked_with_safe_next_action" "credential_reference_absent" $preflight $null $null $null $false 2 } if (-not $preflight.credentialMetadataValid) { Stop-Agent99SignozRun "blocked_with_safe_next_action" "credential_reference_metadata_invalid" $preflight $null $null $null $false 2 } +if (-not $preflight.transportReadbackVerified) { + Stop-Agent99SignozRun "blocked_with_safe_next_action" "preflight_transport_unverified" $preflight $null $null $null $false 1 +} if (-not $preflight.ready) { Stop-Agent99SignozRun "blocked_with_safe_next_action" "metadata_export_preflight_failed" $preflight $null $null $null $false 1 } @@ -613,7 +769,7 @@ $checkProgram = ( $checkCommand = New-Agent99SignozLockedRemoteCommand $checkProgram $CheckVerifyTargetTimeoutSeconds "absent" $check = Invoke-Agent99SignozFixedRemote $checkCommand $CheckVerifyControllerWaitSeconds $checkReceipt = Convert-Agent99JsonReceipt $check -if (-not $check.ok -or -not (Test-Agent99SignozReceiptIdentity $checkReceipt "terminal" @("check_pass_no_write"))) { +if (-not (Test-Agent99SignozTransportOutcome $check) -or -not (Test-Agent99SignozReceiptIdentity $checkReceipt "terminal" @("check_pass_no_write"))) { Stop-Agent99SignozRun "blocked_with_safe_next_action" "metadata_export_check_failed" $preflight $checkReceipt $null $null $false 1 } if ($Mode -eq "Check") { @@ -630,10 +786,15 @@ $applyCommand = New-Agent99SignozLockedRemoteCommand $applyProgram $ApplyTargetT $script:ApplyDispatchAttempted = $true $apply = Invoke-Agent99SignozFixedRemote $applyCommand $ApplyControllerWaitSeconds $applyReceipt = Convert-Agent99JsonReceipt $apply -if (-not $apply.ok -or -not (Test-Agent99SignozReceiptIdentity $applyReceipt "terminal" @("partial_degraded"))) { +if (-not (Test-Agent99SignozTransportOutcome $apply) -or -not (Test-Agent99SignozReceiptIdentity $applyReceipt "terminal" @("partial_degraded"))) { $artifactReadback = Get-Agent99SignozPreflight -ExpectedArtifactState "present" -RequireCredential $false - $writeState = if ($artifactReadback.outputPresent -or $artifactReadback.receiptPresent) { $true } else { $null } - Stop-Agent99SignozRun "apply_failed_artifact_preserved_for_readback" "bounded_export_failed" $artifactReadback $checkReceipt $applyReceipt $null $writeState 1 + if ($artifactReadback.outputPresent -eq $true -or $artifactReadback.receiptPresent -eq $true) { + Stop-Agent99SignozRun "apply_failed_artifact_preserved_for_readback" "bounded_export_failed" $artifactReadback $checkReceipt $applyReceipt $null $true 1 + } + if ($artifactReadback.outputPresent -eq $false -and $artifactReadback.receiptPresent -eq $false) { + Stop-Agent99SignozRun "apply_failed_no_durable_artifact_observed" "bounded_export_failed_no_final_artifact" $artifactReadback $checkReceipt $applyReceipt $null $null 1 + } + Stop-Agent99SignozRun "apply_failed_artifact_state_unverified" "bounded_export_failed_artifact_readback_unverified" $artifactReadback $checkReceipt $applyReceipt $null $null 1 } $verifyProgram = ( @@ -645,12 +806,15 @@ $verifyProgram = ( $verifyCommand = New-Agent99SignozLockedRemoteCommand $verifyProgram $CheckVerifyTargetTimeoutSeconds "present" $verify = Invoke-Agent99SignozFixedRemote $verifyCommand $CheckVerifyControllerWaitSeconds $verifyReceipt = Convert-Agent99JsonReceipt $verify -if (-not $verify.ok -or -not (Test-Agent99SignozReceiptIdentity $verifyReceipt "independent_export_verifier" @("pass_export_verified_restore_pending"))) { +if (-not (Test-Agent99SignozTransportOutcome $verify) -or -not (Test-Agent99SignozReceiptIdentity $verifyReceipt "independent_export_verifier" @("pass_export_verified_restore_pending"))) { $artifactReadback = Get-Agent99SignozPreflight -ExpectedArtifactState "present" -RequireCredential $false Stop-Agent99SignozRun "verification_failed_artifact_preserved" "independent_export_verifier_failed" $artifactReadback $checkReceipt $applyReceipt $verifyReceipt $true 1 } $postflight = Get-Agent99SignozPreflight -ExpectedArtifactState "present" -RequireCredential $false +if (-not $postflight.transportReadbackVerified) { + Stop-Agent99SignozRun "verification_failed_artifact_preserved" "post_verifier_transport_unverified" $postflight $checkReceipt $applyReceipt $verifyReceipt $true 1 +} if (-not $postflight.ready) { Stop-Agent99SignozRun "verification_failed_artifact_preserved" "post_verifier_runtime_or_artifact_drift" $postflight $checkReceipt $applyReceipt $verifyReceipt $true 1 } diff --git a/scripts/ops/tests/test_agent99_signoz_metadata_executor_runtime_entrypoint.py b/scripts/ops/tests/test_agent99_signoz_metadata_executor_runtime_entrypoint.py index cdbc693bb..1109d1197 100644 --- a/scripts/ops/tests/test_agent99_signoz_metadata_executor_runtime_entrypoint.py +++ b/scripts/ops/tests/test_agent99_signoz_metadata_executor_runtime_entrypoint.py @@ -98,6 +98,97 @@ def test_entrypoint_uses_fixed_public_key_transport_and_no_arbitrary_shell() -> assert "|| true" not in lock_helper assert '--signal=TERM --kill-after=$($TargetKillAfterSeconds)s' in source + +def test_entrypoint_uses_typed_stdout_without_windows_process_exit_code() -> None: + source = ENTRYPOINT.read_text(encoding="utf-8") + transport = source[ + source.index("function Invoke-Agent99SignozFixedRemote") : + source.index("function New-Agent99SignozLockedRemoteCommand") + ] + + assert "$process.ExitCode" not in transport + assert "exitCodeObserved" not in transport + assert "AllowedExitCodes" not in transport + assert "function Test-Agent99SignozTransportOutcome" in transport + assert "$Result.completed -eq $true" in transport + assert "-not $Result.timedOut" in transport + assert '$process.WaitForExit($TimeoutSeconds * 1000)' in transport + assert "taskkill.exe /PID $process.Id /T /F" in transport + + +def test_entrypoint_preflight_has_explicit_credential_and_artifact_state_contracts() -> None: + source = ENTRYPOINT.read_text(encoding="utf-8") + + assert 'function New-Agent99SignozPathStateCommand' in source + assert 'agent99_credential:symlink' in source + assert 'agent99_credential:absent' in source + assert 'agent99_credential:present:%u:%a:%s' in source + assert 'agent99_path_state:$ExpectedKind' in source + assert 'agent99_path_state:absent' in source + assert 'agent99_hashes_begin' in source + assert 'agent99_hashes_end' in source + assert '/usr/bin/echo agent99_hashes_begin' in source + assert '/usr/bin/echo agent99_hashes_end' in source + assert 'agent99_hashes_begin\\n' not in source + assert 'agent99_image:' in source + assert 'agent99_runtime:' in source + assert 'agent99_health:' in source + assert 'agent99_active:' in source + assert '$credentialState -in @("absent", "symlink", "other")' in source + assert 'credentialReadbackVerified = $credentialReadbackVerified' in source + assert 'transportReadbackVerified = $transportReadbackVerified' in source + assert 'outputStateReadbackVerified = $outputStateReadbackVerified' in source + assert 'receiptStateReadbackVerified = $receiptStateReadbackVerified' in source + assert '"credential_reference_readback_unverified"' in source + assert '"preflight_transport_unverified"' in source + assert source.count("Test-Agent99SignozTransportOutcome $verify") == 2 + assert "Test-Agent99SignozTransportOutcome $check" in source + assert "Test-Agent99SignozTransportOutcome $apply" in source + + +def test_entrypoint_typed_readbacks_are_exact_and_preserve_unknown_state() -> None: + source = ENTRYPOINT.read_text(encoding="utf-8") + preflight = source[ + source.index("function Get-Agent99SignozPreflight") : + source.index("function Write-Agent99SignozEvidenceCollision") + ] + main = source[ + source.index( + '$preflight = Get-Agent99SignozPreflight ' + '-ExpectedArtifactState "absent"' + ) : + ] + + assert "$hashLines.Count -eq $ToolchainFiles.Count + 2" in preflight + assert "$hashLines[0] -eq \"agent99_hashes_begin\"" in preflight + assert '$hashLines[$hashLines.Count - 1] -eq "agent99_hashes_end"' in preflight + assert "$actualHashes.ContainsKey($path)" in preflight + assert "-not $ToolchainFiles.Contains($path)" in preflight + assert '"^agent99_image:(sha256:[0-9a-f]{64})$"' in preflight + assert '--format={{.Id}},{{.Image}},{{.Config.Image}},{{.State.Running}}' in preflight + assert '--format=`"{{.Id}}|{{.Image}}|{{.Config.Image}}|{{.State.Running}}`"' not in preflight + assert '"^agent99_runtime:([0-9a-f]{64}),(sha256:' in preflight + assert '"^agent99_active:([0-9]+)$"' in preflight + assert '[ `"`$active_rc`" -eq 0 ] || [ `"`$active_rc`" -eq 1 ]' in preflight + assert "outputPresent = if ($outputStateReadbackVerified)" in preflight + assert "receiptPresent = if ($receiptStateReadbackVerified)" in preflight + assert "credentialReferencePresent = if ($credentialReadbackVerified)" in preflight + + credential_unverified = main.index("credential_reference_readback_unverified") + credential_absent = main.index("credential_reference_absent") + credential_invalid = main.index("credential_reference_metadata_invalid") + preflight_unverified = main.index("preflight_transport_unverified") + generic_preflight = main.index("metadata_export_preflight_failed") + assert ( + credential_unverified + < credential_absent + < credential_invalid + < preflight_unverified + < generic_preflight + ) + assert "$RemoteResult.completed -ne $true" in source + assert "$RemoteResult.timedOut" in source + guard = 95 kill_after = 15 transport_reserve = 30 @@ -108,7 +199,7 @@ def test_entrypoint_uses_fixed_public_key_transport_and_no_arbitrary_shell() -> def test_entrypoint_never_reads_or_persists_credential_value_in_controller() -> None: source = ENTRYPOINT.read_text(encoding="utf-8") - assert 'sudo -n /usr/bin/stat -c %u:%a:%s $CredentialFile' in source + assert '/usr/bin/stat -c `"agent99_credential:present:%u:%a:%s`" `"$CredentialFile`"' in source assert 'credentialValueRead = $false' in source assert 'credentialValueOutput = $false' in source assert 'secretValueReadByController = $false' in source @@ -117,7 +208,7 @@ def test_entrypoint_never_reads_or_persists_credential_value_in_controller() -> assert "credentialReferenceDispatchAttempted" in source assert "$script:ApplyDispatchAttempted = $true" in source assert "credentialReferenceConsumedOnTarget = if" in source - assert "elseif ($ApplyReceipt)" in source + assert "elseif (Test-Agent99SignozReceiptIdentity $ApplyReceipt" in source assert 'rawSqliteRead = $false' in source assert 'rawVolumeRead = $false' in source assert "Get-Content -LiteralPath $CredentialFile" not in source @@ -177,7 +268,7 @@ def test_entrypoint_reserves_durable_evidence_before_any_remote_dispatch() -> No def test_entrypoint_binds_export_to_the_running_canonical_signoz_image() -> None: source = ENTRYPOINT.read_text(encoding="utf-8") - assert "{{.Id}}|{{.Image}}|{{.Config.Image}}|{{.State.Running}}" in source + assert "{{.Id}},{{.Image}},{{.Config.Image}},{{.State.Running}}" in source assert "$runtimeImageId -eq $SignozImageId" in source assert "$runtimeImageRef -eq $SignozImage" in source assert "signozRuntimeImageExact = $runtimeImageExact" in source @@ -204,3 +295,21 @@ def test_entrypoint_is_in_exact_agent99_runtime_bundle() -> None: assert expected in _quoted_array(receiver, "$FixedRuntimeFiles = @(", ")") assert 'expectedRuntimeFileCount": 18' in sender assert '$runtimeManifest.fileCount -eq 18' in receiver + + +def test_runtime_contract_check_requires_typed_signoz_transport_readback() -> None: + contract = (ROOT / "agent99-contract-check.ps1").read_text(encoding="utf-8") + + for marker in ( + "agent99_credential:present:%u:%a:%s", + "agent99_path_state:$ExpectedKind", + "agent99_hashes_begin", + "agent99_hashes_end", + "agent99_image:", + "agent99_runtime:", + "agent99_health:", + "agent99_active:", + "preflight_transport_unverified", + ): + assert marker in contract + assert "-not $signozMetadataExecutor.Contains('$process.ExitCode')" in contract