Fix windev.ps1 self-elevation logic and refactor code

This commit is contained in:
Cryostrixx 2024-09-22 08:57:20 -07:00
parent 662139ca5f
commit 683e681a9a
No known key found for this signature in database
GPG Key ID: 2FC11420AFB82801

View File

@ -1,55 +1,184 @@
<# <#
.SYNOPSIS .SYNOPSIS
This Script is used as a target for the https://christitus.com/windev alias. This script is used as a target for the https://christitus.com/windev alias.
It queries the latest winget release (no matter if Pre-Release, Draft or Full Release) and invokes It It queries the latest WinUtil release (no matter if it's a Pre-Release, Draft, or Full Release) and runs it.
.DESCRIPTION .DESCRIPTION
This Script provides a simple way to always start the bleeding edge release even if it's not yet a full release. This script provides a simple way to start the bleeding edge release even if it's not yet a full release.
This function should be run with administrative privileges. This function can be run both with administrator and non-administrator permissions.
Because this way of recursively invoking scripts via Invoke-Expression it might very well happen that AV Programs flag this because it's a common way of mulitstage exploits to run If it is not running as administrator, it will attempt to relaunch itself with administrator permissions.
The script no longer uses Invoke-Expression for its execution and now relies on Start-Process.
.EXAMPLE .EXAMPLE
irm https://christitus.com/windev | iex Run in PowerShell > irm https://christitus.com/windev | iex
OR OR
Run in Admin Powershell > ./windev.ps1 Run in PowerShell > .\windev.ps1
OR
Run in PowerShell > iex "& { $(irm https://christitus.com/windev) } <arguments>"
OR
Run in PowerShell > .\windev.ps1 <arguments>
.NOTES
Below are some usage examples for running the script with arguments:
Run in PowerShell > iex "& { $(irm https://christitus.com/windev) } -Config 'C:\your\config\file\path\here'"
OR
Run in PowerShell > .\windev.ps1 -Config "C:\your\config\file\path\here"
OR
Run in PowerShell > iex "& { $(irm https://christitus.com/windev) } -Run -Config 'C:\your\config\file\path\here'"
OR
Run in PowerShell > .\windev.ps1 -Run -Config "C:\your\config\file\path\here"
#> #>
# Function to fetch the latest release tag from the GitHub API # Speed up download-related tasks by suppressing the output of Write-Progress.
function Get-LatestRelease { $ProgressPreference = "SilentlyContinue"
# Determine whether or not the active process is currently running as administrator.
$ProcessIsElevated = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
# Function to get the latest stable or pre-release tag from the repository's releases page.
function Get-WinUtilReleaseTag {
# Retrieve the list of released versions from the repository's releases page.
try { try {
$releases = Invoke-RestMethod -Uri 'https://api.github.com/repos/ChrisTitusTech/winutil/releases' $ReleasesList = Invoke-RestMethod 'https://api.github.com/repos/ChrisTitusTech/winutil/releases'
$latestRelease = $releases | Where-Object {$_.prerelease -eq $true} | Select-Object -First 1
return $latestRelease.tag_name
} catch { } catch {
Write-Host "Error fetching release data: $_" -ForegroundColor Red Write-Host "Error downloading WinUtil's releases list: $_" -ForegroundColor Red
return $latestRelease.tag_name break
} }
# Filter through the released versions and select the first matching stable version.
$StableRelease = $ReleasesList | Where-Object { $_.prerelease -eq $false } | Select-Object -First 1
# Filter through the released versions and select the first matching pre-release version.
$PreRelease = $ReleasesList | Where-Object { $_.prerelease -eq $true } | Select-Object -First 1
# If a stable or pre-release version is found, set the release tag to the returned version number.
if ($ReleasesList -and ($PreRelease -or $StableRelease)) {
$ReleaseTag = if ($PreRelease) {
$PreRelease.tag_name
} elseif ($StableRelease) {
$StableRelease.tag_name
}
}
# If no releases are found or a stable or pre-release doesn't exist default to using the 'latest' tag.
if (!$ReleasesList -or !($PreRelease -or $StableRelease)) {
$ReleaseTag = "latest"
}
# Return the release tag to facilitate the usage of the version number within other parts of the script.
return $ReleaseTag
} }
# Function to redirect to the latest pre-release version # Get the latest stable or pre-release tag; falling back to the 'latest' release tag if no releases are found.
function RedirectToLatestPreRelease { $WinUtilReleaseTag = Get-WinUtilReleaseTag
$latestRelease = Get-LatestRelease
if ($latestRelease) { # Function to generate the URL used to download the latest release of WinUtil from the releases page.
$url = "https://github.com/ChrisTitusTech/winutil/releases/download/$latestRelease/winutil.ps1" function Get-WinUtilReleaseURL {
$WinUtilDownloadURL = if ($WinUtilReleaseTag -eq "latest") {
"https://github.com/ChrisTitusTech/winutil/releases/$($WinUtilReleaseTag)/download/winutil.ps1"
} else { } else {
Write-Host 'Unable to determine latest pre-release version.' -ForegroundColor Red "https://github.com/ChrisTitusTech/winutil/releases/download/$($WinUtilReleaseTag)/winutil.ps1"
Write-Host "Using latest Full Release"
$url = "https://github.com/ChrisTitusTech/winutil/releases/latest/download/winutil.ps1"
} }
$script = Invoke-RestMethod $url return $WinUtilDownloadURL
# Elevate Shell if necessary }
if (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
Write-Output "Winutil needs to be run as Administrator. Attempting to relaunch."
$powershellcmd = if (Get-Command pwsh -ErrorAction SilentlyContinue) { "pwsh" } else { "powershell" } # Get the URL used to download the latest release of WinUtil from the releases page.
$processCmd = if (Get-Command wt.exe -ErrorAction SilentlyContinue) { "wt.exe" } else { $powershellcmd } $WinUtilReleaseURL = Get-WinUtilReleaseURL
Start-Process $processCmd -ArgumentList "$powershellcmd -ExecutionPolicy Bypass -NoProfile -Command $(Invoke-Expression $script)" -Verb RunAs # Create the file path that the latest WinUtil release will be downloaded to on the local disk.
} $WinUtilScriptPath = Join-Path "$env:TEMP" "winutil.ps1"
else{
Invoke-Expression $script # Function to download the latest release of WinUtil from the releases page to the local disk.
function Get-LatestWinUtil {
if (!(Test-Path $WinUtilScriptPath)) {
Invoke-RestMethod $WinUtilReleaseURL -OutFile $WinUtilScriptPath
} }
} }
# Call the redirect function # Function to check for any version changes to WinUtil, re-downloading the script if it has been upgraded/downgraded.
function Get-WinUtilUpdates {
# Make a web request to the latest WinUtil release URL and store the script's raw code for processing.
$RawWinUtilScript = (Invoke-WebRequest $WinUtilReleaseURL -UseBasicParsing).RawContent
RedirectToLatestPreRelease # Match the 'Version:' header and replace it with an empty string, leaving the script's version number behind.
$RemoteWinUtilVersion = ([regex]"\bVersion\s*:\s[\d.]+").Match($RawWinUtilScript).Value -replace ".*:\s", ""
$LocalWinUtilVersion = ([regex]"\bVersion\s*:\s[\d.]+").Match((Get-Content $WinUtilScriptPath)).Value -replace ".*:\s", ""
# If WinUtil has been upgraded since the last time it has been launched, re-download the WinUtil script.
if ([version]$RemoteWinUtilVersion -gt [version]$LocalWinUtilVersion) {
Write-Host "WinUtil has been upgraded since the last time it was launched. Downloading '$($RemoteWinUtilVersion)'..." -ForegroundColor DarkYellow
Invoke-RestMethod $WinUtilReleaseURL -OutFile $WinUtilScriptPath
}
# If WinUtil has been downgraded since the last time it has been launched, re-download the WinUtil script.
if ([version]$RemoteWinUtilVersion -lt [version]$LocalWinUtilVersion) {
Write-Host "WinUtil has been downgraded since the last time it was launched. Downloading '$($RemoteWinUtilVersion)'..." -ForegroundColor DarkYellow
Invoke-RestMethod $WinUtilReleaseURL -OutFile $WinUtilScriptPath
}
# If WinUtil is already up-to-date, skip updating the script and let the user know it is already up-to-date.
if ([version]$RemoteWinUtilVersion -eq [version]$LocalWinUtilVersion) {
Write-Host "WinUtil is already up-to-date. Skipped update checking." -ForegroundColor Yellow
}
}
# Function to start the latest release of WinUtil that was previously downloaded and saved to '$env:TEMP\winutil.ps1'.
function Start-LatestWinUtil {
param (
[Parameter(Mandatory = $false)]
[array]$WinUtilArgumentsList
)
# Setup the commands used to launch WinUtil using Windows Terminal or Windows PowerShell/PowerShell Core.
$PowerShellCommand = if (Get-Command pwsh.exe -ErrorAction SilentlyContinue) { "pwsh.exe" } else { "powershell.exe" }
$ProcessCommand = if (Get-Command wt.exe -ErrorAction SilentlyContinue) { "wt.exe" } else { $PowerShellCommand }
# Setup the script's launch arguments based on the presence of Windows Terminal or Windows PowerShell/PowerShell Core:
# 1. Windows Terminal needs the name of the process to start ($PowerShellCommand) in addition to the launch arguments.
# 2. Windows PowerShell and PowerShell Core can receive and use the launch arguments as is without extra modification.
if ($ProcessCommand -ne $PowerShellCommand) {
$WinUtilLaunchArguments = "$PowerShellCommand -ExecutionPolicy Bypass -NoProfile -File `"$WinUtilScriptPath`""
} else {
$WinUtilLaunchArguments = "-ExecutionPolicy Bypass -NoProfile -File `"$WinUtilScriptPath`""
}
# If WinUtil's launch arguments are provided, append them to the end of the list of current launch arguments.
if ($WinUtilArgumentsList) {
$WinUtilLaunchArguments += " " + $($WinUtilArgumentsList -join " ")
}
# If the WinUtil script is not running as administrator, relaunch the script with administrator permissions.
if (!$ProcessIsElevated) {
Write-Host "WinUtil is not running as administrator. Relaunching..." -ForegroundColor DarkCyan
Write-Host "Running the selected WinUtil release: Version '$($WinUtilReleaseTag)'." -ForegroundColor Green
Start-Process $ProcessCommand -ArgumentList $WinUtilLaunchArguments -Wait -Verb RunAs
} else {
Write-Host "Running the selected WinUtil release: Version '$($WinUtilReleaseTag)'." -ForegroundColor Green
Start-Process $ProcessCommand -ArgumentList $WinUtilLaunchArguments -Wait
}
}
# If WinUtil has not already been downloaded, attempt to download it and save it to '$env:TEMP\winutil.ps1'.
try {
Get-LatestWinUtil
} catch {
Write-Host "Error downloading WinUtil '$($WinUtilReleaseTag)': $_" -ForegroundColor Red
break
}
# Attempt to check for WinUtil updates, if a version is released or removed this will re-download WinUtil.
try {
Get-WinUtilUpdates
} catch {
Write-Host "Error updating WinUtil '$($WinUtilReleaseTag)': $_" -ForegroundColor Red
break
}
# Attempt to start the latest release of WinUtil saved to the local disk; also supports WinUtil's arguments.
try {
if ($args) {
Start-LatestWinUtil $args
} else {
Start-LatestWinUtil
}
} catch {
Write-Host "Error launching WinUtil '$($WinUtilReleaseTag)': $_" -ForegroundColor Red
}