diff --git a/lib/engines/functest/tests/cases/driver_sign_check.json b/lib/engines/functest/tests/cases/driver_sign_check.json index e8aa46c3..14f1c880 100644 --- a/lib/engines/functest/tests/cases/driver_sign_check.json +++ b/lib/engines/functest/tests/cases/driver_sign_check.json @@ -3,7 +3,7 @@ "display_name": "Installed driver is digitally signed", "description": "Verify the installed driver .sys is digitally signed (@driver_module@)", "test_system_ref": "VIRT-200", - "timeout": 120, + "timeout": 720, "test_steps": [ { "desc": "Reboot so the freshly installed driver is fully loaded", @@ -13,13 +13,13 @@ "desc": "Locate @driver_module@.sys in the DriverStore", "guest_run_file": "lib/engines/functest/tests/scripts/find_driver_in_store.ps1", "expected_output_contains": "PASS:", - "timeout": 60 + "timeout": 300 }, { "desc": "Verify @driver_module@.sys is digitally signed", "guest_run_file": "lib/engines/functest/tests/scripts/verify_driver_signed.ps1", "expected_output_contains": "PASS:", - "timeout": 60 + "timeout": 180 } ] } diff --git a/lib/engines/functest/tests/cases/driver_sigverif.json b/lib/engines/functest/tests/cases/driver_sigverif.json new file mode 100644 index 00000000..3590bbf3 --- /dev/null +++ b/lib/engines/functest/tests/cases/driver_sigverif.json @@ -0,0 +1,41 @@ +{ + "name": "driver_sigverif", + "description": "Verify @driver_module@.sys is Signed via Windows File Signature Verification GUI (sigverif + AutoIt), mirrors KAR win_sigverif", + "test_system_ref": "VIRT-SIGVERIF", + "timeout": 1320, + "extra_software": ["autoit"], + "test_steps": [ + { + "desc": "Upload common interactive/AutoIt helpers, sigverif.au3, and worker", + "files_action": [ + { + "local_path": "lib/engines/functest/tests/scripts/common/Invoke-InteractiveSession.ps1", + "remote_path": "C:\\AutoHCK\\common\\Invoke-InteractiveSession.ps1", + "direction": "local-to-remote" + }, + { + "local_path": "lib/engines/functest/tests/scripts/common/Invoke-AutoIt.ps1", + "remote_path": "C:\\AutoHCK\\common\\Invoke-AutoIt.ps1", + "direction": "local-to-remote" + }, + { + "local_path": "lib/engines/functest/tests/scripts/sigverif.au3", + "remote_path": "C:\\AutoHCK\\sigverif.au3", + "direction": "local-to-remote" + }, + { + "local_path": "lib/engines/functest/tests/scripts/sigverif_worker.ps1", + "remote_path": "C:\\AutoHCK\\sigverif_worker.ps1", + "direction": "local-to-remote" + } + ], + "timeout": 120 + }, + { + "desc": "Run sigverif GUI via AutoIt and confirm @driver_module@.sys is Signed", + "guest_run_file": "lib/engines/functest/tests/scripts/run_sigverif_gui.ps1", + "expected_output_contains": "PASS:", + "timeout": 1200 + } + ] +} diff --git a/lib/engines/functest/tests/scripts/common/Invoke-AutoIt.ps1 b/lib/engines/functest/tests/scripts/common/Invoke-AutoIt.ps1 new file mode 100644 index 00000000..67e11b10 --- /dev/null +++ b/lib/engines/functest/tests/scripts/common/Invoke-AutoIt.ps1 @@ -0,0 +1,138 @@ +<# +.SYNOPSIS + Locate AutoIt and run a .au3 script on the current desktop session. + +.DESCRIPTION + Intended to run inside an interactive session (e.g. after + Invoke-InteractiveSession.ps1). Requires suite or case extra_software: ["autoit"]. + + Usage: upload this script (and Invoke-InteractiveSession.ps1 if needed) from + lib/engines/functest/tests/scripts/common/ via the case files_action step to + e.g. C:\AutoHCK\common\, then invoke it from the interactive worker. + +.PARAMETER Au3Path + Absolute guest path to the .au3 script. + +.PARAMETER TimeoutSec + Max seconds to wait for AutoIt to exit (and optional -WaitForFile). + +.PARAMETER WaitForFile + If set, success requires this file to exist. The helper waits until + AutoIt has exited and the file is present, or TimeoutSec elapses. + +.PARAMETER ScriptArgs + Extra arguments forwarded to the AutoIt script ($CmdLine[1] ...). + +.PARAMETER MinimizeWindows + Call Shell.Application MinimizeAll before starting AutoIt. + +.PARAMETER LogPath + Optional log file to append status lines to. +#> + +param( + [Parameter(Mandatory = $true)] + [string]$Au3Path, + + [int]$TimeoutSec = 180, + + [string]$WaitForFile = '', + + [string[]]$ScriptArgs = @(), + + [switch]$MinimizeWindows, + + [string]$LogPath = '' +) + +$ErrorActionPreference = 'Stop' + +function Write-AutoItLog([string]$Text) { + if ($LogPath) { + Add-Content -Path $LogPath -Value $Text -Encoding ASCII + } +} + +function Get-AutoItPath { + # Native x86 OS: AutoIt3_x64.exe does not exist and ProgramFiles(x86) + # is empty, so skip both to avoid a hang/timeout. + $isNativeX86 = ($env:PROCESSOR_ARCHITECTURE -eq 'x86') -and + [string]::IsNullOrEmpty($env:PROCESSOR_ARCHITEW6432) + + if ($isNativeX86) { + $candidates = @( + "${env:ProgramFiles}\AutoIt3\AutoIt3.exe" + ) + } else { + $candidates = @( + "${env:ProgramFiles}\AutoIt3\AutoIt3_x64.exe", + "${env:ProgramFiles}\AutoIt3\AutoIt3.exe", + "${env:ProgramFiles(x86)}\AutoIt3\AutoIt3_x64.exe", + "${env:ProgramFiles(x86)}\AutoIt3\AutoIt3.exe" + ) + } + foreach ($p in $candidates) { + if ($p -and (Test-Path $p)) { return $p } + } + return $null +} + +$autoIt = Get-AutoItPath +if (-not $autoIt) { + Write-Output 'FAIL: AutoIt not found (suite or case extra_software autoit required)' + exit 1 +} +if (-not (Test-Path $Au3Path)) { + Write-Output "FAIL: AutoIt script not found at $Au3Path" + exit 1 +} + +if ($MinimizeWindows) { + try { + $shell = New-Object -ComObject Shell.Application + $shell.MinimizeAll() + Start-Sleep -Seconds 2 + } catch { + Write-AutoItLog ("WARN: MinimizeAll failed: $($_.Exception.Message)") + } +} + +$argList = @('"' + $Au3Path + '"') +if ($ScriptArgs) { $argList += $ScriptArgs } +Write-AutoItLog ("Running: $autoIt $($argList -join ' ')") +$p = Start-Process -FilePath $autoIt -ArgumentList $argList -PassThru + +$killedOnTimeout = $false +$deadline = (Get-Date).AddSeconds($TimeoutSec) +while ((Get-Date) -lt $deadline) { + $null = $p.Refresh() + if ($p.HasExited) { + if (-not $WaitForFile -or (Test-Path $WaitForFile)) { break } + # AutoIt already exited; keep polling for the output file until deadline. + } + Start-Sleep -Seconds 2 +} + +if (-not $p.HasExited) { + Stop-Process -Id $p.Id -Force -ErrorAction SilentlyContinue + Write-AutoItLog 'WARN: AutoIt still running after timeout; killed' + $killedOnTimeout = $true +} else { + Write-AutoItLog ("AutoIt exit code=$($p.ExitCode)") +} + +if ($WaitForFile -and -not (Test-Path $WaitForFile)) { + Write-Output "FAIL: expected output file not created: $WaitForFile" + exit 1 +} +if ($killedOnTimeout) { + Write-Output "FAIL: AutoIt killed after timeout (${TimeoutSec}s)" + exit 1 +} +if ($p.ExitCode -ne 0) { + Write-Output "FAIL: AutoIt exited $($p.ExitCode) ($Au3Path)" + exit 1 +} + +Write-Output "PASS: AutoIt finished ($Au3Path)" +exit 0 diff --git a/lib/engines/functest/tests/scripts/common/Invoke-InteractiveSession.ps1 b/lib/engines/functest/tests/scripts/common/Invoke-InteractiveSession.ps1 new file mode 100644 index 00000000..c3c7c294 --- /dev/null +++ b/lib/engines/functest/tests/scripts/common/Invoke-InteractiveSession.ps1 @@ -0,0 +1,121 @@ +<# +.SYNOPSIS + Hop from Session 0 to the interactive desktop and run a worker script. + +.DESCRIPTION + Functest guest_run / guest_run_file often execute in Session 0, where GUI + automation (AutoIt, etc.) cannot attach to the user desktop. This helper + registers a Scheduled Task with LogonType Interactive, starts it as the + AutoHCK autologin user, and waits for a result file. + + Any GUI / AutoIt functest can reuse this; keep test-specific logic in the + worker script pointed at by -ScriptPath. + + Usage: upload helpers from lib/engines/functest/tests/scripts/common/ via + the case files_action step to e.g. C:\AutoHCK\common\, then call this + script from the Session-0 launcher (see driver_sigverif). + +.PARAMETER ScriptPath + Absolute guest path to the worker .ps1 (uploaded via files_action). + +.PARAMETER ArgumentList + Extra arguments appended after powershell -File . + +.PARAMETER TaskName + Scheduled Task name. Must be unique per concurrent test/client (use an + AutoHCK_ prefix plus a test-specific suffix, e.g. AutoHCK_Sigverif) so + overlapping runs do not unregister each other's tasks. + +.PARAMETER ResultFile + Absolute path the worker writes; first line should be PASS:... or FAIL:... + +.PARAMETER TimeoutSec + Seconds to wait for ResultFile before failing. + +.PARAMETER WorkDir + Working directory created before the task starts. + +.PARAMETER WorkerLog + Optional log path to dump on success/timeout (if present). +#> + +param( + [Parameter(Mandatory = $true)] + [string]$ScriptPath, + + [string]$ArgumentList = '', + + [string]$TaskName = 'AutoHCK_Interactive', + + [Parameter(Mandatory = $true)] + [string]$ResultFile, + + [int]$TimeoutSec = 240, + + [string]$WorkDir = 'C:\AutoHCK\interactive_work', + + [string]$WorkerLog = '' +) + +$ErrorActionPreference = 'Stop' + +function Get-InteractiveUser { + # AutoHCK autologin: Winlogon DefaultUserName names the interactive user. + $winlogon = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon' + $autoUser = (Get-ItemProperty -Path $winlogon -Name 'DefaultUserName' -ErrorAction SilentlyContinue).DefaultUserName + if ($autoUser) { return $autoUser } + return 'Administrator' +} + +if (-not (Test-Path $ScriptPath)) { + Write-Output "FAIL: worker script not found at $ScriptPath (files_action upload required)" + exit 1 +} + +New-Item -ItemType Directory -Force -Path $WorkDir | Out-Null +Remove-Item -Force $ResultFile -ErrorAction SilentlyContinue +if ($WorkerLog) { + Remove-Item -Force $WorkerLog -ErrorAction SilentlyContinue +} + +$user = Get-InteractiveUser +Write-Output "Outer session=$((Get-Process -Id $PID).SessionId); launching interactive user='$user'" +Write-Output "Worker: $ScriptPath" + +Unregister-ScheduledTask -TaskName $TaskName -Confirm:$false -ErrorAction SilentlyContinue + +$arg = "-NoProfile -ExecutionPolicy Bypass -File `"$ScriptPath`"" +if ($ArgumentList) { + $arg = "$arg $ArgumentList" +} + +$action = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument $arg +$principal = New-ScheduledTaskPrincipal -UserId $user -LogonType Interactive -RunLevel Highest +$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -MultipleInstances IgnoreNew +Register-ScheduledTask -TaskName $TaskName -Action $action -Principal $principal -Settings $settings -Force | Out-Null +Start-ScheduledTask -TaskName $TaskName +Write-Output "Started scheduled task '$TaskName'; waiting for result..." + +$deadline = (Get-Date).AddSeconds($TimeoutSec) +while ((Get-Date) -lt $deadline) { + if (Test-Path $ResultFile) { + $text = (Get-Content -Path $ResultFile -Raw).Trim() + Write-Output $text + if ($WorkerLog -and (Test-Path $WorkerLog)) { + Write-Output '--- worker.log ---' + Get-Content -Path $WorkerLog | ForEach-Object { Write-Output $_ } + } + Unregister-ScheduledTask -TaskName $TaskName -Confirm:$false -ErrorAction SilentlyContinue + if ($text -like 'PASS:*') { exit 0 } + exit 1 + } + Start-Sleep -Seconds 2 +} + +Write-Output "FAIL: timed out waiting for interactive worker result ($TaskName)" +if ($WorkerLog -and (Test-Path $WorkerLog)) { + Write-Output '--- worker.log ---' + Get-Content -Path $WorkerLog | ForEach-Object { Write-Output $_ } +} +Unregister-ScheduledTask -TaskName $TaskName -Confirm:$false -ErrorAction SilentlyContinue +exit 1 diff --git a/lib/engines/functest/tests/scripts/common/README.md b/lib/engines/functest/tests/scripts/common/README.md new file mode 100644 index 00000000..845f09d5 --- /dev/null +++ b/lib/engines/functest/tests/scripts/common/README.md @@ -0,0 +1,18 @@ +# Common functest helpers (guest scripts) + +Upload the scripts you need via the test case `files_action` step, for example: + +``` +local_path: lib/engines/functest/tests/scripts/common/Invoke-InteractiveSession.ps1 +remote_path: C:\AutoHCK\common\Invoke-InteractiveSession.ps1 +``` + +Typical GUI flow: + +``` +Session 0 launcher -> Invoke-InteractiveSession.ps1 (-TaskName unique per test) + -> worker.ps1 on interactive desktop + -> Invoke-AutoIt.ps1 (-Au3Path ..., optional -WaitForFile) +``` + +Requires suite or case `extra_software: ["autoit"]` when using Invoke-AutoIt.ps1. diff --git a/lib/engines/functest/tests/scripts/run_sigverif_gui.ps1 b/lib/engines/functest/tests/scripts/run_sigverif_gui.ps1 new file mode 100644 index 00000000..69d3b501 --- /dev/null +++ b/lib/engines/functest/tests/scripts/run_sigverif_gui.ps1 @@ -0,0 +1,45 @@ +<# +.SYNOPSIS + Run Windows File Signature Verification (sigverif) via AutoIt and confirm + @driver_module@.sys is Signed — mirrors KAR/avocado win_sigverif. + +.DESCRIPTION + Requires suite or case extra_software: ["autoit"]. The test case uploads common + helpers, sigverif.au3, and sigverif_worker.ps1 via files_action. + + This launcher only hops to the interactive desktop via + Invoke-InteractiveSession.ps1; GUI work lives in the worker. +#> + +$ErrorActionPreference = 'Stop' + +$moduleName = '@driver_module@' +if (-not $moduleName -or $moduleName -like '@*@') { + Write-Output 'FAIL: driver_module was not substituted by functest' + exit 1 +} + +$workDir = 'C:\AutoHCK\sigverif_work' +$resultFile = Join-Path $workDir 'result.txt' +$workerLog = Join-Path $workDir 'worker.log' +$workerScript = 'C:\AutoHCK\sigverif_worker.ps1' +$interactiveHelper = 'C:\AutoHCK\common\Invoke-InteractiveSession.ps1' +$scanTimeoutSec = 900 + +Write-Output "driver_module=$moduleName" + +if (-not (Test-Path $interactiveHelper)) { + Write-Output "FAIL: interactive helper not found at $interactiveHelper (files_action upload required)" + exit 1 +} + +# Uploaded helpers need an explicit Bypass launch from Session 0 guest_run_file. +& powershell.exe -NoProfile -ExecutionPolicy Bypass -File $interactiveHelper ` + -ScriptPath $workerScript ` + -ArgumentList "-ModuleName `"$moduleName`"" ` + -TaskName 'AutoHCK_Sigverif' ` + -ResultFile $resultFile ` + -TimeoutSec ($scanTimeoutSec + 60) ` + -WorkDir $workDir ` + -WorkerLog $workerLog +exit $LASTEXITCODE diff --git a/lib/engines/functest/tests/scripts/sigverif.au3 b/lib/engines/functest/tests/scripts/sigverif.au3 new file mode 100644 index 00000000..43a744ef --- /dev/null +++ b/lib/engines/functest/tests/scripts/sigverif.au3 @@ -0,0 +1,26 @@ +#cs ---------------------------------------------------------------------------- + + AutoIt Version: 3.3.14.5 + Author: Li Jin + Script Function: + Driver File Signature Verification (sigverif.exe): Start scan, dismiss + result dialog, close. Used by functest driver_sigverif (KAR win_sigverif). + Optional $CmdLine[1] is the scan timeout in seconds (default 900). +#ce ---------------------------------------------------------------------------- + +Local $scanTimeout = 900 +If $CmdLine[0] >= 1 Then + $scanTimeout = Number($CmdLine[1]) + If $scanTimeout < 1 Then $scanTimeout = 900 +EndIf + +Run("sigverif.exe") +WinWaitActive("File Signature Verification", "&Start") +Send("!s") +if WinWaitActive("SigVerif", "Your files have been scanned and verified as digitally signed.", $scanTimeout) then + Send("{ENTER}") +else + Send("!c") +endif +WinWaitActive("File Signature Verification", "&Start") +Send("!c") diff --git a/lib/engines/functest/tests/scripts/sigverif_worker.ps1 b/lib/engines/functest/tests/scripts/sigverif_worker.ps1 new file mode 100644 index 00000000..a727e47d --- /dev/null +++ b/lib/engines/functest/tests/scripts/sigverif_worker.ps1 @@ -0,0 +1,92 @@ +<# +.SYNOPSIS + Interactive-session worker: run sigverif via AutoIt and check @driver_module@.sys. + +.DESCRIPTION + Runs on the interactive desktop (via Invoke-InteractiveSession). Uploaded + from the host via files_action; launched by run_sigverif_gui.ps1. +#> + +param( + [Parameter(Mandatory = $true)] + [string]$ModuleName +) + +$ErrorActionPreference = 'Stop' + +$driverSys = "$ModuleName.sys" +$sigverifLog = 'C:\Users\Public\Documents\SIGVERIF.TXT' +$au3 = 'C:\AutoHCK\sigverif.au3' +$autoItHelper = 'C:\AutoHCK\common\Invoke-AutoIt.ps1' +$workDir = 'C:\AutoHCK\sigverif_work' +$resultFile = Join-Path $workDir 'result.txt' +$workerLog = Join-Path $workDir 'worker.log' +$scanTimeoutSec = 900 + +function Write-Result([string]$Text, [int]$Code = 0) { + Set-Content -Path $resultFile -Value $Text -Encoding ASCII + Add-Content -Path $workerLog -Value $Text -Encoding ASCII + exit $Code +} + +New-Item -ItemType Directory -Force -Path $workDir | Out-Null +Set-Content -Path $workerLog -Value ("worker start module=$ModuleName session=$((Get-Process -Id $PID).SessionId)") -Encoding ASCII + +if (-not (Test-Path $autoItHelper)) { + Write-Result "FAIL: AutoIt helper not found at $autoItHelper" 1 +} + +if (Test-Path $sigverifLog) { + Remove-Item -Force $sigverifLog + Add-Content -Path $workerLog -Value ("Removed previous log: $sigverifLog") +} + +& powershell.exe -NoProfile -ExecutionPolicy Bypass -File $autoItHelper ` + -Au3Path $au3 ` + -TimeoutSec $scanTimeoutSec ` + -ScriptArgs $scanTimeoutSec ` + -WaitForFile $sigverifLog ` + -MinimizeWindows ` + -LogPath $workerLog +if ($LASTEXITCODE -ne 0) { + Write-Result "FAIL: AutoIt/sigverif run failed (exit=$LASTEXITCODE)" 1 +} + +if (-not (Test-Path $sigverifLog)) { + Write-Result ("FAIL: sigverif log not created: $sigverifLog") 1 +} + +$logText = Get-Content -Path $sigverifLog -Raw -ErrorAction Stop +Add-Content -Path $workerLog -Value ("--- SIGVERIF.TXT ($ModuleName lines) ---") +foreach ($line in ($logText -split [Environment]::NewLine)) { + if ($line -match [regex]::Escape($ModuleName)) { + Add-Content -Path $workerLog -Value $line + } +} + +function Test-SigverifSignedLine([string]$Line, [string]$DriverSys) { + $esc = [regex]::Escape($DriverSys) + # Complete filename token, not a substring of another filename. + if ($Line -notmatch "(?i)(?