diff --git a/template/Setup-WinBuild2025.ps1 b/template/Setup-WinBuild2025.ps1 index 40c89ed..3be4486 100644 --- a/template/Setup-WinBuild2025.ps1 +++ b/template/Setup-WinBuild2025.ps1 @@ -1196,23 +1196,42 @@ $nsisExe = 'C:\Program Files (x86)\NSIS\makensis.exe' if (Test-Path $nsisExe) { Write-Host "NSIS already installed." } else { - $nsisUrl = "https://prdownloads.sourceforge.net/nsis/nsis-$NSISVersion-setup.exe" + # SourceForge redirect chain: + # Step 1: sf.net/.../download?use_mirror=autoselect → 200 HTML meta-refresh + # The meta-refresh URL is: https://downloads.sourceforge.net/project/nsis/...?ts=TOKEN + # That tokenised URL resolves directly to the binary (no further HTML). + # Strategy: fetch the HTML page, extract the meta-refresh URL, download it immediately + # (the ts= token is short-lived, so we use it right away). + $nsisPageUrl = "https://sourceforge.net/projects/nsis/files/NSIS%20$($NSISVersion.Split('.')[0])/$NSISVersion/nsis-$NSISVersion-setup.exe/download?use_mirror=autoselect" $nsisInstaller = 'C:\CI\nsis_installer.exe' - Write-Host "Downloading NSIS $NSISVersion from SourceForge..." - # SourceForge returns an HTML redirect page when accessed with PS default User-Agent. - # Supplying a browser-like User-Agent forces the CDN to serve the raw binary. + $sfUA = @{ 'User-Agent' = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)' } + + Write-Host "Resolving NSIS $NSISVersion download URL from SourceForge..." try { - Invoke-WebRequest -Uri $nsisUrl -OutFile $nsisInstaller -UseBasicParsing ` - -Headers @{ 'User-Agent' = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)' } - } catch { throw "Failed to download NSIS from '$nsisUrl': $_" } - # Guard: verify downloaded file is a valid PE executable (MZ magic bytes). - # If SourceForge still returned an HTML page the installer would silently corrupt - # the WinRM session — fail fast with a clear message instead. + $sfPage = Invoke-WebRequest -Uri $nsisPageUrl -UseBasicParsing -MaximumRedirection 5 ` + -Headers $sfUA + } catch { throw "Failed to reach SourceForge download page: $_" } + + # Extract tokenised URL from: + $nsisCdnUrl = $null + if ($sfPage.RawContent -match 'content="\d+;\s*url=(https://downloads\.sourceforge\.net/[^"]+)"') { + $nsisCdnUrl = $Matches[1] + } + if (-not $nsisCdnUrl) { + throw "Could not extract tokenised download URL from SourceForge page for NSIS $NSISVersion. " + + "Page structure may have changed." + } + Write-Host "Downloading NSIS $NSISVersion from SourceForge..." + try { + Invoke-WebRequest -Uri $nsisCdnUrl -OutFile $nsisInstaller -UseBasicParsing -Headers $sfUA + } catch { throw "Failed to download NSIS from '$nsisCdnUrl': $_" } + + # Guard: verify the file is a valid PE executable (MZ magic bytes). $peBytes = [System.IO.File]::ReadAllBytes($nsisInstaller) if ($peBytes.Length -lt 2 -or $peBytes[0] -ne 0x4D -or $peBytes[1] -ne 0x5A) { Remove-Item $nsisInstaller -ErrorAction SilentlyContinue throw "NSIS download is not a valid PE executable (MZ header missing). " + - "SourceForge likely returned an HTML page. Set `$script:Hashes['NSIS']` to pin a known-good hash." + "SourceForge may have returned HTML instead of the binary." } Assert-Hash -FilePath $nsisInstaller -Label "nsis-$NSISVersion-setup.exe" ` -Expected $script:Hashes['NSIS']