- fix(installer): 修复低完整性产物无法写入临时目录的问题,并在 CI 打包前后检查完整性标签 - fix(firmware): 从本次 Cargo ELF 打包并同步配套镜像,修正短目录说明与 LLVM 搜索顺序 - fix(installer): 修复中文完成页和页眉遮挡,侧栏使用等比缩放的原始 Logo - style(client): 应用图标增加圆角,统一“裁判系统客户端”命名 - fix(config): 安装提示改用可选本地热点配置,排除 Wi-Fi 凭据 - docs: 润色项目说明并统一裁判系统术语 - test(packaging): 补充 Windows 完整性和固件打包回归,覆盖外部输出目录、旧镜像与重复生成 验证:Windows NSIS 构建及原生页面预览;通用与本地安装配置编译;固件 release 构建、重复打包和客户端离线校验;6 项固件打包回归与 Windows 权限回归;git diff --check。未执行实机烧录。
159 lines
8.0 KiB
PowerShell
159 lines
8.0 KiB
PowerShell
# Integration regression: actual Windows labels, child process tokens, and an
|
|
# NSIS bootstrap that writes to TEMP and exits without installing anything.
|
|
[CmdletBinding()]
|
|
param(
|
|
[string]$NsisCompiler = (Join-Path $env:LOCALAPPDATA 'tauri/NSIS/makensis.exe')
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
Set-StrictMode -Version Latest
|
|
$prepare = Join-Path $PSScriptRoot 'prepare-windows-bundle.ps1'
|
|
& $prepare -CheckProcessOnly
|
|
if (-not (Test-Path -LiteralPath $NsisCompiler)) { throw "NSIS compiler not found: $NsisCompiler" }
|
|
|
|
function Assert([bool]$Condition, [string]$Message) {
|
|
if (-not $Condition) { throw $Message }
|
|
}
|
|
|
|
$testId = [Guid]::NewGuid().ToString('N')
|
|
$tempRoot = [IO.Path]::GetFullPath([IO.Path]::GetTempPath())
|
|
$fixture = Join-Path $tempRoot "rmic bundle test $testId"
|
|
$probeFile = Join-Path $tempRoot "rmic-nsis-temp-$testId.txt"
|
|
$installer = Join-Path $fixture 'bundle/nsis/probe-setup.exe'
|
|
$main = Join-Path $fixture 'cam-desktop.exe'
|
|
try {
|
|
New-Item -ItemType Directory -Force -Path (Join-Path $fixture 'bundle/nsis'), (Join-Path $fixture 'nsis/x64') | Out-Null
|
|
Copy-Item -LiteralPath (Join-Path $env:WINDIR 'System32/whoami.exe') -Destination $main
|
|
$accessBefore = (Get-Acl -LiteralPath $main).Sddl
|
|
|
|
# A small real NSIS executable exercises its early TEMP validation and
|
|
# plugin extraction without touching app files, registry, or shortcuts.
|
|
$source = Join-Path $fixture 'probe.nsi'
|
|
@"
|
|
Unicode true
|
|
Name "RMIC temporary directory probe"
|
|
OutFile "$($installer.Replace('$', '$$'))"
|
|
RequestExecutionLevel user
|
|
SilentInstall silent
|
|
Section
|
|
InitPluginsDir
|
|
FileOpen `$0 "`$TEMP\rmic-nsis-temp-$testId.txt" w
|
|
IfErrors failed
|
|
FileWrite `$0 "NSIS TEMP writable"
|
|
FileClose `$0
|
|
SetErrorLevel 0
|
|
Quit
|
|
failed:
|
|
SetErrorLevel 3
|
|
SectionEnd
|
|
"@ | Set-Content -LiteralPath $source -Encoding UTF8
|
|
& $NsisCompiler /V2 $source
|
|
Assert ($LASTEXITCODE -eq 0) 'Cannot compile the NSIS TEMP probe.'
|
|
|
|
& icacls.exe $fixture /setintegritylevel '(OI)(CI)L' | Out-Null
|
|
Assert ($LASTEXITCODE -eq 0) 'Cannot create a Low integrity fixture.'
|
|
& icacls.exe $main /setintegritylevel L | Out-Null
|
|
Assert ($LASTEXITCODE -eq 0) 'Cannot create an explicit Low executable label.'
|
|
$before = (& $main /groups) -join "`n"
|
|
Assert ($LASTEXITCODE -eq 0 -and $before -match 'S-1-16-4096') 'Low executable did not start with a Low token.'
|
|
|
|
$rejected = $false
|
|
try { & $prepare -OutputDir $fixture -CheckOnly } catch {
|
|
if ($_.Exception.Message -notmatch 'Low / Untrusted') { throw }
|
|
$rejected = $true
|
|
}
|
|
Assert $rejected 'Read-only artifact verification accepted a Low label.'
|
|
Assert ([RmicBundleIntegrity]::ReadLabel($installer) -match ';LW\)') 'Read-only verification changed the installer.'
|
|
|
|
# A Low process must fail before it tries to modify any output labels.
|
|
$lowShell = Join-Path $fixture 'low-shell.exe'
|
|
Copy-Item -LiteralPath (Join-Path $env:WINDIR 'System32/WindowsPowerShell/v1.0/powershell.exe') -Destination $lowShell
|
|
# Windows PowerShell 5 treats redirected native stderr as ErrorRecord objects.
|
|
# This command is expected to fail, so collect its diagnostic before asserting.
|
|
try {
|
|
$ErrorActionPreference = 'Continue'
|
|
$lowOutput = (& $lowShell -NoProfile -ExecutionPolicy Bypass -File $prepare -CheckProcessOnly 2>&1) -join "`n"
|
|
} finally {
|
|
$ErrorActionPreference = 'Stop'
|
|
}
|
|
Assert ($LASTEXITCODE -ne 0 -and $lowOutput -match 'requires Medium integrity') 'Low packaging process was not rejected.'
|
|
|
|
& $prepare -OutputDir $fixture
|
|
& $prepare -OutputDir $fixture -CheckOnly
|
|
& $prepare -OutputDir $fixture
|
|
$after = (& $main /groups) -join "`n"
|
|
Assert ($LASTEXITCODE -eq 0 -and $after -match 'S-1-16-(\d+)' -and [int]$Matches[1] -ge 8192) 'Repaired executable still starts below Medium integrity.'
|
|
Assert ((Get-Acl -LiteralPath $main).Sddl -eq $accessBefore) 'Repair changed the executable DACL.'
|
|
|
|
# The repaired shell now starts at Medium even from an elevated test runner.
|
|
# Verify that repairing owned Low outputs does not require elevation.
|
|
$mediumFixture = Join-Path $fixture 'medium repair'
|
|
New-Item -ItemType Directory -Path $mediumFixture | Out-Null
|
|
& icacls.exe $mediumFixture /setintegritylevel '(OI)(CI)L' | Out-Null
|
|
Assert ($LASTEXITCODE -eq 0) 'Cannot prepare the Medium-process repair fixture.'
|
|
$mediumOutput = (& $lowShell -NoProfile -ExecutionPolicy Bypass -File $prepare -OutputDir $mediumFixture 2>&1) -join "`n"
|
|
Assert ($LASTEXITCODE -eq 0) "Repair failed at Medium integrity: $mediumOutput"
|
|
Assert ([RmicBundleIntegrity]::ReadLabel($mediumFixture) -match ';ME\)') 'Medium process did not repair the output label.'
|
|
|
|
$probe = Start-Process -FilePath $installer -ArgumentList '/S' -PassThru -WindowStyle Hidden
|
|
try {
|
|
Assert ($probe.WaitForExit(15000)) 'NSIS TEMP probe timed out.'
|
|
Assert ($probe.ExitCode -eq 0) "NSIS TEMP probe failed: $($probe.ExitCode)"
|
|
Assert ((Get-Content -LiteralPath $probeFile -Raw) -eq 'NSIS TEMP writable') 'NSIS could not write to the normal TEMP directory.'
|
|
} finally {
|
|
if (-not $probe.HasExited) { $probe.Kill(); $probe.WaitForExit() }
|
|
$probe.Dispose()
|
|
}
|
|
|
|
$emptyOutput = Join-Path $fixture 'empty'
|
|
& $prepare -OutputDir $emptyOutput
|
|
$rejected = $false
|
|
try { & $prepare -OutputDir $emptyOutput -CheckOnly } catch {
|
|
if ($_.Exception.Message -notmatch 'No NSIS installer') { throw }
|
|
$rejected = $true
|
|
}
|
|
Assert $rejected 'Artifact verification accepted missing installers.'
|
|
|
|
# Exercise automatic Cargo output discovery, debug target selection and the
|
|
# separate host build-script directory, without invoking a real Rust build.
|
|
$savedEnvironment = @{}
|
|
foreach ($name in @('CARGO_TARGET_DIR', 'TAURI_ENV_TARGET_TRIPLE', 'TAURI_ENV_DEBUG')) {
|
|
$savedEnvironment[$name] = @{
|
|
Exists = Test-Path -LiteralPath "Env:$name"
|
|
Value = [Environment]::GetEnvironmentVariable($name, 'Process')
|
|
}
|
|
}
|
|
try {
|
|
$env:CARGO_TARGET_DIR = Join-Path $fixture 'cargo cache'
|
|
$env:TAURI_ENV_TARGET_TRIPLE = 'x86_64-pc-windows-msvc'
|
|
$env:TAURI_ENV_DEBUG = 'true'
|
|
$hostBuildDir = Join-Path $env:CARGO_TARGET_DIR 'debug/build/probe'
|
|
New-Item -ItemType Directory -Force -Path $hostBuildDir | Out-Null
|
|
$hostScript = Join-Path $hostBuildDir 'build-script-build.exe'
|
|
Copy-Item -LiteralPath $main -Destination $hostScript
|
|
& icacls.exe $env:CARGO_TARGET_DIR /setintegritylevel '(OI)(CI)L' | Out-Null
|
|
Assert ($LASTEXITCODE -eq 0) 'Cannot create Low host build outputs.'
|
|
& $prepare
|
|
Assert ([RmicBundleIntegrity]::ReadLabel($hostScript) -match ';ME\)') 'Host build script was not repaired.'
|
|
$targetOutput = Join-Path $env:CARGO_TARGET_DIR 'x86_64-pc-windows-msvc/debug/bundle/nsis'
|
|
Assert (Test-Path -LiteralPath $targetOutput) 'Cargo target directory or debug profile was ignored.'
|
|
} finally {
|
|
foreach ($name in $savedEnvironment.Keys) {
|
|
if ($savedEnvironment[$name].Exists) {
|
|
Set-Item -LiteralPath "Env:$name" -Value $savedEnvironment[$name].Value
|
|
} else {
|
|
Remove-Item -LiteralPath "Env:$name" -ErrorAction SilentlyContinue
|
|
}
|
|
}
|
|
}
|
|
Write-Host 'PASS: Low token reproduction, process guard, read-only rejection, label repair, unchanged DACL, repeat repair, NSIS TEMP bootstrap, missing artifacts, custom Cargo output and host build scripts.'
|
|
} finally {
|
|
# Only delete this test's uniquely named fixture, after checking its absolute path.
|
|
$resolvedFixture = [IO.Path]::GetFullPath($fixture)
|
|
if ($resolvedFixture.StartsWith($tempRoot.TrimEnd('\') + '\', [StringComparison]::OrdinalIgnoreCase) -and
|
|
(Split-Path -Leaf $resolvedFixture) -eq "rmic bundle test $testId") {
|
|
if (Test-Path -LiteralPath $resolvedFixture) { Remove-Item -LiteralPath $resolvedFixture -Recurse -Force }
|
|
}
|
|
if (Test-Path -LiteralPath $probeFile) { Remove-Item -LiteralPath $probeFile -Force }
|
|
}
|