Merge remote-tracking branch 'gitea/main' into codex/p0-obs-002-credential-20260722
This commit is contained in:
@@ -17,11 +17,17 @@ $ProgressPreference = "SilentlyContinue"
|
||||
$LegacyTaskName = "Wooo-Agent99-Host188-Performance-Guard"
|
||||
$ReplacementTaskName = "Wooo-Agent99-Performance-Guard"
|
||||
$ExpectedLegacyActionSha256 = "a2f98560673ebed5aa1f828b4e577c8a34d1c277b2e498044c3526a4ffe15379"
|
||||
$ExpectedPowerShellExecutable = Join-Path $env:SystemRoot "System32\WindowsPowerShell\v1.0\powershell.exe"
|
||||
$ExpectedReplacementLauncher = Join-Path $AgentRoot "agent99-run.ps1"
|
||||
$ExpectedReplacementArguments = "-NoProfile -ExecutionPolicy Bypass -File `"$ExpectedReplacementLauncher`" -Mode Perf -ControlledApply"
|
||||
$EvidenceRoot = Join-Path $AgentRoot "evidence\controlled-task-quarantine"
|
||||
$ReceiptPath = Join-Path $EvidenceRoot "$RunId.json"
|
||||
$RollbackPath = Join-Path $EvidenceRoot "$RunId.rollback.xml"
|
||||
$TemporaryReceiptPath = "$ReceiptPath.tmp"
|
||||
$SafeIdentityPattern = "^[A-Za-z0-9](?:[A-Za-z0-9_.:-]{0,158}[A-Za-z0-9])?$"
|
||||
$ExecutorSourceSha256 = (Get-FileHash `
|
||||
-LiteralPath $MyInvocation.MyCommand.Path `
|
||||
-Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
|
||||
function Get-ActionArgumentSha256 {
|
||||
param([Parameter(Mandatory = $true)][object]$Action)
|
||||
@@ -42,6 +48,15 @@ function Get-ControlledTaskState {
|
||||
$replacementActions = @($replacement.Actions)
|
||||
|
||||
if ($legacyActions.Count -ne 1) { throw "legacy_task_action_count_mismatch" }
|
||||
if (-not [StringComparer]::OrdinalIgnoreCase.Equals(
|
||||
[string]$legacyActions[0].Execute,
|
||||
[string]$ExpectedPowerShellExecutable
|
||||
)) {
|
||||
throw "legacy_task_executable_identity_mismatch"
|
||||
}
|
||||
if (([string]$legacyActions[0].WorkingDirectory).Length -ne 0) {
|
||||
throw "legacy_task_working_directory_identity_mismatch"
|
||||
}
|
||||
if ((Get-ActionArgumentSha256 $legacyActions[0]) -ne $ExpectedLegacyActionSha256) {
|
||||
throw "legacy_task_action_identity_mismatch"
|
||||
}
|
||||
@@ -50,8 +65,18 @@ function Get-ControlledTaskState {
|
||||
throw "canonical_replacement_not_ready"
|
||||
}
|
||||
|
||||
$replacementArguments = [string]$replacementActions[0].Arguments
|
||||
if ($replacementArguments -notmatch "agent99-run\.ps1.+-Mode\s+Perf") {
|
||||
$replacementAction = $replacementActions[0]
|
||||
$replacementArguments = [string]$replacementAction.Arguments
|
||||
if (-not [StringComparer]::OrdinalIgnoreCase.Equals(
|
||||
[string]$replacementAction.Execute,
|
||||
[string]$ExpectedPowerShellExecutable
|
||||
)) {
|
||||
throw "canonical_replacement_executable_identity_mismatch"
|
||||
}
|
||||
if (([string]$replacementAction.WorkingDirectory).Length -ne 0) {
|
||||
throw "canonical_replacement_working_directory_identity_mismatch"
|
||||
}
|
||||
if ($replacementArguments -cne $ExpectedReplacementArguments) {
|
||||
throw "canonical_replacement_action_mismatch"
|
||||
}
|
||||
if ($replacementArguments -match "api\.telegram\.org|sendMessage|BotToken|ChatId") {
|
||||
@@ -63,6 +88,8 @@ function Get-ControlledTaskState {
|
||||
legacyState = [string]$legacy.State
|
||||
replacementEnabled = [bool]$replacement.Settings.Enabled
|
||||
legacyActionSha256 = $ExpectedLegacyActionSha256
|
||||
legacyActionIdentityVerified = $true
|
||||
replacementActionIdentityVerified = $true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,7 +136,63 @@ if ($Mode -eq "Check") {
|
||||
|
||||
if ($Mode -eq "Verify") {
|
||||
$receiptAvailable = Test-Path -LiteralPath $ReceiptPath -PathType Leaf
|
||||
$verified = [bool](-not $state.legacyEnabled -and $state.replacementEnabled -and $receiptAvailable)
|
||||
$receiptValid = $false
|
||||
if ($receiptAvailable) {
|
||||
try {
|
||||
$receipt = Get-Content -LiteralPath $ReceiptPath -Raw | ConvertFrom-Json
|
||||
$rollbackAvailable = Test-Path -LiteralPath $RollbackPath -PathType Leaf
|
||||
$rollbackSha256 = if ($rollbackAvailable) {
|
||||
(Get-FileHash -LiteralPath $RollbackPath -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
} else {
|
||||
""
|
||||
}
|
||||
$receiptBooleanTypesValid = [bool](
|
||||
$receipt.legacyActionIdentityVerified -is [bool] -and
|
||||
$receipt.replacementActionIdentityVerified -is [bool] -and
|
||||
$receipt.legacyEnabledBefore -is [bool] -and
|
||||
$receipt.legacyEnabledAfter -is [bool] -and
|
||||
$receipt.replacementEnabledAfter -is [bool] -and
|
||||
$receipt.remoteWritePerformed -is [bool] -and
|
||||
$receipt.scheduledTaskModification -is [bool] -and
|
||||
$receipt.taskDeleted -is [bool] -and
|
||||
$receipt.serviceRestart -is [bool] -and
|
||||
$receipt.hostReboot -is [bool] -and
|
||||
$receipt.vmPowerChange -is [bool] -and
|
||||
$receipt.secretValueRead -is [bool] -and
|
||||
$receipt.privateKeyValueRead -is [bool] -and
|
||||
$receipt.tokenValueRead -is [bool] -and
|
||||
$receipt.environmentSecretRead -is [bool] -and
|
||||
$receipt.rawTaskArgumentsStored -is [bool] -and
|
||||
$receipt.rollbackAvailable -is [bool] -and
|
||||
$receipt.verifierPassed -is [bool]
|
||||
)
|
||||
$receiptValid = [bool](
|
||||
$receiptBooleanTypesValid -and
|
||||
[string]$receipt.schemaVersion -eq "agent99_legacy_sender_quarantine_receipt_v1" -and
|
||||
[string]$receipt.traceId -eq $TraceId -and
|
||||
[string]$receipt.runId -eq $RunId -and
|
||||
[string]$receipt.workItemId -eq $WorkItemId -and
|
||||
[string]$receipt.canonicalAssetId -eq "windows-vmware:host_99" -and
|
||||
[string]$receipt.legacyTask -eq $LegacyTaskName -and
|
||||
[string]$receipt.replacementTask -eq $ReplacementTaskName -and
|
||||
[string]$receipt.legacyActionSha256 -eq $ExpectedLegacyActionSha256 -and
|
||||
[string]$receipt.executorSourceSha256 -eq $ExecutorSourceSha256 -and
|
||||
$rollbackAvailable -and
|
||||
[string]$receipt.rollbackArtifactSha256 -eq $rollbackSha256 -and
|
||||
$receipt.legacyActionIdentityVerified -eq $true -and
|
||||
$receipt.replacementActionIdentityVerified -eq $true -and
|
||||
$receipt.legacyEnabledAfter -eq $false -and
|
||||
$receipt.replacementEnabledAfter -eq $true -and
|
||||
$receipt.taskDeleted -eq $false -and
|
||||
$receipt.secretValueRead -eq $false -and
|
||||
$receipt.rawTaskArgumentsStored -eq $false -and
|
||||
$receipt.verifierPassed -eq $true
|
||||
)
|
||||
} catch {
|
||||
$receiptValid = $false
|
||||
}
|
||||
}
|
||||
$verified = [bool](-not $state.legacyEnabled -and $state.replacementEnabled -and $receiptValid)
|
||||
[ordered]@{
|
||||
schemaVersion = "agent99_legacy_sender_quarantine_verify_v1"
|
||||
status = if ($verified) { "quarantine_verified" } else { "quarantine_unverified" }
|
||||
@@ -121,6 +204,12 @@ if ($Mode -eq "Verify") {
|
||||
legacyEnabled = $state.legacyEnabled
|
||||
replacementEnabled = $state.replacementEnabled
|
||||
durableReceiptAvailable = $receiptAvailable
|
||||
durableReceiptValid = $receiptValid
|
||||
durableReceiptSha256 = if ($receiptAvailable) {
|
||||
(Get-FileHash -LiteralPath $ReceiptPath -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
} else {
|
||||
$null
|
||||
}
|
||||
remoteWritePerformed = $false
|
||||
secretValueRead = $false
|
||||
} | ConvertTo-Json -Compress -Depth 6
|
||||
@@ -161,6 +250,9 @@ try {
|
||||
legacyTask = $LegacyTaskName
|
||||
replacementTask = $ReplacementTaskName
|
||||
legacyActionSha256 = $ExpectedLegacyActionSha256
|
||||
executorSourceSha256 = $ExecutorSourceSha256
|
||||
legacyActionIdentityVerified = $after.legacyActionIdentityVerified
|
||||
replacementActionIdentityVerified = $after.replacementActionIdentityVerified
|
||||
rollbackArtifactSha256 = $rollbackSha256
|
||||
legacyEnabledBefore = $state.legacyEnabled
|
||||
legacyEnabledAfter = $after.legacyEnabled
|
||||
@@ -175,6 +267,7 @@ try {
|
||||
privateKeyValueRead = $false
|
||||
tokenValueRead = $false
|
||||
environmentSecretRead = $false
|
||||
rawTaskArgumentsStored = $false
|
||||
rollbackAvailable = $true
|
||||
verifierPassed = $true
|
||||
nextSafeAction = "persist_exact_legacy_sender_sunset_policy_in_gitea_and_verify_no_new_wooo_bot_receipts"
|
||||
|
||||
@@ -4,6 +4,7 @@ from pathlib import Path
|
||||
ROOT = Path(__file__).resolve().parents[3]
|
||||
SCRIPT = ROOT / "scripts/reboot-recovery/agent99-legacy-sender-quarantine.ps1"
|
||||
REGISTER_TASKS = ROOT / "agent99-register-tasks.ps1"
|
||||
CONTROL_PLANE = ROOT / "agent99-control-plane.ps1"
|
||||
|
||||
|
||||
def test_legacy_sender_quarantine_is_exact_bounded_and_rollback_ready() -> None:
|
||||
@@ -21,6 +22,16 @@ def test_legacy_sender_quarantine_is_exact_bounded_and_rollback_ready() -> None:
|
||||
assert "Restart-Service" not in source
|
||||
assert "Restart-Computer" not in source
|
||||
assert r"api\.telegram\.org|sendMessage|BotToken|ChatId" in source
|
||||
assert "legacy_task_executable_identity_mismatch" in source
|
||||
assert "legacy_task_working_directory_identity_mismatch" in source
|
||||
assert "canonical_replacement_executable_identity_mismatch" in source
|
||||
assert "canonical_replacement_working_directory_identity_mismatch" in source
|
||||
assert "$replacementArguments -cne $ExpectedReplacementArguments" in source
|
||||
assert "durableReceiptValid" in source
|
||||
assert "receiptBooleanTypesValid" in source
|
||||
assert "executorSourceSha256" in source
|
||||
assert "rollbackArtifactSha256 -eq $rollbackSha256" in source
|
||||
assert "rawTaskArgumentsStored = $false" in source
|
||||
assert "secretValueRead = $false" in source
|
||||
assert "taskDeleted = $false" in source
|
||||
|
||||
@@ -42,11 +53,59 @@ def test_legacy_sender_quarantine_has_check_apply_verify_receipts() -> None:
|
||||
|
||||
def test_task_registration_persists_the_exact_legacy_sender_sunset() -> None:
|
||||
source = REGISTER_TASKS.read_text(encoding="utf-8")
|
||||
quarantine_source = source[source.index("# Superseded by the canonical all-host performance guard.") :]
|
||||
|
||||
assert '"Wooo-Agent99-Host188-Performance-Guard"' in source
|
||||
assert "a2f98560673ebed5aa1f828b4e577c8a34d1c277b2e498044c3526a4ffe15379" in source
|
||||
assert "Disable-ScheduledTask" in source
|
||||
assert "Unregister-ScheduledTask" not in source
|
||||
assert "legacy_host188_sender_action_identity_mismatch" in source
|
||||
assert "legacy_host188_sender_executable_identity_mismatch" in source
|
||||
assert "legacy_host188_sender_working_directory_identity_mismatch" in source
|
||||
assert "legacy_host188_sender_running_refuse_quarantine" in source
|
||||
assert "legacy_host188_sender_quarantine_post_verifier_failed" in source
|
||||
assert "agent99_legacy_sender_deployment_quarantine_v1" in source
|
||||
assert "agent99-legacy-sender-deployment-quarantine.json" in source
|
||||
assert "canonical_performance_sender_not_ready" in source
|
||||
assert "canonical_performance_sender_direct_telegram_marker" in source
|
||||
assert "canonical_performance_sender_executable_identity_mismatch" in source
|
||||
assert "canonical_performance_sender_working_directory_identity_mismatch" in source
|
||||
assert "$canonicalPerformanceArguments -cne $perfArgs" in source
|
||||
assert "legacyActionIdentityVerified" in source
|
||||
assert "replacementActionIdentityVerified" in source
|
||||
assert "$registerTaskProducerManifestBound = [bool](" in source
|
||||
assert "producerManifestBound = $registerTaskProducerManifestBound" in source
|
||||
assert "rawTaskArgumentsStored = $false" in source
|
||||
assert "secretValueRead = $false" in source
|
||||
assert "receiptWritePerformed = $true" in source
|
||||
assert "Move-Item -Force" in source
|
||||
assert quarantine_source.index("$canonicalPerformanceTask = Get-ScheduledTask") < quarantine_source.index(
|
||||
"Disable-ScheduledTask"
|
||||
)
|
||||
|
||||
|
||||
def test_selfcheck_independently_verifies_legacy_sender_quarantine() -> None:
|
||||
source = CONTROL_PLANE.read_text(encoding="utf-8")
|
||||
|
||||
assert "function Test-AgentLegacySenderQuarantine" in source
|
||||
assert "agent99_legacy_sender_deployment_quarantine_v1" in source
|
||||
assert "legacy_sender_quarantine_receipt_missing" in source
|
||||
assert "legacy_sender_quarantine_verified" in source
|
||||
assert "legacy_sender_quarantine_drift" in source
|
||||
assert "currentStateMatchesReceipt" in source
|
||||
assert "receiptStatusMatchesState" in source
|
||||
assert "replacementActionValid" in source
|
||||
assert "legacyActionValid" in source
|
||||
assert "$legacyActionSha256 = Get-AgentSha256Hex" in source
|
||||
assert "$replacementArguments -ceq $expectedReplacementArguments" in source
|
||||
assert "[StringComparer]::OrdinalIgnoreCase.Equals" in source
|
||||
assert "legacyActionIdentityVerified -eq $true" in source
|
||||
assert "replacementActionIdentityVerified -eq $true" in source
|
||||
assert "receiptBooleanTypesValid" in source
|
||||
assert "producerIdentityValid" in source
|
||||
assert "producerManifestBound -is [bool]" in source
|
||||
assert "legacyTaskFound -is [bool]" in source
|
||||
assert "rawTaskArgumentsStored -eq $false" in source
|
||||
assert "$legacySenderQuarantine = Test-AgentLegacySenderQuarantine" in source
|
||||
assert 'if ($legacySenderQuarantine.severity -eq "critical")' in source
|
||||
assert "legacySenderQuarantineEvidenceRef" in source
|
||||
|
||||
@@ -523,6 +523,7 @@ def test_agent99_propagates_identity_and_requires_independent_receipt() -> None:
|
||||
assert f'[string]`${parameter} = ""' in bootstrap
|
||||
assert '[string]$dispatchIdentityValidation.reason -eq "identity_control_value_unsafe"' in control
|
||||
assert '"controlled_dispatch_identity_unsafe"' in control
|
||||
assert '"controlled_dispatch_identity_invalid"' in control
|
||||
assert "reason = $identityRejectionReason" in control
|
||||
assert 'applyBlockedReason = "control_identity_incomplete_or_unsafe"' in function
|
||||
assert '$dryRun.managerPreflightStatus -eq "ready"' in function
|
||||
|
||||
Reference in New Issue
Block a user