Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ Configure `WIRESOCKUI_WGBOOSTER_PATH_X86`, `WIRESOCKUI_WGBOOSTER_PATH_X64`, and

Install a dedicated organization GitHub App with only Self-hosted runners (read), expose its `WIRESOCK_SDK_RUNNER_POLICY_CLIENT_ID` variable and `WIRESOCK_SDK_RUNNER_POLICY_PRIVATE_KEY` secret through the protected `wiresock-sdk` environment, and scope its installation to this repository. The hosted preflight uses its short-lived token only to audit the runner group; candidate code never receives that token.

The manually dispatched **Hosted WireSock SDK experiment** is an isolated x64 feasibility check and does not replace the protected self-hosted runner policy. It runs only from the current protected `main` tip on a disposable GitHub-hosted Windows VM, bootstraps WinGet when necessary, downloads the exact `NTKERNEL.WireSockVPNClientCLI` SDK version, verifies the installer against the audited WinGet SHA-256 and Authenticode signature before execution, builds and installation-tests an unsigned candidate MSI, and exercises the real SDK lifecycle with synthetic profiles restricted to IANA documentation networks. The experiment uses no VPN credentials or repository secrets and uninstalls the SDK before the VM is discarded. Run it from **Actions → Hosted WireSock SDK experiment → Run workflow**; promote this design to x86/ARM64 or release gating only after the x64 driver, routing, and cleanup behavior succeeds consistently.
The manually dispatched **Hosted WireSock SDK experiment** is an isolated x64 feasibility check and does not replace the protected self-hosted runner policy. It runs only from the current protected `main` tip on a disposable GitHub-hosted Windows VM, bootstraps WinGet when necessary, downloads the exact `NTKERNEL.WireSockVPNClientCLI` SDK version, verifies the installer against the audited WinGet SHA-256 and Authenticode signature, waits for installation completion and the expected signed SDK artifacts, builds and installation-tests an unsigned candidate MSI, and exercises the real SDK lifecycle with synthetic profiles restricted to IANA documentation networks. The experiment uses no VPN credentials or repository secrets and uninstalls the SDK before the VM is discarded. Run it from **Actions → Hosted WireSock SDK experiment → Run workflow**; promote this design to x86/ARM64 or release gating only after the x64 driver, routing, and cleanup behavior succeeds consistently.

Native state and statistics polling use bounded asynchronous queries. If `wgbooster.dll` does not return before the query timeout, WireSockUI stops issuing additional native operations, records a recovery marker, and requires recovery or restart. Startup also compares the process and `wgbooster.dll` PE architectures so x86/x64/ARM64 mismatches are reported directly.

Expand Down
87 changes: 75 additions & 12 deletions scripts/Invoke-HostedSdkExperiment.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ $buildMsiScriptPath = Join-Path $PSScriptRoot 'Build-Msi.ps1'
$testMsiInstallationScriptPath = Join-Path `
$PSScriptRoot `
'Test-MsiInstallation.ps1'
$wireSockSdkRegistryPaths = @(
'SOFTWARE\WireSock Foundation\WireSock Secure Connect',
'SOFTWARE\WireSock Foundation\WireSock Secure Connect Pro',
'SOFTWARE\NTKernelResources\WinpkFilterForVPNClient'
)

function Assert-LastExitCode {
param(
Expand All @@ -48,6 +53,27 @@ function Assert-LastExitCode {
}
}

function Assert-SdkInstallerExitCode {
param(
[Parameter(Mandatory = $true)]
[int] $ExitCode,

[Parameter(Mandatory = $true)]
[string] $Operation
)

if ($ExitCode -eq 0) {
return
}
if ($ExitCode -eq 3010) {
Write-Warning (
"$Operation succeeded but requested a restart; continuing on " +
'the disposable hosted runner.')
return
}
throw "$Operation failed with exit code $ExitCode."
}

function Remove-HostedExperimentDirectory {
param(
[Parameter(Mandatory = $true)]
Expand Down Expand Up @@ -115,11 +141,6 @@ function Get-WinGetExecutable {
}

function Get-WireSockSdkLibraries {
$registryPaths = @(
'SOFTWARE\WireSock Foundation\WireSock Secure Connect',
'SOFTWARE\WireSock Foundation\WireSock Secure Connect Pro',
'SOFTWARE\NTKernelResources\WinpkFilterForVPNClient'
)
$registryViews = @(
[Microsoft.Win32.RegistryView]::Registry64,
[Microsoft.Win32.RegistryView]::Registry32
Expand All @@ -130,7 +151,7 @@ function Get-WireSockSdkLibraries {
[Microsoft.Win32.RegistryHive]::LocalMachine,
$view)
try {
foreach ($registryPath in $registryPaths) {
foreach ($registryPath in $wireSockSdkRegistryPaths) {
$key = $baseKey.OpenSubKey($registryPath)
try {
$location = if ($null -eq $key) {
Expand Down Expand Up @@ -172,6 +193,45 @@ function Get-WireSockSdkLibraries {
return @($libraries)
}

function Wait-WireSockSdkLibraries {
param(
[ValidateRange(1, 600)]
[int] $TimeoutSeconds = 120,

[ValidateRange(100, 10000)]
[int] $PollIntervalMilliseconds = 2000
)

$timeoutMilliseconds = $TimeoutSeconds * 1000
$stopwatch = [Diagnostics.Stopwatch]::StartNew()
try {
while ($stopwatch.ElapsedMilliseconds -lt $timeoutMilliseconds) {
$libraries = @(Get-WireSockSdkLibraries)
if ($libraries.Count -gt 0) {
return $libraries
}
$remainingMilliseconds =
$timeoutMilliseconds - $stopwatch.ElapsedMilliseconds
if ($remainingMilliseconds -gt 0) {
Start-Sleep -Milliseconds ([Math]::Min(
$PollIntervalMilliseconds,
[int]$remainingMilliseconds))
}
}
}
finally {
$stopwatch.Stop()
}

throw (
"The SDK installer registered no wgbooster.dll candidate within " +
"$TimeoutSeconds seconds while polling every " +
"$PollIntervalMilliseconds milliseconds. Expected InstallLocation " +
'under the 32-bit or 64-bit HKLM registry paths ' +
"'$($wireSockSdkRegistryPaths -join "', '")', with wgbooster.dll " +
'in that location, sdk, or bin.')
}

function Set-ProtectedProfileAcl {
param(
[Parameter(Mandatory = $true)]
Expand Down Expand Up @@ -377,14 +437,17 @@ try {
throw "SDK installer has Authenticode status '$($installerSignature.Status)'."
}

& $installers[0].FullName /S /NCRC
Assert-LastExitCode -Operation "Installing $packageId $packageVersion"
$installerProcess = Start-Process `
-FilePath $installers[0].FullName `
-ArgumentList @('/S', '/NCRC') `
-Wait `
-PassThru
Assert-SdkInstallerExitCode `
-ExitCode $installerProcess.ExitCode `
-Operation "Installing $packageId $packageVersion"
$installedSdk = $true

$libraries = @(Get-WireSockSdkLibraries)
if ($libraries.Count -eq 0) {
throw 'The SDK installer registered no wgbooster.dll candidate.'
}
$libraries = @(Wait-WireSockSdkLibraries)
$libraryPath = $null
foreach ($candidate in $libraries) {
$signature = Get-AuthenticodeSignature -FilePath $candidate
Expand Down