feat(sre): close provider and telegram automation receipts
This commit is contained in:
@@ -10,10 +10,21 @@ import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
def _bootstrap_api_import(repo_root: Path) -> Path:
|
||||
candidates = (
|
||||
repo_root / "apps" / "api", # source checkout
|
||||
repo_root, # production API image: /app/src
|
||||
)
|
||||
for api_root in candidates:
|
||||
if (api_root / "src").is_dir():
|
||||
if str(api_root) not in sys.path:
|
||||
sys.path.insert(0, str(api_root))
|
||||
return api_root
|
||||
raise RuntimeError("paid_provider_canary_api_source_root_not_found")
|
||||
|
||||
|
||||
_REPO_ROOT = Path(__file__).resolve().parents[2]
|
||||
_API_ROOT = _REPO_ROOT / "apps" / "api"
|
||||
if str(_API_ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(_API_ROOT))
|
||||
_API_ROOT = _bootstrap_api_import(_REPO_ROOT)
|
||||
|
||||
|
||||
async def _init_runtime() -> None:
|
||||
|
||||
208
scripts/reboot-recovery/agent99-legacy-sender-quarantine.ps1
Normal file
208
scripts/reboot-recovery/agent99-legacy-sender-quarantine.ps1
Normal file
@@ -0,0 +1,208 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[ValidateSet("Check", "Apply", "Verify")]
|
||||
[string]$Mode = "Check",
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$TraceId,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$RunId,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$WorkItemId,
|
||||
[string]$AgentRoot = "C:\Wooo\Agent99"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$ProgressPreference = "SilentlyContinue"
|
||||
|
||||
$LegacyTaskName = "Wooo-Agent99-Host188-Performance-Guard"
|
||||
$ReplacementTaskName = "Wooo-Agent99-Performance-Guard"
|
||||
$ExpectedLegacyActionSha256 = "a2f98560673ebed5aa1f828b4e577c8a34d1c277b2e498044c3526a4ffe15379"
|
||||
$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])?$"
|
||||
|
||||
function Get-ActionArgumentSha256 {
|
||||
param([Parameter(Mandatory = $true)][object]$Action)
|
||||
|
||||
$sha256 = [Security.Cryptography.SHA256]::Create()
|
||||
try {
|
||||
$bytes = [Text.Encoding]::UTF8.GetBytes([string]$Action.Arguments)
|
||||
return (($sha256.ComputeHash($bytes) | ForEach-Object { $_.ToString("x2") }) -join "")
|
||||
} finally {
|
||||
$sha256.Dispose()
|
||||
}
|
||||
}
|
||||
|
||||
function Get-ControlledTaskState {
|
||||
$legacy = Get-ScheduledTask -TaskName $LegacyTaskName -TaskPath "\" -ErrorAction Stop
|
||||
$replacement = Get-ScheduledTask -TaskName $ReplacementTaskName -TaskPath "\" -ErrorAction Stop
|
||||
$legacyActions = @($legacy.Actions)
|
||||
$replacementActions = @($replacement.Actions)
|
||||
|
||||
if ($legacyActions.Count -ne 1) { throw "legacy_task_action_count_mismatch" }
|
||||
if ((Get-ActionArgumentSha256 $legacyActions[0]) -ne $ExpectedLegacyActionSha256) {
|
||||
throw "legacy_task_action_identity_mismatch"
|
||||
}
|
||||
if ([string]$legacy.State -eq "Running") { throw "legacy_task_running_refuse_apply" }
|
||||
if ($replacementActions.Count -ne 1 -or -not [bool]$replacement.Settings.Enabled) {
|
||||
throw "canonical_replacement_not_ready"
|
||||
}
|
||||
|
||||
$replacementArguments = [string]$replacementActions[0].Arguments
|
||||
if ($replacementArguments -notmatch "agent99-run\.ps1.+-Mode\s+Perf") {
|
||||
throw "canonical_replacement_action_mismatch"
|
||||
}
|
||||
if ($replacementArguments -match "api\.telegram\.org|sendMessage|BotToken|ChatId") {
|
||||
throw "canonical_replacement_direct_telegram_marker"
|
||||
}
|
||||
|
||||
return [pscustomobject]@{
|
||||
legacyEnabled = [bool]$legacy.Settings.Enabled
|
||||
legacyState = [string]$legacy.State
|
||||
replacementEnabled = [bool]$replacement.Settings.Enabled
|
||||
legacyActionSha256 = $ExpectedLegacyActionSha256
|
||||
}
|
||||
}
|
||||
|
||||
function Write-AtomicReceipt {
|
||||
param([Parameter(Mandatory = $true)][object]$Receipt)
|
||||
|
||||
if (Test-Path -LiteralPath $TemporaryReceiptPath) {
|
||||
Remove-Item -LiteralPath $TemporaryReceiptPath -Force
|
||||
}
|
||||
$Receipt | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath $TemporaryReceiptPath -Encoding UTF8
|
||||
if (Test-Path -LiteralPath $ReceiptPath) { throw "controlled_receipt_identity_already_exists" }
|
||||
Move-Item -LiteralPath $TemporaryReceiptPath -Destination $ReceiptPath
|
||||
}
|
||||
|
||||
foreach ($identity in @($TraceId, $RunId, $WorkItemId)) {
|
||||
if ($identity -notmatch $SafeIdentityPattern -or $identity -in @(".", "..")) {
|
||||
throw "controlled_identity_invalid"
|
||||
}
|
||||
}
|
||||
|
||||
$state = Get-ControlledTaskState
|
||||
|
||||
if ($Mode -eq "Check") {
|
||||
[ordered]@{
|
||||
schemaVersion = "agent99_legacy_sender_quarantine_check_v1"
|
||||
status = "check_ready"
|
||||
mode = "check"
|
||||
traceId = $TraceId
|
||||
runId = $RunId
|
||||
workItemId = $WorkItemId
|
||||
canonicalAssetId = "windows-vmware:host_99"
|
||||
legacyTask = $LegacyTaskName
|
||||
replacementTask = $ReplacementTaskName
|
||||
legacyEnabled = $state.legacyEnabled
|
||||
replacementEnabled = $state.replacementEnabled
|
||||
legacyActionSha256 = $state.legacyActionSha256
|
||||
wouldDisableLegacySender = $state.legacyEnabled
|
||||
remoteWritePerformed = $false
|
||||
secretValueRead = $false
|
||||
nextSafeAction = "apply_once_with_new_run_identity_then_independent_verify"
|
||||
} | ConvertTo-Json -Compress -Depth 6
|
||||
exit 0
|
||||
}
|
||||
|
||||
if ($Mode -eq "Verify") {
|
||||
$receiptAvailable = Test-Path -LiteralPath $ReceiptPath -PathType Leaf
|
||||
$verified = [bool](-not $state.legacyEnabled -and $state.replacementEnabled -and $receiptAvailable)
|
||||
[ordered]@{
|
||||
schemaVersion = "agent99_legacy_sender_quarantine_verify_v1"
|
||||
status = if ($verified) { "quarantine_verified" } else { "quarantine_unverified" }
|
||||
mode = "verify"
|
||||
traceId = $TraceId
|
||||
runId = $RunId
|
||||
workItemId = $WorkItemId
|
||||
canonicalAssetId = "windows-vmware:host_99"
|
||||
legacyEnabled = $state.legacyEnabled
|
||||
replacementEnabled = $state.replacementEnabled
|
||||
durableReceiptAvailable = $receiptAvailable
|
||||
remoteWritePerformed = $false
|
||||
secretValueRead = $false
|
||||
} | ConvertTo-Json -Compress -Depth 6
|
||||
if (-not $verified) { exit 2 }
|
||||
exit 0
|
||||
}
|
||||
|
||||
$modified = $false
|
||||
$rollbackVerified = $false
|
||||
try {
|
||||
if (
|
||||
(Test-Path -LiteralPath $ReceiptPath) -or
|
||||
(Test-Path -LiteralPath $RollbackPath)
|
||||
) {
|
||||
throw "controlled_receipt_identity_already_exists"
|
||||
}
|
||||
New-Item -ItemType Directory -Force -Path $EvidenceRoot | Out-Null
|
||||
Export-ScheduledTask -TaskName $LegacyTaskName -TaskPath "\" |
|
||||
Set-Content -LiteralPath $RollbackPath -Encoding Unicode
|
||||
$rollbackSha256 = (Get-FileHash -LiteralPath $RollbackPath -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
|
||||
if ($state.legacyEnabled) {
|
||||
Disable-ScheduledTask -TaskName $LegacyTaskName -TaskPath "\" | Out-Null
|
||||
$modified = $true
|
||||
}
|
||||
|
||||
$after = Get-ControlledTaskState
|
||||
if ($after.legacyEnabled) { throw "legacy_task_disable_post_verifier_failed" }
|
||||
if (-not $after.replacementEnabled) { throw "canonical_replacement_disabled" }
|
||||
|
||||
$receipt = [ordered]@{
|
||||
schemaVersion = "agent99_legacy_sender_quarantine_receipt_v1"
|
||||
status = "quarantined_verified"
|
||||
traceId = $TraceId
|
||||
runId = $RunId
|
||||
workItemId = $WorkItemId
|
||||
canonicalAssetId = "windows-vmware:host_99"
|
||||
legacyTask = $LegacyTaskName
|
||||
replacementTask = $ReplacementTaskName
|
||||
legacyActionSha256 = $ExpectedLegacyActionSha256
|
||||
rollbackArtifactSha256 = $rollbackSha256
|
||||
legacyEnabledBefore = $state.legacyEnabled
|
||||
legacyEnabledAfter = $after.legacyEnabled
|
||||
replacementEnabledAfter = $after.replacementEnabled
|
||||
remoteWritePerformed = $modified
|
||||
scheduledTaskModification = $modified
|
||||
taskDeleted = $false
|
||||
serviceRestart = $false
|
||||
hostReboot = $false
|
||||
vmPowerChange = $false
|
||||
secretValueRead = $false
|
||||
privateKeyValueRead = $false
|
||||
tokenValueRead = $false
|
||||
environmentSecretRead = $false
|
||||
rollbackAvailable = $true
|
||||
verifierPassed = $true
|
||||
nextSafeAction = "persist_exact_legacy_sender_sunset_policy_in_gitea_and_verify_no_new_wooo_bot_receipts"
|
||||
}
|
||||
Write-AtomicReceipt $receipt
|
||||
$receipt | ConvertTo-Json -Compress -Depth 8
|
||||
exit 0
|
||||
} catch {
|
||||
if ($modified) {
|
||||
try {
|
||||
Enable-ScheduledTask -TaskName $LegacyTaskName -TaskPath "\" | Out-Null
|
||||
$rollbackVerified = [bool](Get-ControlledTaskState).legacyEnabled
|
||||
} catch {
|
||||
$rollbackVerified = $false
|
||||
}
|
||||
}
|
||||
[ordered]@{
|
||||
schemaVersion = "agent99_legacy_sender_quarantine_receipt_v1"
|
||||
status = if ($rollbackVerified) { "failed_rolled_back_verified" } else { "failed_no_write_or_rollback_unverified" }
|
||||
traceId = $TraceId
|
||||
runId = $RunId
|
||||
workItemId = $WorkItemId
|
||||
failureCode = [string]$_.Exception.Message
|
||||
remoteWritePerformed = $modified
|
||||
rollbackVerified = $rollbackVerified
|
||||
secretValueRead = $false
|
||||
hostReboot = $false
|
||||
vmPowerChange = $false
|
||||
} | ConvertTo-Json -Compress -Depth 6
|
||||
exit 2
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
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"
|
||||
|
||||
|
||||
def test_legacy_sender_quarantine_is_exact_bounded_and_rollback_ready() -> None:
|
||||
source = SCRIPT.read_text(encoding="utf-8")
|
||||
|
||||
assert 'ValidateSet("Check", "Apply", "Verify")' in source
|
||||
assert '"Wooo-Agent99-Host188-Performance-Guard"' in source
|
||||
assert '"Wooo-Agent99-Performance-Guard"' in source
|
||||
assert "a2f98560673ebed5aa1f828b4e577c8a34d1c277b2e498044c3526a4ffe15379" in source
|
||||
assert "Export-ScheduledTask" in source
|
||||
assert "Disable-ScheduledTask" in source
|
||||
assert "Enable-ScheduledTask" in source
|
||||
assert "Unregister-ScheduledTask" not in source
|
||||
assert "Stop-ScheduledTask" not in source
|
||||
assert "Restart-Service" not in source
|
||||
assert "Restart-Computer" not in source
|
||||
assert r"api\.telegram\.org|sendMessage|BotToken|ChatId" in source
|
||||
assert "secretValueRead = $false" in source
|
||||
assert "taskDeleted = $false" in source
|
||||
|
||||
|
||||
def test_legacy_sender_quarantine_has_check_apply_verify_receipts() -> None:
|
||||
source = SCRIPT.read_text(encoding="utf-8")
|
||||
|
||||
for schema in (
|
||||
"agent99_legacy_sender_quarantine_check_v1",
|
||||
"agent99_legacy_sender_quarantine_receipt_v1",
|
||||
"agent99_legacy_sender_quarantine_verify_v1",
|
||||
):
|
||||
assert schema in source
|
||||
assert "wouldDisableLegacySender" in source
|
||||
assert "durableReceiptAvailable" in source
|
||||
assert "controlled_receipt_identity_already_exists" in source
|
||||
assert 'identity -in @(".", "..")' in source
|
||||
|
||||
|
||||
def test_task_registration_persists_the_exact_legacy_sender_sunset() -> None:
|
||||
source = REGISTER_TASKS.read_text(encoding="utf-8")
|
||||
|
||||
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_running_refuse_quarantine" in source
|
||||
assert "legacy_host188_sender_quarantine_post_verifier_failed" in source
|
||||
Reference in New Issue
Block a user