<# .SYNOPSIS AlertaVuln CLI installer - Windows (PowerShell 5.1+). .DESCRIPTION One-liner: irm https://get.alertavuln.com/cli/install.ps1 | iex With parameters (the pipe-to-iex form cannot take arguments): & ([scriptblock]::Create((irm https://get.alertavuln.com/cli/install.ps1))) -Version v0.4.0 -WithMcpRouter Downloads the Windows CLI binary from Azure Blob Storage, verifies it against the release's SHA256SUMS with Get-FileHash (hard-fails on any mismatch), and installs it to $env:LOCALAPPDATA\Programs\AlertaVuln as alertavuln.exe plus an av.exe copy. The install dir is appended to the USER Path unless -NoPathUpdate is given. On Linux/macOS use install.sh instead: curl -fsSL https://get.alertavuln.com/cli/install.sh | sh .PARAMETER Version Version to install, e.g. v0.4.0 (default: "latest" from the published version.json). .PARAMETER WithMcpRouter Also install the AlertaVuln MCP router binary (mcp-router.exe). .PARAMETER NoPathUpdate Do not modify the user PATH environment variable. .PARAMETER DryRun Resolve platform + version and print the URLs that would be used; downloads and installs NOTHING. Combined with -Version, performs no network I/O at all. #> [CmdletBinding()] param( [string]$Version = '', [switch]$WithMcpRouter, [switch]$NoPathUpdate, [switch]$DryRun ) $ErrorActionPreference = 'Stop' $BaseUrl = 'https://downloads.alertavuln.com' $InstallDir = Join-Path $env:LOCALAPPDATA 'Programs\AlertaVuln' function Get-ExpectedHash { param([string]$SumsPath, [string]$FileName) # SHA256SUMS uses `sha256sum` format: " " (optionally "*"). foreach ($line in Get-Content -LiteralPath $SumsPath) { if ($line -match '^([0-9a-fA-F]{64})\s+\*?(.+)$') { if ($Matches[2].Trim() -eq $FileName) { return $Matches[1].ToLowerInvariant() } } } return $null } function Assert-Checksum { param([string]$FilePath, [string]$FileName, [string]$SumsPath) $expected = Get-ExpectedHash -SumsPath $SumsPath -FileName $FileName if (-not $expected) { throw "install.ps1: no entry for $FileName in SHA256SUMS - refusing to install." } $actual = (Get-FileHash -LiteralPath $FilePath -Algorithm SHA256).Hash.ToLowerInvariant() if ($actual -ne $expected) { throw ("install.ps1: SHA-256 MISMATCH for {0}`n expected: {1}`n actual: {2}`nThe download is corrupt or has been tampered with. NOT installing." -f $FileName, $expected, $actual) } Write-Host "verified $FileName (sha256 ok)" } # ---- platform checks -------------------------------------------------------- if ($PSVersionTable.PSEdition -eq 'Core' -and -not $IsWindows) { throw 'install.ps1: this script targets Windows. On Linux/macOS run: curl -fsSL https://get.alertavuln.com/cli/install.sh | sh' } # Only windows-amd64 is published. ARM64 Windows runs amd64 binaries through # emulation; 32-bit x86 is not supported. $archRaw = $env:PROCESSOR_ARCHITECTURE if ($archRaw -eq 'x86') { throw 'install.ps1: 32-bit Windows is not supported (only windows-amd64 binaries are published).' } if ($archRaw -eq 'ARM64') { Write-Warning 'No native windows-arm64 build is published; installing the amd64 binary (runs under emulation).' } # PowerShell 5.1 defaults can exclude TLS 1.2 - make sure it is enabled. try { [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 } catch { } # ---- version resolution ------------------------------------------------------- if (-not $Version) { try { $meta = Invoke-RestMethod -Uri "$BaseUrl/version.json" -UseBasicParsing } catch { throw "install.ps1: could not fetch $BaseUrl/version.json - pass -Version vX.Y.Z or try again later. ($($_.Exception.Message))" } $Version = $meta.latest if (-not $Version) { throw "install.ps1: could not read 'latest' from $BaseUrl/version.json" } } if ($Version -notmatch '^v') { $Version = "v$Version" } if ($Version -notmatch '^v\d+\.\d+\.\d+') { throw "install.ps1: invalid -Version '$Version' (expected vX.Y.Z)" } $binName = 'alertavuln-windows-amd64.exe' $routerName = 'mcp-router-windows-amd64.exe' $binUrl = "$BaseUrl/$Version/$binName" $routerUrl = "$BaseUrl/$Version/$routerName" $sumsUrl = "$BaseUrl/$Version/SHA256SUMS" # ---- dry run: print what would happen, touch nothing -------------------------- if ($DryRun) { Write-Host "dry-run: os=windows arch=amd64 version=$Version" Write-Host "dry-run: binary-url=$binUrl" Write-Host "dry-run: checksums-url=$sumsUrl" if ($WithMcpRouter) { Write-Host "dry-run: mcp-router-url=$routerUrl" } Write-Host "dry-run: install-dir=$InstallDir" Write-Host 'dry-run: nothing downloaded, nothing installed' return } # ---- download + verify + install ---------------------------------------------- $tmp = Join-Path ([System.IO.Path]::GetTempPath()) ('alertavuln-install-' + [guid]::NewGuid().ToString('N')) New-Item -ItemType Directory -Path $tmp | Out-Null try { $binPath = Join-Path $tmp $binName $sumsPath = Join-Path $tmp 'SHA256SUMS' Write-Host "Downloading $binUrl" Invoke-WebRequest -Uri $binUrl -OutFile $binPath -UseBasicParsing Write-Host "Downloading $sumsUrl" Invoke-WebRequest -Uri $sumsUrl -OutFile $sumsPath -UseBasicParsing Assert-Checksum -FilePath $binPath -FileName $binName -SumsPath $sumsPath if ($WithMcpRouter) { $routerPath = Join-Path $tmp $routerName Write-Host "Downloading $routerUrl" try { Invoke-WebRequest -Uri $routerUrl -OutFile $routerPath -UseBasicParsing } catch { throw "install.ps1: download failed: $routerUrl (the MCP router ships from the release after v0.3.0). ($($_.Exception.Message))" } Assert-Checksum -FilePath $routerPath -FileName $routerName -SumsPath $sumsPath } New-Item -ItemType Directory -Force -Path $InstallDir | Out-Null Copy-Item -LiteralPath $binPath -Destination (Join-Path $InstallDir 'alertavuln.exe') -Force Copy-Item -LiteralPath $binPath -Destination (Join-Path $InstallDir 'av.exe') -Force if ($WithMcpRouter) { Copy-Item -LiteralPath $routerPath -Destination (Join-Path $InstallDir 'mcp-router.exe') -Force } } finally { Remove-Item -Recurse -Force -Path $tmp -ErrorAction SilentlyContinue } # ---- PATH ----------------------------------------------------------------------- if (-not $NoPathUpdate) { $userPath = [Environment]::GetEnvironmentVariable('Path', 'User') if (-not $userPath) { $userPath = '' } $parts = $userPath -split ';' | Where-Object { $_ } if ($parts -notcontains $InstallDir) { $trimmed = $userPath.TrimEnd(';') if ($trimmed) { $newPath = $trimmed + ';' + $InstallDir } else { $newPath = $InstallDir } [Environment]::SetEnvironmentVariable('Path', $newPath, 'User') Write-Host "Added $InstallDir to your user PATH (new terminals pick it up automatically)." } # Make this session work immediately too. if (($env:Path -split ';') -notcontains $InstallDir) { $env:Path = $env:Path.TrimEnd(';') + ';' + $InstallDir } } else { Write-Host "Skipped PATH update (-NoPathUpdate). Add $InstallDir to your PATH to use 'av' from any terminal." } # ---- wrap up ---------------------------------------------------------------------- Write-Host '' Write-Host "AlertaVuln CLI $Version installed: $InstallDir\alertavuln.exe (alias: av.exe)" if ($WithMcpRouter) { Write-Host "MCP router installed: $InstallDir\mcp-router.exe" } Write-Host '' Write-Host 'Next steps:' Write-Host ' av login # authenticate' Write-Host ' av check npm vite 6.0.0 # vet a package before you adopt it' Write-Host '' Write-Host 'Docs: https://docs.alertavuln.com'