summaryrefslogtreecommitdiff
path: root/scripts/Run-Final-ElevatedVerification.ps1
blob: 286d459a5d7e0424065fd9d439567cb32e57c3e3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
param(
    [string]$EvidenceRoot = 'artifacts\evidence\final-elevated',
    [string]$TestRoot = 'C:\tmp',
    [string]$VenvPython = 'C:\tmp\.venv\Scripts\python.exe',
    [switch]$WindowsSecurityChild
)

$ErrorActionPreference = 'Continue'
$OutputEncoding = [Text.UTF8Encoding]::new($false)
[Console]::OutputEncoding = $OutputEncoding
$repository = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path
$evidence = [IO.Path]::GetFullPath((Join-Path $repository $EvidenceRoot))
if (-not $evidence.StartsWith($repository + [IO.Path]::DirectorySeparatorChar, [StringComparison]::OrdinalIgnoreCase)) {
    throw 'Evidence output must remain inside the repository.'
}
New-Item -ItemType Directory -Force -Path $evidence | Out-Null
$failed = $false
$metadata = Join-Path $evidence 'metadata.txt'
$administrator = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(
    [Security.Principal.WindowsBuiltInRole]::Administrator)
@(
    "timestamp=$(Get-Date -Format o)"
    "commit=$(git -C $repository rev-parse HEAD)"
    "administrator=$administrator"
    "working_tree_clean=$(-not [bool](git -C $repository status --porcelain))"
    "os=$([Environment]::OSVersion.VersionString)"
    "dotnet=$(& dotnet --version)"
) | Set-Content -LiteralPath $metadata -Encoding utf8NoBOM

function Invoke-VerificationStep {
    param([string]$Name, [scriptblock]$Action)
    $path = Join-Path $evidence "$Name.log"
    "START $Name $(Get-Date -Format o)" | Tee-Object -FilePath $path
    $exitCode = 0
    try {
        $global:LASTEXITCODE = 0
        & $Action *>&1 | Tee-Object -FilePath $path -Append
        if ($global:LASTEXITCODE) { $exitCode = $global:LASTEXITCODE }
    }
    catch {
        $_ | Out-String | Tee-Object -FilePath $path -Append
        $exitCode = 1
    }
    "END $Name exit=$exitCode $(Get-Date -Format o)" | Tee-Object -FilePath $path -Append
    if ($exitCode -ne 0) { $script:failed = $true }
}

$bundle = Join-Path $repository 'build\Release\publish\winagent-sandbox-mcp'
$windowsTests = Join-Path $repository 'build\Release\Agent.Windows.Tests\net10.0-windows10.0.26100.0\Agent.Windows.Tests.exe'
$coreTests = Join-Path $repository 'build\Release\Agent.Core.Tests\net10.0-windows10.0.26100.0\Agent.Core.Tests.exe'
$plannerTests = Join-Path $repository 'build\Release\Agent.Planner.Tests\net10.0\Agent.Planner.Tests.exe'

if ($WindowsSecurityChild) {
    if (-not $administrator) { Write-Error 'Windows Security child did not receive an administrator token.'; exit 2 }
    $env:AGENT_MCP_MONKEY_ROOT = (Resolve-Path -LiteralPath $TestRoot).Path
    $env:AGENT_MCP_TEST_VENV_PYTHON = (Resolve-Path -LiteralPath $VenvPython).Path
    $env:AGENT_MCP_TEST_POWERSHELL = (Get-Command pwsh.exe -ErrorAction Stop).Source
    $env:AGENT_MCP_TEST_POWERSHELL_RUNTIME = Split-Path $env:AGENT_MCP_TEST_POWERSHELL
    $env:AGENT_MCP_TEST_CMD = $env:ComSpec
    $childLog = Join-Path $evidence 'windows-security-tests-elevated.log'
    # The suite intentionally shares one fixed Application Profile and global loopback state.
    # Serialize it so crash/recovery cases cannot race another WFP/loopback case.
    & $windowsTests -parallel none -failSkips *>&1 | Tee-Object -FilePath $childLog
    exit $LASTEXITCODE
}
if ($administrator) {
    throw 'Run the final verification orchestrator with a standard-user token. It elevates only the Windows Security test child.'
}

Invoke-VerificationStep 'bundle-show' {
    & (Join-Path $bundle 'agent-mcp.exe') show (Join-Path $bundle 'agent.yaml')
}
Invoke-VerificationStep 'windows-security-tests' {
    $pwsh = (Get-Command pwsh.exe -ErrorAction Stop).Source
    $child = Start-Process -FilePath $pwsh -Verb RunAs -WindowStyle Hidden -PassThru -Wait -ArgumentList @(
        '-NoProfile', '-File', $PSCommandPath, '-EvidenceRoot', $EvidenceRoot,
        '-TestRoot', $TestRoot, '-VenvPython', $VenvPython, '-WindowsSecurityChild')
    if ($child.ExitCode -ne 0) { throw "Elevated Windows Security test child failed with exit code $($child.ExitCode)." }
}
Invoke-VerificationStep 'core-unit-integration-tests' { & $coreTests }
Invoke-VerificationStep 'planner-unit-tests' { & $plannerTests }
Invoke-VerificationStep 'e2e-01-stdio' {
    & (Join-Path $PSScriptRoot 'Operational-E2E.ps1') -Transport stdio
}
Invoke-VerificationStep 'e2e-02-http-ipv4' {
    & (Join-Path $PSScriptRoot 'Operational-E2E.ps1') -Transport http -HttpHost 127.0.0.1
}
Invoke-VerificationStep 'e2e-02-http-ipv6' {
    & (Join-Path $PSScriptRoot 'Operational-E2E.ps1') -Transport http -HttpHost ::1
}
Invoke-VerificationStep 'residual-state' {
    $journalCount = @(Get-ChildItem -LiteralPath (Join-Path $bundle 'state\journals') -File -ErrorAction SilentlyContinue).Count
    $backupCount = @(Get-ChildItem -LiteralPath (Join-Path $bundle 'state\backups') -File -Recurse -ErrorAction SilentlyContinue).Count
    $bundleAgent = Join-Path $bundle 'agent-mcp.exe'
    $processCount = @(Get-Process -Name agent-mcp -ErrorAction SilentlyContinue | Where-Object { try { $_.Path -eq $bundleAgent } catch { $false } }).Count
    "journal_count=$journalCount backup_count=$backupCount published_process_count=$processCount"
    if ($journalCount -ne 0 -or $backupCount -ne 0 -or $processCount -ne 0) { throw 'Task Resource residue was detected.' }
}

if ($failed) {
    Write-Error "Final elevated verification FAILED. Evidence: $evidence"
    exit 1
}
Write-Output "Final elevated verification PASS. Evidence: $evidence"
exit 0