**Related issue:** N/A — part of the Windows Fleet-maintained apps catalog expansion (letter C batch; follows #48872, #48881, #48950). Adds seventeen new Windows Fleet-maintained apps: | App | winget package | Installer | Notes | |-----|----------------|-----------|-------| | Advanced Installer | `Caphyon.AdvancedInstaller` | MSI, machine, x64 | Versioned ARP name ("Advanced Installer 23.8") → fuzzy match. | | Certify The Web | `CertifyTheWeb.CertifySSLManager` | Inno, machine, x64 | ARP name is "Certify Certificate Manager version 7.1.0.0" (product renamed at v7) → fuzzy on "Certify Certificate Manager". | | Chatbox | `Bin-Huang.Chatbox` | NSIS (electron-builder), machine, x64 | `/allusers /S`; custom exists query excludes the separate Chatbox Community Edition; uninstall matches `Chatbox [0-9]*`. | | Citrix Workspace | `Citrix.Workspace` | Vendor bootstrapper EXE, machine, x86 universal | ARP name "Citrix Workspace \<YYMM\>" → fuzzy. Uninstall runs the registered TrolleyExpress with `/uninstall /cleanup /silent`; 3010 treated as success. See "not added" for the LTSR caveat. | | CPU-Z | `CPUID.CPU-Z` | Inno, machine | `installer_locale` pinned to en-US (manifest also carries zh-CN); `/ALLUSERS` added to Inno switches; versioned ARP name → fuzzy. | | CodeMeter Runtime Kit | `Wibu-Systems.CodeMeterRuntimeKit` | Vendor bootstrapper embedding MSI, x64 | `installer_scope: ""` (manifest declares no scope; embedded MSI is per-machine). Install `/q /nosplash /ComponentArgs "*":"/quiet /norestart"`; uninstall via msiexec by ARP name prefix. Identity verified by carving the embedded MSI. | | ClipboardFusion | `BinaryFortress.ClipboardFusion` | Inno, machine, x64 | ARP name carries a locale-dependent "(64-bit)" suffix → fuzzy pattern `ClipboardFusion%`; `/LAUNCHAFTER=0` prevents post-install launch. | | CloudShow | `BinaryFortress.CloudShow` | Inno, machine, x64 | Same Binary Fortress framework; ARP name "CloudShow Launcher (64-bit)" is framework-inferred (no third-party corroboration exists) — the CI validator is the confirmation. | | ClockAssist | `ClockAssist.ClockAssist` | MSI, machine, x64 | Latest-pointer URL but actively maintained manifest (bot replaces the single version dir ~6-weekly) → `ignore_hash`. | | Crestron AirMedia | `Crestron.AirMedia` | MSI, machine, x86 | ARP name is "Crestron AirMedia Machine-Wide Installer" (Teams-style per-user stamping); identity from msiinfo. | | Crestron AirMedia Peripherals | `Crestron.AirMediaPeripherals` | MSI, machine, x64 | Clean MSI. LaunchCondition fails install (1603) on hosts with a pending reboot — noted. | | CrisisGo | `CrisisGo.CrisisGo` | InstallShield Basic MSI, machine, x86 | Custom install script passes `ISSETUPDRIVEN=1` to defuse the "must run setup.exe" guard (vendor ships this bare MSI for network deployment; winget sandbox validates it). | | Cyberduck CLI | `Iterate.CyberduckCLI` | MSI, machine, x64 | Clean WiX MSI. winget lags the vendor by ~4 releases, but pinned URLs stay live, so installs work — just not bleeding-edge. | | Cisco Webex Recorder and Player | `Cisco.WebexRecorderAndPlayer` | InstallShield MSI, machine, x86 | Legacy WRF player but actively updated by Cisco (8 winget bumps in 10 months); latest-pointer URL → `ignore_hash`. | | Creative Force Kelvin | `CreativeForce.Kelvin` | WiX MSI, machine, x64 | Manifest offers per-user NSIS, machine NSIS, and MSI — the MSI is selected (unversioned ARP name "Kelvin"). | | Creative Force Triad | `CreativeForce.Triad` | NSIS (electron-builder), machine, x64 | `/allusers /S`; versioned ARP name → fuzzy. | | Cube Browser | `RystadEnergy.CubeBrowser` | WiX burn bundle, machine, x64 | Bundle Arp manifest carved from the installer ("Cube Browser (64 bit)"); dual-mode uninstall (prefers bundle entry, msiexec fallback) since bundle + chained MSI may both register the same name. | Considered but **not** added (recorded in the workstream tracker): - **Citrix Workspace app LTSR** (`Citrix.Workspace.LTSR`): the LTSR and current tracks register the **identical** ARP key (`CitrixOnlinePluginPackWeb`) and the same "Citrix Workspace \<YYMM\>" DisplayName with no LTSR marker — the only discriminator is a registry value outside the programs table, so Fleet inventory cannot tell the tracks apart. An LTSR FMA would cross-match current-release installs (and vice versa). **Consequence for the shipped Citrix Workspace FMA:** hosts running LTSR will match it and may show "update available" toward the current release — flagged here for reviewer judgment. - **Charles** (`XK72.Charles`): all winget 5.x manifests are per-user MSIX only. The vendor ships a machine-scope MSI for 5.2 but it isn't indexed in winget; revisit if the manifest adds it. - **Calibrite Profiler** (`Calibrite.PROFILER`): abandoned winget manifest — one version dir ever while the vendor is five releases and a major version ahead. - **Cloud Drive Mapper** (`IAMCloud.CloudDriveMapperV3`): latest-pointer URL whose manifest SHA is already stale, and the MSI sets `ARPSYSTEMCOMPONENT=1`, hiding the ARP entry from inventory entirely. - **CloudCompare** (`CloudCompare.CloudCompare`): registry DisplayVersion (and the winget PackageVersion itself) embed a parenthetical date — "2.13.2 (07-06-2024)" — which breaks version comparison; manifest also stale. - **Classic Shell** (`IvoSoft.ClassicShell`): development ended in 2017; superseded by Open-Shell. (CutePDF Writer was already deferred in the letter A batch.) Identities verified per app (msiinfo Property tables; burn bundle Arp manifests carved from installers; AppxManifest/electron-builder sources; uninstall-database corroboration). SHAs verified against manifests for pinned URLs; `ignore_hash` used only where the manifest is demonstrably actively maintained. Icons via `tools/software/icons/generate-icons.sh`; the pre-existing CitrixWorkspace icon component is reused untouched. # Checklist for submitter - [x] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements), JS inline code is prevented especially for url redirects, and untrusted data interpolated into shell scripts/commands is validated against shell metacharacters. - [x] Timeouts are implemented and retries are limited to avoid infinite loops ## Testing - [ ] QA'd all new/changed functionality manually (relying on the FMA CI validator for Windows install/uninstall validation)
78 lines
2.9 KiB
PowerShell
78 lines
2.9 KiB
PowerShell
# Uninstalls Cube Browser.
|
|
#
|
|
# The app is a WiX "burn" bundle that chains an MSI, and both may register
|
|
# ARP entries with DisplayName "Cube Browser (64 bit)". Prefer the bundle
|
|
# entry (an .exe UninstallString, uninstalls the whole chain with
|
|
# /uninstall /quiet /norestart); fall back to the chained MSI entry
|
|
# (msiexec /X{ProductCode}) if only that one is present.
|
|
|
|
$softwareName = "Cube Browser (64 bit)"
|
|
|
|
$machineKey = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*'
|
|
$machineKey32on64 = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
|
|
|
|
function Split-UninstallString {
|
|
param([string]$raw)
|
|
# Parse into executable + args, handling quoted/unquoted/bare shapes.
|
|
if ($raw -match '^\s*"([^"]+)"\s*(.*)$') {
|
|
return @($matches[1], $matches[2].Trim())
|
|
} elseif ($raw -match '(?i)^\s*(.+?\.exe)\s*(.*)$') {
|
|
return @($matches[1], $matches[2].Trim())
|
|
}
|
|
return @($raw, "")
|
|
}
|
|
|
|
$exitCode = $null
|
|
|
|
try {
|
|
|
|
[array]$uninstallKeys = Get-ChildItem `
|
|
-Path @($machineKey, $machineKey32on64) `
|
|
-ErrorAction SilentlyContinue |
|
|
ForEach-Object { Get-ItemProperty $_.PSPath }
|
|
|
|
[array]$matches_ = $uninstallKeys | Where-Object { $_.DisplayName -eq $softwareName }
|
|
|
|
# Prefer the burn bundle entry (non-msiexec .exe uninstaller) over the chained MSI.
|
|
$bundle = $matches_ | Where-Object {
|
|
$raw = if ($_.QuietUninstallString) { $_.QuietUninstallString } else { $_.UninstallString }
|
|
$raw -and $raw -notmatch '(?i)msiexec'
|
|
} | Select-Object -First 1
|
|
$entry = if ($bundle) { $bundle } else { $matches_ | Select-Object -First 1 }
|
|
|
|
if ($entry) {
|
|
$raw = if ($entry.QuietUninstallString) { $entry.QuietUninstallString } else { $entry.UninstallString }
|
|
$exe, $exeArgs = Split-UninstallString -raw $raw
|
|
|
|
if ($exe -match '(?i)msiexec') {
|
|
if ($exeArgs -notmatch '(?i)/(x|uninstall)') { $exeArgs = "/X $exeArgs" }
|
|
if ($exeArgs -notmatch '(?i)/(qn|quiet)') { $exeArgs = "$exeArgs /qn" }
|
|
if ($exeArgs -notmatch '(?i)/norestart') { $exeArgs = "$exeArgs /norestart" }
|
|
} else {
|
|
if ($exeArgs -notmatch '/uninstall') { $exeArgs = "/uninstall $exeArgs" }
|
|
if ($exeArgs -notmatch '/quiet') { $exeArgs = "$exeArgs /quiet" }
|
|
if ($exeArgs -notmatch '/norestart') { $exeArgs = "$exeArgs /norestart" }
|
|
}
|
|
$exeArgs = $exeArgs.Trim()
|
|
|
|
Write-Host "Uninstall command: $exe"
|
|
Write-Host "Uninstall args: $exeArgs"
|
|
$process = Start-Process -FilePath $exe -ArgumentList $exeArgs -NoNewWindow -PassThru -Wait
|
|
$exitCode = $process.ExitCode
|
|
}
|
|
|
|
} catch {
|
|
Write-Host "Error: $_"
|
|
Exit 1
|
|
}
|
|
|
|
if ($null -eq $exitCode) {
|
|
Write-Host "Uninstall entry not found for '$softwareName'."
|
|
Exit 1
|
|
}
|
|
|
|
Write-Host "Uninstall exit code: $exitCode"
|
|
# 0 = success, 3010 = success but reboot required, 1641 = reboot initiated
|
|
if ($exitCode -eq 3010 -or $exitCode -eq 1641) { Exit 0 }
|
|
Exit $exitCode
|