fix(agent99): harden Windows99 no-IME transport
All checks were successful
CD Pipeline / workflow-shape (push) Successful in 2s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 2m29s
CD Pipeline / build-and-deploy (push) Successful in 29m59s
AI 技術雷達監控 / ai-technology-watch (push) Successful in 38s
CD Pipeline / post-deploy-checks (push) Successful in 4m20s
AWOOOI Harbor 110 Local Repair / workflow-shape (push) Successful in 1s
AWOOOI Harbor 110 Local Repair / harbor-110-local-repair (push) Successful in 18s
All checks were successful
CD Pipeline / workflow-shape (push) Successful in 2s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 2m29s
CD Pipeline / build-and-deploy (push) Successful in 29m59s
AI 技術雷達監控 / ai-technology-watch (push) Successful in 38s
CD Pipeline / post-deploy-checks (push) Successful in 4m20s
AWOOOI Harbor 110 Local Repair / workflow-shape (push) Successful in 1s
AWOOOI Harbor 110 Local Repair / harbor-110-local-repair (push) Successful in 18s
This commit is contained in:
@@ -142,6 +142,7 @@ $windowsControlBaseline = [IO.File]::ReadAllText((Join-Path $SourceRoot "agent99
|
||||
Add-Check "transport:dedicated_identity" ($control.Contains("sshIdentityFile") -and $control.Contains("IdentitiesOnly=yes")) "dedicated SSH identity is enforced"
|
||||
Add-Check "transport:jump_first" ($control.Contains("preferJumpHost") -and $control.Contains("ssh_jump_fallback")) "preferred jump with bounded fallback is present"
|
||||
Add-Check "transport:stale_process_guard" ($control.Contains("Invoke-AgentStaleSshProcessGuard") -and $control.Contains("taskkill.exe /PID") -and $control.Contains("ssh_stale_process_guard")) "timed-out and stale SSH clients are bounded"
|
||||
Add-Check "transport:stale_windows_probe_guard" ($control.Contains("Invoke-AgentStaleWindowsProbeProcessGuard") -and $control.Contains("windows_probe_process_guard") -and $control.Contains("commandLineCaptured = `$false")) "legacy stdin probes are matched without command-line capture and cleared in bounded batches"
|
||||
Add-Check "transport:cross_process_serialization" ($control.Contains("Enter-AgentSshTransportLock") -and $control.Contains("[IO.FileShare]::None") -and $control.Contains("ssh_transport_lock_failed")) "cross-process SSH sessions are serialized"
|
||||
Add-Check "windows99:input_independent_control" ($windowsControlBaseline.Contains('$FallbackInputTip = "0409:00000409"') -and $windowsControlBaseline.Contains('$PreservedChineseLanguage = "zh-Hant-TW"') -and $windowsControlBaseline.Contains('encodedCommandCompatible = $true') -and $windowsControlBaseline.Contains('inputMethodIndependent = $true') -and $windowsControlBaseline.Contains('Invoke-IndependentSnapshot') -and $windowsControlBaseline.Contains('Restore-WindowsControlBaseline') -and $windowsControlBaseline.Contains('$Mode -eq "Check" -and $runtimeVerified')) "Windows99 keeps Traditional Chinese/Bopomofo while enforcing a US-keyboard and UTF-8 fallback with an independent verifier, fail-closed drift detection, and rollback"
|
||||
Add-Check "windows99:selfcheck_control_baseline" ($control.Contains('function Test-AgentWindowsControlBaseline') -and $control.Contains('$windowsControlBaseline = Test-AgentWindowsControlBaseline $runtimeManifest') -and $control.Contains('windows_control_baseline_verified_no_write') -and $control.Contains('windowsControlBaseline = $windowsControlBaseline')) "Agent99 SelfCheck continuously verifies the Windows99 keyboard, UTF-8 console, and SSH control baseline without applying writes"
|
||||
|
||||
@@ -472,7 +472,7 @@ function Get-AgentSshProcessGuardConfig {
|
||||
function Invoke-AgentStaleSshProcessGuard {
|
||||
$guard = Get-AgentSshProcessGuardConfig
|
||||
if (-not $guard.enabled -or -not $ControlledApply) {
|
||||
return [pscustomobject]@{ ok = $true; skipped = $true; reason = "guard_disabled_or_observe_mode"; stoppedCount = 0; remainingCount = $null; processes = @() }
|
||||
return [pscustomobject]@{ ok = $true; skipped = $true; reason = "guard_disabled_or_observe_mode"; runtimeWritePerformed = $false; stoppedCount = 0; remainingCount = $null; processes = @() }
|
||||
}
|
||||
|
||||
$targets = @($Config.hosts)
|
||||
@@ -522,6 +522,7 @@ function Invoke-AgentStaleSshProcessGuard {
|
||||
$result = [pscustomobject]@{
|
||||
ok = [bool]($failures -eq 0)
|
||||
skipped = $false
|
||||
runtimeWritePerformed = [bool]($stoppedCount -gt 0)
|
||||
stoppedCount = $stoppedCount
|
||||
failureCount = $failures
|
||||
remainingCount = $remainingCount
|
||||
@@ -535,6 +536,154 @@ function Invoke-AgentStaleSshProcessGuard {
|
||||
$result
|
||||
}
|
||||
|
||||
function Get-AgentWindowsProbeProcessGuardConfig {
|
||||
$configured = if ($Config.PSObject.Properties["windowsProbeProcessGuard"]) { $Config.windowsProbeProcessGuard } else { $null }
|
||||
[pscustomobject]@{
|
||||
enabled = [bool](-not $configured -or -not $configured.PSObject.Properties["enabled"] -or (Convert-AgentBool $configured.enabled))
|
||||
staleMinutes = if ($configured -and $configured.PSObject.Properties["staleMinutes"]) { [math]::Max(5, [int]$configured.staleMinutes) } else { 10 }
|
||||
maxStopsPerRun = if ($configured -and $configured.PSObject.Properties["maxStopsPerRun"]) { [math]::Min(50, [math]::Max(1, [int]$configured.maxStopsPerRun)) } else { 20 }
|
||||
}
|
||||
}
|
||||
|
||||
function Get-AgentStaleWindowsProbeProcessReadback {
|
||||
$guard = Get-AgentWindowsProbeProcessGuardConfig
|
||||
if (-not $guard.enabled) {
|
||||
return [pscustomobject]@{
|
||||
ok = $true
|
||||
skipped = $true
|
||||
reason = "guard_disabled"
|
||||
staleMinutes = $guard.staleMinutes
|
||||
staleCount = 0
|
||||
processes = @()
|
||||
commandLineCaptured = $false
|
||||
runtimeWritePerformed = $false
|
||||
}
|
||||
}
|
||||
|
||||
$rowsByPid = @{}
|
||||
try {
|
||||
foreach ($probeMode in @("Verify", "Check")) {
|
||||
# WMI performs the command-line match but returns no command-line field.
|
||||
$query = "SELECT ProcessId,ParentProcessId,CreationDate,Name FROM Win32_Process WHERE (Name='powershell.exe' OR Name='pwsh.exe') AND CommandLine LIKE '%scriptblock%Create%Console%ReadToEnd%' AND CommandLine LIKE '%-Mode $probeMode%'"
|
||||
foreach ($processInfo in @(Get-CimInstance -Query $query -ErrorAction Stop)) {
|
||||
$pidKey = [string]$processInfo.ProcessId
|
||||
if ($rowsByPid.ContainsKey($pidKey)) { continue }
|
||||
$process = Get-Process -Id $processInfo.ProcessId -ErrorAction SilentlyContinue
|
||||
if (-not $process) { continue }
|
||||
$parentInfo = Get-CimInstance -Query "SELECT Name FROM Win32_Process WHERE ProcessId=$($processInfo.ParentProcessId)" -ErrorAction SilentlyContinue
|
||||
$parentName = if ($parentInfo) { [string]$parentInfo.Name } else { "" }
|
||||
$ageMinutes = [math]::Round(((Get-Date) - $process.StartTime).TotalMinutes, 1)
|
||||
$eligible = [bool]($parentName -ieq "cmd.exe" -and $ageMinutes -ge $guard.staleMinutes)
|
||||
$rowsByPid[$pidKey] = [pscustomobject]@{
|
||||
pid = [int]$processInfo.ProcessId
|
||||
parentPid = [int]$processInfo.ParentProcessId
|
||||
parentName = $parentName
|
||||
mode = $probeMode
|
||||
ageMinutes = $ageMinutes
|
||||
workingSetMiB = [math]::Round($process.WorkingSet64 / 1MB, 1)
|
||||
eligible = $eligible
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
return [pscustomobject]@{
|
||||
ok = $false
|
||||
skipped = $false
|
||||
reason = "windows_probe_process_readback_failed"
|
||||
staleMinutes = $guard.staleMinutes
|
||||
staleCount = 0
|
||||
processes = @()
|
||||
commandLineCaptured = $false
|
||||
runtimeWritePerformed = $false
|
||||
}
|
||||
}
|
||||
|
||||
$rows = @($rowsByPid.Values | Sort-Object ageMinutes -Descending)
|
||||
$stale = @($rows | Where-Object { $_.eligible })
|
||||
[pscustomobject]@{
|
||||
ok = $true
|
||||
skipped = $false
|
||||
reason = if ($stale.Count -eq 0) { "no_stale_legacy_windows_probes" } else { "stale_legacy_windows_probes_detected" }
|
||||
staleMinutes = $guard.staleMinutes
|
||||
staleCount = $stale.Count
|
||||
totalWorkingSetMiB = [math]::Round(($stale | Measure-Object -Property workingSetMiB -Sum).Sum, 1)
|
||||
processes = $stale
|
||||
commandLineCaptured = $false
|
||||
runtimeWritePerformed = $false
|
||||
}
|
||||
}
|
||||
|
||||
function Invoke-AgentStaleWindowsProbeProcessGuard {
|
||||
$guard = Get-AgentWindowsProbeProcessGuardConfig
|
||||
if (-not $guard.enabled -or -not $ControlledApply) {
|
||||
return [pscustomobject]@{
|
||||
ok = $true
|
||||
skipped = $true
|
||||
reason = "guard_disabled_or_observe_mode"
|
||||
stoppedCount = 0
|
||||
remainingStaleCount = $null
|
||||
commandLineCaptured = $false
|
||||
runtimeWritePerformed = $false
|
||||
}
|
||||
}
|
||||
|
||||
$before = Get-AgentStaleWindowsProbeProcessReadback
|
||||
if (-not $before.ok) {
|
||||
return [pscustomobject]@{
|
||||
ok = $false
|
||||
skipped = $false
|
||||
reason = $before.reason
|
||||
stoppedCount = 0
|
||||
failureCount = 0
|
||||
remainingStaleCount = $null
|
||||
commandLineCaptured = $false
|
||||
runtimeWritePerformed = $false
|
||||
}
|
||||
}
|
||||
|
||||
$attempts = @()
|
||||
foreach ($candidate in @($before.processes | Select-Object -First $guard.maxStopsPerRun)) {
|
||||
$stopped = $false
|
||||
try {
|
||||
Stop-Process -Id $candidate.pid -Force -ErrorAction Stop
|
||||
try { Wait-Process -Id $candidate.pid -Timeout 5 -ErrorAction SilentlyContinue } catch {}
|
||||
$stopped = [bool](-not (Get-Process -Id $candidate.pid -ErrorAction SilentlyContinue))
|
||||
} catch {
|
||||
$stopped = [bool](-not (Get-Process -Id $candidate.pid -ErrorAction SilentlyContinue))
|
||||
}
|
||||
$attempts += [pscustomobject]@{
|
||||
pid = $candidate.pid
|
||||
mode = $candidate.mode
|
||||
ageMinutes = $candidate.ageMinutes
|
||||
stopped = $stopped
|
||||
}
|
||||
}
|
||||
|
||||
$after = Get-AgentStaleWindowsProbeProcessReadback
|
||||
$stoppedCount = @($attempts | Where-Object { $_.stopped }).Count
|
||||
$failureCount = @($attempts | Where-Object { -not $_.stopped }).Count
|
||||
$remainingStaleCount = if ($after.ok) { [int]$after.staleCount } else { $null }
|
||||
$ok = [bool]($after.ok -and $failureCount -eq 0 -and $remainingStaleCount -eq 0)
|
||||
$result = [pscustomobject]@{
|
||||
ok = $ok
|
||||
skipped = $false
|
||||
reason = if ($ok) { "stale_legacy_windows_probes_cleared" } elseif ($remainingStaleCount -gt 0) { "bounded_batch_remaining" } else { "stale_legacy_windows_probe_stop_failed" }
|
||||
staleMinutes = $guard.staleMinutes
|
||||
maxStopsPerRun = $guard.maxStopsPerRun
|
||||
candidateCount = $before.staleCount
|
||||
stoppedCount = $stoppedCount
|
||||
failureCount = $failureCount
|
||||
remainingStaleCount = $remainingStaleCount
|
||||
attempts = $attempts
|
||||
commandLineCaptured = $false
|
||||
runtimeWritePerformed = [bool]($stoppedCount -gt 0)
|
||||
}
|
||||
if ($before.staleCount -gt 0 -or $failureCount -gt 0) {
|
||||
Record-AgentEvent "windows_probe_process_guard" $(if ($failureCount -gt 0) { "warning" } else { "info" }) "candidates=$($before.staleCount) stopped=$stoppedCount failures=$failureCount remaining=$remainingStaleCount" $result -NoAlert
|
||||
}
|
||||
$result
|
||||
}
|
||||
|
||||
function Get-AgentObjectValue {
|
||||
param(
|
||||
[object]$Data,
|
||||
@@ -4372,6 +4521,7 @@ function Test-AgentSelfHealth {
|
||||
$latestPerf = Test-AgentLatestPerformanceEvidence $selfConfig.requiredHosts
|
||||
$runtimeManifest = Test-AgentRuntimeManifest
|
||||
$windowsControlBaseline = Test-AgentWindowsControlBaseline $runtimeManifest
|
||||
$windowsProbeProcesses = Get-AgentStaleWindowsProbeProcessReadback
|
||||
$runningQueue = Get-AgentRunningQueueHealth
|
||||
$telegram = if ($selfConfig.requireRecentTelegramDelivery) {
|
||||
Test-AgentRecentTelegramDelivery $selfConfig.evidenceFreshness
|
||||
@@ -4396,11 +4546,13 @@ function Test-AgentSelfHealth {
|
||||
if ($telegram.severity -eq "critical") { $critical += 1 }
|
||||
if ($runtimeManifest.severity -eq "critical") { $critical += 1 }
|
||||
if ($windowsControlBaseline.severity -eq "critical") { $critical += 1 }
|
||||
if (-not $windowsProbeProcesses.ok) { $critical += 1 }
|
||||
if ($runningQueue.severity -eq "critical") { $critical += 1 }
|
||||
$warning = $evidenceWarning
|
||||
if ($windowsProbeProcesses.ok -and $windowsProbeProcesses.staleCount -gt 0) { $warning += 1 }
|
||||
|
||||
$severity = if ($critical -gt 0) { "critical" } elseif ($warning -gt 0) { "warning" } else { "ok" }
|
||||
$message = "selfHealth severity=$severity taskFailures=$taskFailures evidenceCritical=$evidenceCritical evidenceWarning=$evidenceWarning perfSignal=$($latestPerf.severity) perfReadbackFailures=$($perfReadbackFailures.Count) telegram=$($telegram.severity) runtimeManifest=$($runtimeManifest.severity) windowsControlBaseline=$($windowsControlBaseline.severity) staleRunningQueue=$($runningQueue.staleCount)"
|
||||
$message = "selfHealth severity=$severity taskFailures=$taskFailures evidenceCritical=$evidenceCritical evidenceWarning=$evidenceWarning perfSignal=$($latestPerf.severity) perfReadbackFailures=$($perfReadbackFailures.Count) telegram=$($telegram.severity) runtimeManifest=$($runtimeManifest.severity) windowsControlBaseline=$($windowsControlBaseline.severity) staleWindowsProbes=$($windowsProbeProcesses.staleCount) staleRunningQueue=$($runningQueue.staleCount)"
|
||||
Record-AgentEvent "agent_selfcheck" $(if ($severity -eq "ok") { "info" } else { $severity }) $message $null -Alert
|
||||
|
||||
[pscustomobject]@{
|
||||
@@ -4412,6 +4564,7 @@ function Test-AgentSelfHealth {
|
||||
telegramDelivery = $telegram
|
||||
runtimeManifest = $runtimeManifest
|
||||
windowsControlBaseline = $windowsControlBaseline
|
||||
windowsProbeProcesses = $windowsProbeProcesses
|
||||
runningQueue = $runningQueue
|
||||
summary = [pscustomobject]@{
|
||||
taskFailures = $taskFailures
|
||||
@@ -4427,6 +4580,9 @@ function Test-AgentSelfHealth {
|
||||
windowsControlBaselineSeverity = $windowsControlBaseline.severity
|
||||
windowsControlBaselineReason = $windowsControlBaseline.reason
|
||||
windowsControlBaselineEvidenceRef = $windowsControlBaseline.evidenceRef
|
||||
windowsProbeProcessReadbackOk = $windowsProbeProcesses.ok
|
||||
windowsProbeProcessStaleCount = $windowsProbeProcesses.staleCount
|
||||
windowsProbeProcessWorkingSetMiB = $windowsProbeProcesses.totalWorkingSetMiB
|
||||
runningQueueActiveCount = $runningQueue.activeCount
|
||||
runningQueueStaleCount = $runningQueue.staleCount
|
||||
runningQueueThresholdSeconds = $runningQueue.thresholdSeconds
|
||||
@@ -8617,6 +8773,7 @@ $providerFreshness = $null
|
||||
$securityTriage = $null
|
||||
$controlTick = $null
|
||||
$sshProcessGuard = $null
|
||||
$windowsProbeProcessGuard = $null
|
||||
$bootState = $null
|
||||
$recoveryTrigger = $null
|
||||
$recoverySlo = $null
|
||||
@@ -8764,6 +8921,7 @@ if ($Mode -in @("Status", "Recover", "Perf")) {
|
||||
|
||||
if ($Mode -in @("Perf", "LoadShed") -and $ControlledApply) {
|
||||
$sshProcessGuard = Invoke-AgentStaleSshProcessGuard
|
||||
$windowsProbeProcessGuard = Invoke-AgentStaleWindowsProbeProcessGuard
|
||||
}
|
||||
|
||||
if ($Mode -in @("Status", "Recover", "Perf", "LoadShed")) {
|
||||
@@ -8864,9 +9022,12 @@ $result = [pscustomobject]@{
|
||||
($controlTick -and $controlTick.staleRunningRecovery -and $controlTick.staleRunningRecovery.runtimeWritePerformed) -or
|
||||
@($externalHostRecovery | Where-Object { $_.runtimeWritePerformed }).Count -gt 0 -or
|
||||
($host112Recovery -and $host112Recovery.runtimeWritePerformed) -or
|
||||
($host188EdgeRecovery -and $host188EdgeRecovery.runtimeWritePerformed)
|
||||
($host188EdgeRecovery -and $host188EdgeRecovery.runtimeWritePerformed) -or
|
||||
($sshProcessGuard -and $sshProcessGuard.runtimeWritePerformed) -or
|
||||
($windowsProbeProcessGuard -and $windowsProbeProcessGuard.runtimeWritePerformed)
|
||||
)
|
||||
sshProcessGuard = $sshProcessGuard
|
||||
windowsProbeProcessGuard = $windowsProbeProcessGuard
|
||||
bootState = $bootState
|
||||
recoveryTrigger = $recoveryTrigger
|
||||
recoverySlo = $recoverySlo
|
||||
|
||||
@@ -428,6 +428,10 @@ try {
|
||||
Add-DefaultProperty $config.sshProcessGuard "enabled" $true
|
||||
Add-DefaultProperty $config.sshProcessGuard "staleMinutes" 15
|
||||
Add-DefaultProperty $config.sshProcessGuard "hardStaleMinutes" 60
|
||||
Add-DefaultProperty $config "windowsProbeProcessGuard" ([pscustomobject]@{})
|
||||
Add-DefaultProperty $config.windowsProbeProcessGuard "enabled" $true
|
||||
Add-DefaultProperty $config.windowsProbeProcessGuard "staleMinutes" 10
|
||||
Add-DefaultProperty $config.windowsProbeProcessGuard "maxStopsPerRun" 20
|
||||
Add-DefaultProperty $config "hosts" @("192.168.0.110", "192.168.0.111", "192.168.0.112", "192.168.0.120", "192.168.0.121", "192.168.0.188")
|
||||
$configuredHosts = @($config.hosts | ForEach-Object { [string]$_ } | Where-Object { $_ })
|
||||
if ("192.168.0.111" -notin $configuredHosts) {
|
||||
|
||||
@@ -13,6 +13,11 @@
|
||||
"staleMinutes": 15,
|
||||
"hardStaleMinutes": 60
|
||||
},
|
||||
"windowsProbeProcessGuard": {
|
||||
"enabled": true,
|
||||
"staleMinutes": 10,
|
||||
"maxStopsPerRun": 20
|
||||
},
|
||||
"sshUsers": {
|
||||
"192.168.0.111": "ooo",
|
||||
"192.168.0.112": "kali",
|
||||
|
||||
@@ -554,6 +554,34 @@ def test_agent99_bounds_stale_ssh_processes_without_touching_tunnels() -> None:
|
||||
}
|
||||
|
||||
|
||||
def test_agent99_bounds_legacy_windows_probe_processes_without_command_capture() -> None:
|
||||
source = CONTROL.read_text(encoding="utf-8")
|
||||
config = json.loads((ROOT / "agent99.config.99.example.json").read_text())
|
||||
deploy = DEPLOY.read_text(encoding="utf-8")
|
||||
guard = source[source.index("function Get-AgentWindowsProbeProcessGuardConfig") :]
|
||||
guard = guard[: guard.index("function Get-AgentObjectValue")]
|
||||
|
||||
assert "SELECT ProcessId,ParentProcessId,CreationDate,Name" in guard
|
||||
assert "CommandLine LIKE '%scriptblock%Create%Console%ReadToEnd%'" in guard
|
||||
assert "CommandLine LIKE '%-Mode $probeMode%'" in guard
|
||||
assert 'parentName -ieq "cmd.exe"' in guard
|
||||
assert "Select-Object -First $guard.maxStopsPerRun" in guard
|
||||
assert "Stop-Process -Id $candidate.pid -Force" in guard
|
||||
assert "commandLineCaptured = $false" in guard
|
||||
assert "CommandLine =" not in guard
|
||||
assert 'Record-AgentEvent "windows_probe_process_guard"' in guard
|
||||
assert config["windowsProbeProcessGuard"] == {
|
||||
"enabled": True,
|
||||
"staleMinutes": 10,
|
||||
"maxStopsPerRun": 20,
|
||||
}
|
||||
assert 'Add-DefaultProperty $config "windowsProbeProcessGuard"' in deploy
|
||||
assert 'Add-DefaultProperty $config.windowsProbeProcessGuard "staleMinutes" 10' in deploy
|
||||
assert 'Add-DefaultProperty $config.windowsProbeProcessGuard "maxStopsPerRun" 20' in deploy
|
||||
assert "$windowsProbeProcesses = Get-AgentStaleWindowsProbeProcessReadback" in source
|
||||
assert "windowsProbeProcessStaleCount = $windowsProbeProcesses.staleCount" in source
|
||||
|
||||
|
||||
def test_agent99_serializes_ssh_across_scheduled_task_processes() -> None:
|
||||
source = CONTROL.read_text(encoding="utf-8")
|
||||
config = json.loads((ROOT / "agent99.config.99.example.json").read_text())
|
||||
|
||||
@@ -16,7 +16,10 @@ if [[ -n "${WINDOWS99_MAX_AUTH_USERS:-}" ]]; then
|
||||
fi
|
||||
KNOWN_HOSTS_FILE="${WINDOWS99_KNOWN_HOSTS_FILE:-${HOME:-/home/wooo}/.ssh/known_hosts}"
|
||||
LOCAL_VERIFY_SCRIPT="${WINDOWS99_LOCAL_VERIFY_SCRIPT:-${SCRIPT_DIR}/windows99-vmware-autostart.ps1}"
|
||||
REMOTE_VERIFY_COMMAND="${WINDOWS99_REMOTE_VERIFY_COMMAND:-powershell -NoProfile -ExecutionPolicy Bypass -Command \"& ([scriptblock]::Create([Console]::In.ReadToEnd())) -Mode Verify\"}"
|
||||
REMOTE_VERIFY_COMMAND_OVERRIDE="${WINDOWS99_REMOTE_VERIFY_COMMAND:-}"
|
||||
REMOTE_VERIFY_COMMAND=""
|
||||
REMOTE_VERIFY_MODE="encoded_command_bounded_job_base64_line"
|
||||
REMOTE_TRANSPORT_READY=0
|
||||
SSH_USERS=(Administrator ogt wooo ooo administrator)
|
||||
SSH_USERS_EXPLICIT=0
|
||||
|
||||
@@ -39,6 +42,7 @@ fi
|
||||
if ! is_positive_int "${REMOTE_VERIFY_TIMEOUT}"; then
|
||||
REMOTE_VERIFY_TIMEOUT=45
|
||||
fi
|
||||
REMOTE_VERIFY_JOB_TIMEOUT=$((REMOTE_VERIFY_TIMEOUT > 20 ? REMOTE_VERIFY_TIMEOUT - 15 : 5))
|
||||
if ! is_positive_int "${MAX_AUTH_USERS}"; then
|
||||
MAX_AUTH_USERS="${#SSH_USERS[@]}"
|
||||
fi
|
||||
@@ -111,6 +115,71 @@ if ! is_positive_int "${MAX_AUTH_USERS}"; then
|
||||
MAX_AUTH_USERS=2
|
||||
fi
|
||||
|
||||
build_bounded_powershell_command() {
|
||||
local probe_mode="$1"
|
||||
local timeout_seconds="$2"
|
||||
local template encoded
|
||||
template="$(cat <<'POWERSHELL'
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$ProgressPreference = 'SilentlyContinue'
|
||||
[Console]::OutputEncoding = [Text.UTF8Encoding]::new($false)
|
||||
$OutputEncoding = [Console]::OutputEncoding
|
||||
$sourceLine = [Console]::In.ReadLine()
|
||||
$sourceText = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($sourceLine))
|
||||
$job = $null
|
||||
$exitCode = 0
|
||||
try {
|
||||
$job = Start-Job -ScriptBlock {
|
||||
param($probeSource, $probeMode)
|
||||
$ProgressPreference = 'SilentlyContinue'
|
||||
[Console]::OutputEncoding = [Text.UTF8Encoding]::new($false)
|
||||
$OutputEncoding = [Console]::OutputEncoding
|
||||
& ([scriptblock]::Create($probeSource)) -Mode $probeMode
|
||||
} -ArgumentList $sourceText, '__MODE__'
|
||||
$completed = Wait-Job -Job $job -Timeout __TIMEOUT__
|
||||
if ($null -eq $completed) {
|
||||
[Console]::Error.WriteLine('AGENT99_REMOTE_PROBE_TIMEOUT=1')
|
||||
$exitCode = 124
|
||||
} else {
|
||||
Receive-Job -Job $job -ErrorAction Continue
|
||||
if ($job.State -ne 'Completed') { $exitCode = 1 }
|
||||
}
|
||||
} catch {
|
||||
[Console]::Error.WriteLine('AGENT99_REMOTE_PROBE_FAILED=1')
|
||||
$exitCode = 1
|
||||
} finally {
|
||||
if ($null -ne $job) {
|
||||
if ($job.State -eq 'Running') { Stop-Job -Job $job -ErrorAction SilentlyContinue }
|
||||
Remove-Job -Job $job -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
exit $exitCode
|
||||
POWERSHELL
|
||||
)"
|
||||
template="${template//__MODE__/${probe_mode}}"
|
||||
template="${template//__TIMEOUT__/${timeout_seconds}}"
|
||||
encoded="$(printf '%s' "${template}" | iconv -f UTF-8 -t UTF-16LE | base64 | tr -d '\r\n')" || return 1
|
||||
[[ -n "${encoded}" ]] || return 1
|
||||
printf '%s' "powershell -NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass -EncodedCommand ${encoded}"
|
||||
}
|
||||
|
||||
encode_probe_source_line() {
|
||||
base64 <"$1" | tr -d '\r\n'
|
||||
printf '\n'
|
||||
}
|
||||
|
||||
if [[ "${REMOTE_VERIFY_COMMAND_OVERRIDE}" == *"ReadToEnd"* ]]; then
|
||||
REMOTE_VERIFY_MODE="unsafe_legacy_override_rejected"
|
||||
elif [[ -n "${REMOTE_VERIFY_COMMAND_OVERRIDE}" ]]; then
|
||||
REMOTE_VERIFY_COMMAND="${REMOTE_VERIFY_COMMAND_OVERRIDE}"
|
||||
REMOTE_VERIFY_MODE="explicit_command_override"
|
||||
REMOTE_TRANSPORT_READY=1
|
||||
elif command -v iconv >/dev/null 2>&1 && command -v base64 >/dev/null 2>&1; then
|
||||
if REMOTE_VERIFY_COMMAND="$(build_bounded_powershell_command Verify "${REMOTE_VERIFY_JOB_TIMEOUT}")"; then
|
||||
REMOTE_TRANSPORT_READY=1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "${MODE}" != "check" && "${MODE}" != "collect" ]]; then
|
||||
printf '%s\n' "error=invalid_mode:${MODE}" >&2
|
||||
exit 64
|
||||
@@ -293,6 +362,12 @@ elif [[ "${SSH_BATCHMODE_AUTH_READY}" != "1" ]]; then
|
||||
if [[ "${MODE}" == "collect" ]]; then
|
||||
PROCESS_EXIT_STATUS=75
|
||||
fi
|
||||
elif [[ "${REMOTE_TRANSPORT_READY}" != "1" ]]; then
|
||||
VERIFY_COLLECTION_STATUS="blocked_local_transport_encoder_missing"
|
||||
SAFE_NEXT_STEP="install_or_restore_local_iconv_and_base64_then_rerun_no_secret_no_remote_write"
|
||||
if [[ "${MODE}" == "collect" ]]; then
|
||||
PROCESS_EXIT_STATUS=75
|
||||
fi
|
||||
elif [[ "${MODE}" == "check" ]]; then
|
||||
VERIFY_COLLECTION_STATUS="ready_ssh_batchmode_auth_probe_only"
|
||||
SAFE_NEXT_STEP="rerun_collector_with_collect_then_commit_no_secret_verify_artifact_and_scorecard_rerun"
|
||||
@@ -304,7 +379,7 @@ elif [[ "${LOCAL_VERIFY_SCRIPT_PRESENT}" != "1" ]]; then
|
||||
else
|
||||
DRY_RUN="false"
|
||||
REMOTE_VERIFY_ATTEMPTED=1
|
||||
if REMOTE_VERIFY_OUTPUT="$(run_ssh_timed "${SSH_AUTHENTICATED_USER}" "${REMOTE_VERIFY_TIMEOUT}" "${REMOTE_VERIFY_COMMAND}" <"${LOCAL_VERIFY_SCRIPT}" 2>&1)"; then
|
||||
if REMOTE_VERIFY_OUTPUT="$(encode_probe_source_line "${LOCAL_VERIFY_SCRIPT}" | run_ssh_timed "${SSH_AUTHENTICATED_USER}" "${REMOTE_VERIFY_TIMEOUT}" "${REMOTE_VERIFY_COMMAND}" 2>&1)"; then
|
||||
REMOTE_VERIFY_OUTPUT="${REMOTE_VERIFY_OUTPUT//$'\r'/}"
|
||||
REMOTE_VERIFY_EXIT_STATUS=0
|
||||
if grep -q '^AWOOOI_WINDOWS99_VMWARE_AUTOSTART=1$' <<<"${REMOTE_VERIFY_OUTPUT}" && grep -q '^MODE=Verify$' <<<"${REMOTE_VERIFY_OUTPUT}"; then
|
||||
@@ -331,6 +406,7 @@ printf '%s\n' "via_host=${VIA_HOST:-none}"
|
||||
printf '%s\n' "connect_timeout_seconds=${CONNECT_TIMEOUT}"
|
||||
printf '%s\n' "ssh_timeout_seconds=${SSH_TIMEOUT}"
|
||||
printf '%s\n' "remote_verify_timeout_seconds=${REMOTE_VERIFY_TIMEOUT}"
|
||||
printf '%s\n' "remote_verify_job_timeout_seconds=${REMOTE_VERIFY_JOB_TIMEOUT}"
|
||||
printf '%s\n' "port_timeout_wrapper=${PORT_TIMEOUT_WRAPPER}"
|
||||
printf '%s\n' "ssh_auth_probe_user_limit=${MAX_AUTH_USERS}"
|
||||
printf '%s\n' "ssh_timeout_wrapper=${SSH_TIMEOUT_WRAPPER}"
|
||||
@@ -345,7 +421,8 @@ printf '%s\n' "ssh_batchmode_auth_ready=${SSH_BATCHMODE_AUTH_READY}"
|
||||
printf '%s\n' "ssh_authenticated_user=${SSH_AUTHENTICATED_USER}"
|
||||
printf '%s\n' "ssh_auth_probe_exit_status=${SSH_AUTH_PROBE_EXIT_STATUS}"
|
||||
printf '%s\n' "ssh_auth_probe_stdout_present=${SSH_AUTH_PROBE_STDOUT_PRESENT}"
|
||||
printf '%s\n' "remote_verify_mode=in_memory_stdin_scriptblock"
|
||||
printf '%s\n' "remote_verify_mode=${REMOTE_VERIFY_MODE}"
|
||||
printf '%s\n' "remote_transport_ready=${REMOTE_TRANSPORT_READY}"
|
||||
printf '%s\n' "local_verify_script_present=${LOCAL_VERIFY_SCRIPT_PRESENT}"
|
||||
printf '%s\n' "remote_verify_attempted=${REMOTE_VERIFY_ATTEMPTED}"
|
||||
printf '%s\n' "remote_verify_exit_status=${REMOTE_VERIFY_EXIT_STATUS}"
|
||||
|
||||
@@ -17,7 +17,10 @@ if [[ -n "${WINDOWS99_MAX_AUTH_USERS:-}" ]]; then
|
||||
fi
|
||||
KNOWN_HOSTS_FILE="${WINDOWS99_KNOWN_HOSTS_FILE:-/tmp/awoooi-windows99-known_hosts}"
|
||||
LOCAL_LOCATOR_SCRIPT="${WINDOWS99_LOCAL_LOCATOR_SCRIPT:-${SCRIPT_DIR}/windows99-vmx-source-locator.ps1}"
|
||||
REMOTE_LOCATOR_COMMAND="${WINDOWS99_REMOTE_LOCATOR_COMMAND:-powershell -NoProfile -ExecutionPolicy Bypass -Command \"& ([scriptblock]::Create([Console]::In.ReadToEnd())) -Mode Check\"}"
|
||||
REMOTE_LOCATOR_COMMAND_OVERRIDE="${WINDOWS99_REMOTE_LOCATOR_COMMAND:-}"
|
||||
REMOTE_LOCATOR_COMMAND=""
|
||||
REMOTE_LOCATOR_MODE="encoded_command_bounded_job_base64_line"
|
||||
REMOTE_TRANSPORT_READY=0
|
||||
SSH_USERS=(ogt wooo ooo administrator Administrator)
|
||||
SSH_USERS_EXPLICIT=0
|
||||
|
||||
@@ -93,6 +96,7 @@ fi
|
||||
if ! is_positive_int "${REMOTE_LOCATOR_TIMEOUT}"; then
|
||||
REMOTE_LOCATOR_TIMEOUT=60
|
||||
fi
|
||||
REMOTE_LOCATOR_JOB_TIMEOUT=$((REMOTE_LOCATOR_TIMEOUT > 20 ? REMOTE_LOCATOR_TIMEOUT - 15 : 5))
|
||||
if ! is_positive_int "${MAX_AUTH_USERS}"; then
|
||||
MAX_AUTH_USERS="${#SSH_USERS[@]}"
|
||||
fi
|
||||
@@ -103,6 +107,71 @@ if ! is_positive_int "${MAX_AUTH_USERS}"; then
|
||||
MAX_AUTH_USERS=2
|
||||
fi
|
||||
|
||||
build_bounded_powershell_command() {
|
||||
local probe_mode="$1"
|
||||
local timeout_seconds="$2"
|
||||
local template encoded
|
||||
template="$(cat <<'POWERSHELL'
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$ProgressPreference = 'SilentlyContinue'
|
||||
[Console]::OutputEncoding = [Text.UTF8Encoding]::new($false)
|
||||
$OutputEncoding = [Console]::OutputEncoding
|
||||
$sourceLine = [Console]::In.ReadLine()
|
||||
$sourceText = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($sourceLine))
|
||||
$job = $null
|
||||
$exitCode = 0
|
||||
try {
|
||||
$job = Start-Job -ScriptBlock {
|
||||
param($probeSource, $probeMode)
|
||||
$ProgressPreference = 'SilentlyContinue'
|
||||
[Console]::OutputEncoding = [Text.UTF8Encoding]::new($false)
|
||||
$OutputEncoding = [Console]::OutputEncoding
|
||||
& ([scriptblock]::Create($probeSource)) -Mode $probeMode
|
||||
} -ArgumentList $sourceText, '__MODE__'
|
||||
$completed = Wait-Job -Job $job -Timeout __TIMEOUT__
|
||||
if ($null -eq $completed) {
|
||||
[Console]::Error.WriteLine('AGENT99_REMOTE_PROBE_TIMEOUT=1')
|
||||
$exitCode = 124
|
||||
} else {
|
||||
Receive-Job -Job $job -ErrorAction Continue
|
||||
if ($job.State -ne 'Completed') { $exitCode = 1 }
|
||||
}
|
||||
} catch {
|
||||
[Console]::Error.WriteLine('AGENT99_REMOTE_PROBE_FAILED=1')
|
||||
$exitCode = 1
|
||||
} finally {
|
||||
if ($null -ne $job) {
|
||||
if ($job.State -eq 'Running') { Stop-Job -Job $job -ErrorAction SilentlyContinue }
|
||||
Remove-Job -Job $job -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
exit $exitCode
|
||||
POWERSHELL
|
||||
)"
|
||||
template="${template//__MODE__/${probe_mode}}"
|
||||
template="${template//__TIMEOUT__/${timeout_seconds}}"
|
||||
encoded="$(printf '%s' "${template}" | iconv -f UTF-8 -t UTF-16LE | base64 | tr -d '\r\n')" || return 1
|
||||
[[ -n "${encoded}" ]] || return 1
|
||||
printf '%s' "powershell -NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass -EncodedCommand ${encoded}"
|
||||
}
|
||||
|
||||
encode_probe_source_line() {
|
||||
base64 <"$1" | tr -d '\r\n'
|
||||
printf '\n'
|
||||
}
|
||||
|
||||
if [[ "${REMOTE_LOCATOR_COMMAND_OVERRIDE}" == *"ReadToEnd"* ]]; then
|
||||
REMOTE_LOCATOR_MODE="unsafe_legacy_override_rejected"
|
||||
elif [[ -n "${REMOTE_LOCATOR_COMMAND_OVERRIDE}" ]]; then
|
||||
REMOTE_LOCATOR_COMMAND="${REMOTE_LOCATOR_COMMAND_OVERRIDE}"
|
||||
REMOTE_LOCATOR_MODE="explicit_command_override"
|
||||
REMOTE_TRANSPORT_READY=1
|
||||
elif command -v iconv >/dev/null 2>&1 && command -v base64 >/dev/null 2>&1; then
|
||||
if REMOTE_LOCATOR_COMMAND="$(build_bounded_powershell_command Check "${REMOTE_LOCATOR_JOB_TIMEOUT}")"; then
|
||||
REMOTE_TRANSPORT_READY=1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "${MODE}" != "check" && "${MODE}" != "collect" ]]; then
|
||||
printf '%s\n' "error=invalid_mode:${MODE}" >&2
|
||||
exit 64
|
||||
@@ -287,6 +356,12 @@ elif [[ "${SSH_BATCHMODE_AUTH_READY}" != "1" ]]; then
|
||||
if [[ "${MODE}" == "collect" ]]; then
|
||||
PROCESS_EXIT_STATUS=75
|
||||
fi
|
||||
elif [[ "${REMOTE_TRANSPORT_READY}" != "1" ]]; then
|
||||
LOCATOR_COLLECTION_STATUS="blocked_local_transport_encoder_missing"
|
||||
SAFE_NEXT_STEP="install_or_restore_local_iconv_and_base64_then_rerun_no_secret_no_remote_write"
|
||||
if [[ "${MODE}" == "collect" ]]; then
|
||||
PROCESS_EXIT_STATUS=75
|
||||
fi
|
||||
elif [[ "${MODE}" == "check" ]]; then
|
||||
LOCATOR_COLLECTION_STATUS="ready_ssh_batchmode_auth_probe_only"
|
||||
SAFE_NEXT_STEP="rerun_locator_with_collect_then_commit_no_secret_locator_artifact_and_scorecard_rerun"
|
||||
@@ -298,7 +373,7 @@ elif [[ "${LOCAL_LOCATOR_SCRIPT_PRESENT}" != "1" ]]; then
|
||||
else
|
||||
DRY_RUN="false"
|
||||
REMOTE_LOCATOR_ATTEMPTED=1
|
||||
if REMOTE_LOCATOR_OUTPUT="$(run_ssh_timed "${SSH_AUTHENTICATED_USER}" "${REMOTE_LOCATOR_TIMEOUT}" "${REMOTE_LOCATOR_COMMAND}" <"${LOCAL_LOCATOR_SCRIPT}" 2>&1)"; then
|
||||
if REMOTE_LOCATOR_OUTPUT="$(encode_probe_source_line "${LOCAL_LOCATOR_SCRIPT}" | run_ssh_timed "${SSH_AUTHENTICATED_USER}" "${REMOTE_LOCATOR_TIMEOUT}" "${REMOTE_LOCATOR_COMMAND}" 2>&1)"; then
|
||||
REMOTE_LOCATOR_OUTPUT="${REMOTE_LOCATOR_OUTPUT//$'\r'/}"
|
||||
REMOTE_LOCATOR_EXIT_STATUS=0
|
||||
if grep -q '^AWOOOI_WINDOWS99_VMX_SOURCE_LOCATOR=1$' <<<"${REMOTE_LOCATOR_OUTPUT}" && grep -q '^MODE=Check$' <<<"${REMOTE_LOCATOR_OUTPUT}"; then
|
||||
@@ -326,6 +401,7 @@ printf '%s\n' "target_vm_alias=111"
|
||||
printf '%s\n' "connect_timeout_seconds=${CONNECT_TIMEOUT}"
|
||||
printf '%s\n' "ssh_timeout_seconds=${SSH_TIMEOUT}"
|
||||
printf '%s\n' "remote_locator_timeout_seconds=${REMOTE_LOCATOR_TIMEOUT}"
|
||||
printf '%s\n' "remote_locator_job_timeout_seconds=${REMOTE_LOCATOR_JOB_TIMEOUT}"
|
||||
printf '%s\n' "port_timeout_wrapper=${PORT_TIMEOUT_WRAPPER}"
|
||||
printf '%s\n' "ssh_auth_probe_user_limit=${MAX_AUTH_USERS}"
|
||||
printf '%s\n' "ssh_timeout_wrapper=${SSH_TIMEOUT_WRAPPER}"
|
||||
@@ -339,7 +415,8 @@ printf '%s\n' "ssh_batchmode_auth_ready=${SSH_BATCHMODE_AUTH_READY}"
|
||||
printf '%s\n' "ssh_authenticated_user=${SSH_AUTHENTICATED_USER}"
|
||||
printf '%s\n' "ssh_auth_probe_exit_status=${SSH_AUTH_PROBE_EXIT_STATUS}"
|
||||
printf '%s\n' "ssh_auth_probe_stdout_present=${SSH_AUTH_PROBE_STDOUT_PRESENT}"
|
||||
printf '%s\n' "remote_locator_mode=in_memory_stdin_scriptblock"
|
||||
printf '%s\n' "remote_locator_mode=${REMOTE_LOCATOR_MODE}"
|
||||
printf '%s\n' "remote_transport_ready=${REMOTE_TRANSPORT_READY}"
|
||||
printf '%s\n' "local_locator_script_present=${LOCAL_LOCATOR_SCRIPT_PRESENT}"
|
||||
printf '%s\n' "remote_locator_attempted=${REMOTE_LOCATOR_ATTEMPTED}"
|
||||
printf '%s\n' "remote_locator_exit_status=${REMOTE_LOCATOR_EXIT_STATUS}"
|
||||
|
||||
@@ -54,12 +54,25 @@ def test_collector_contract_forbids_secret_and_runtime_actions() -> None:
|
||||
assert "StrictHostKeyChecking=accept-new" in text
|
||||
assert "StrictHostKeyChecking=no" not in text
|
||||
assert 'REMOTE_VERIFY_TIMEOUT="${WINDOWS99_REMOTE_VERIFY_TIMEOUT:-45}"' in text
|
||||
assert "REMOTE_VERIFY_TIMEOUT - 15" in text
|
||||
assert "unsafe_legacy_override_rejected" in text
|
||||
assert "SSH_USERS=(Administrator ogt wooo ooo administrator)" in text
|
||||
assert "GSSAPIAuthentication=no" in text
|
||||
assert "--via-host" in text
|
||||
assert "via_host=" in text
|
||||
assert "in_memory_stdin_scriptblock" in text
|
||||
assert "[Console]::In.ReadToEnd()" in text
|
||||
assert "encoded_command_bounded_job_base64_line" in text
|
||||
assert "[Console]::In.ReadLine()" in text
|
||||
assert "FromBase64String" in text
|
||||
assert "[Console]::In.ReadToEnd()" not in text
|
||||
assert "$ProgressPreference = 'SilentlyContinue'" in text
|
||||
assert "[Text.UTF8Encoding]::new($false)" in text
|
||||
assert "Start-Job" in text
|
||||
assert "Wait-Job" in text
|
||||
assert "Stop-Job" in text
|
||||
assert "Remove-Job" in text
|
||||
assert "-NonInteractive" in text
|
||||
assert "-EncodedCommand" in text
|
||||
assert 'Command \"& ([scriptblock]::Create([Console]::In.ReadToEnd()))' not in text
|
||||
assert ".\\\\windows99-vmware-autostart.ps1 -Mode Verify" not in text
|
||||
for forbidden in [
|
||||
"sshpass",
|
||||
@@ -161,6 +174,45 @@ def test_check_mode_probes_all_explicit_users_without_secret_prompt(tmp_path: Pa
|
||||
assert values["password_prompt_allowed"] == "false"
|
||||
|
||||
|
||||
def test_collect_mode_rejects_legacy_readtoend_override(
|
||||
tmp_path: Path, monkeypatch
|
||||
) -> None:
|
||||
fake_bin = tmp_path / "bin"
|
||||
fake_bin.mkdir()
|
||||
_write_executable(
|
||||
fake_bin / "nc",
|
||||
"""
|
||||
#!/usr/bin/env bash
|
||||
exit 0
|
||||
""",
|
||||
)
|
||||
_write_executable(
|
||||
fake_bin / "ssh",
|
||||
"""
|
||||
#!/usr/bin/env bash
|
||||
args="$*"
|
||||
if [[ "$args" == *"powershell"* ]]; then
|
||||
exit 44
|
||||
fi
|
||||
printf '%s\n' 'AWOOOI_WINDOWS99_SSH_READY'
|
||||
exit 0
|
||||
""",
|
||||
)
|
||||
monkeypatch.setenv(
|
||||
"WINDOWS99_REMOTE_VERIFY_COMMAND",
|
||||
"powershell -Command [Console]::In.ReadToEnd()",
|
||||
)
|
||||
|
||||
result = _run_collector(fake_bin, "--collect", "--max-auth-users", "1")
|
||||
|
||||
assert result.returncode == 75
|
||||
values = _key_values(result.stdout)
|
||||
assert values["remote_verify_mode"] == "unsafe_legacy_override_rejected"
|
||||
assert values["remote_transport_ready"] == "0"
|
||||
assert values["remote_verify_attempted"] == "0"
|
||||
assert values["verify_collection_status"] == "blocked_local_transport_encoder_missing"
|
||||
|
||||
|
||||
def test_check_mode_can_cap_auth_probe_user_count_without_secret_prompt(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
@@ -254,7 +306,9 @@ def test_check_mode_auth_ready_does_not_run_remote_verify(tmp_path: Path) -> Non
|
||||
assert result.returncode == 0
|
||||
values = _key_values(result.stdout)
|
||||
assert values["ssh_batchmode_auth_ready"] == "1"
|
||||
assert values["remote_verify_mode"] == "in_memory_stdin_scriptblock"
|
||||
assert values["remote_verify_mode"] == "encoded_command_bounded_job_base64_line"
|
||||
assert values["remote_transport_ready"] == "1"
|
||||
assert values["remote_verify_job_timeout_seconds"] == "30"
|
||||
assert values["local_verify_script_present"] == "1"
|
||||
assert values["remote_verify_attempted"] == "0"
|
||||
assert values["verify_collection_status"] == "ready_ssh_batchmode_auth_probe_only"
|
||||
@@ -296,6 +350,7 @@ def test_collect_mode_runs_readonly_remote_verify_when_auth_ready(tmp_path: Path
|
||||
assert values["dry_run"] == "false"
|
||||
assert values["ssh_batchmode_auth_ready"] == "1"
|
||||
assert values["remote_verify_attempted"] == "1"
|
||||
assert values["remote_transport_ready"] == "1"
|
||||
assert values["remote_verify_exit_status"] == "0"
|
||||
assert values["verify_collection_status"] == "collected_windows99_vmware_verify_stdout"
|
||||
assert "remote_verify_output_begin" in result.stdout
|
||||
|
||||
@@ -52,8 +52,21 @@ def test_locator_contract_forbids_secret_runtime_actions_and_vmx_content_reads()
|
||||
assert "NumberOfPasswordPrompts=0" in bash_text
|
||||
assert "--via-host" in bash_text
|
||||
assert "via_host=" in bash_text
|
||||
assert "in_memory_stdin_scriptblock" in bash_text
|
||||
assert "[Console]::In.ReadToEnd()" in bash_text
|
||||
assert "REMOTE_LOCATOR_TIMEOUT - 15" in bash_text
|
||||
assert "unsafe_legacy_override_rejected" in bash_text
|
||||
assert "encoded_command_bounded_job_base64_line" in bash_text
|
||||
assert "[Console]::In.ReadLine()" in bash_text
|
||||
assert "FromBase64String" in bash_text
|
||||
assert "[Console]::In.ReadToEnd()" not in bash_text
|
||||
assert "$ProgressPreference = 'SilentlyContinue'" in bash_text
|
||||
assert "[Text.UTF8Encoding]::new($false)" in bash_text
|
||||
assert "Start-Job" in bash_text
|
||||
assert "Wait-Job" in bash_text
|
||||
assert "Stop-Job" in bash_text
|
||||
assert "Remove-Job" in bash_text
|
||||
assert "-NonInteractive" in bash_text
|
||||
assert "-EncodedCommand" in bash_text
|
||||
assert 'Command \"& ([scriptblock]::Create([Console]::In.ReadToEnd()))' not in bash_text
|
||||
assert "Get-ChildItem" in ps_text
|
||||
assert "Test-Path" in ps_text
|
||||
for text in [bash_text, ps_text]:
|
||||
@@ -110,6 +123,9 @@ def test_check_mode_reports_auth_probe_without_remote_locator(tmp_path: Path) ->
|
||||
assert values["target_host_alias"] == "99"
|
||||
assert values["target_vm_alias"] == "111"
|
||||
assert values["ssh_batchmode_auth_ready"] == "1"
|
||||
assert values["remote_locator_mode"] == "encoded_command_bounded_job_base64_line"
|
||||
assert values["remote_transport_ready"] == "1"
|
||||
assert values["remote_locator_job_timeout_seconds"] == "45"
|
||||
assert values["remote_locator_attempted"] == "0"
|
||||
assert values["locator_collection_status"] == "ready_ssh_batchmode_auth_probe_only"
|
||||
assert values["secret_value_read"] == "false"
|
||||
|
||||
Reference in New Issue
Block a user