Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions .github/workflows/required.yml
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,58 @@ jobs:
[ "$rc" -eq 0 ] && echo "ok — Nix installer pin agrees across install.sh, install.ps1 and both libs"
exit "$rc"

- name: Bun installer digests match their pinned tags
# Same contract as the Nix and hook pins. Bun's installers are run
# rather than reimplemented — they do CPU baseline detection we do not
# want to duplicate — so what we verify is the script, pinned to a tag.
#
# Note the two are checked independently: the shell installer is
# byte-identical to bun.sh/install, the PowerShell one is not, so they
# cannot be assumed to move together.
run: |
set -euo pipefail
rc=0

# Extract by variable name rather than by matching a URL or a bare
# 64-hex run. bun.ps1 carries two pins, so a "first hex string wins"
# pattern would silently check one of them twice.
verify() { # label, url, want
if [ -z "$2" ] || [ -z "$3" ]; then
echo "::error::could not read the $1 pin — the extraction in this workflow no longer matches the file"
rc=1; return
fi
actual="$(curl -fsSL --proto '=https' --proto-redir '=https' --tlsv1.2 "$2" | sha256sum | cut -d' ' -f1)"
if [ "$actual" != "$3" ]; then
echo "::error::$1: $2 now hashes to $actual, but the pin says $3"
echo "::error::Review the change, then update scripts/lib/bun.sh and scripts/lib/bun.ps1 together."
rc=1
else
echo "ok — $1 matches its pin"
fi
}

verify "bun.sh install.sh" \
"$(sed -n 's/.*bun_url="\([^"]*\)".*/\1/p' scripts/lib/bun.sh)" \
"$(sed -n 's/.*bun_sha="\([^"]*\)".*/\1/p' scripts/lib/bun.sh)"

verify "bun.ps1 install.ps1" \
"$(sed -n "s/.*bunUrl = '\([^']*\)'.*/\1/p" scripts/lib/bun.ps1)" \
"$(sed -n "s/.*bunSha = '\([^']*\)'.*/\1/p" scripts/lib/bun.ps1)"

verify "bun.ps1 install.sh" \
"$(sed -n "s/.*shUrl = '\([^']*\)'.*/\1/p" scripts/lib/bun.ps1)" \
"$(sed -n "s/.*shSha = '\([^']*\)'.*/\1/p" scripts/lib/bun.ps1)"

# The shell installer is pinned in two files; they must not drift.
a="$(sed -n 's/.*bun_sha="\([^"]*\)".*/\1/p' scripts/lib/bun.sh)"
b="$(sed -n "s/.*shSha = '\([^']*\)'.*/\1/p" scripts/lib/bun.ps1)"
if [ "$a" != "$b" ]; then
echo "::error::bun.sh and bun.ps1 pin different digests for install.sh: $a vs $b"
rc=1
fi

exit "$rc"

- name: gen-pins missing-PINS guard is reachable
# `grep -c` exits 1 on zero matches, so without `|| true` the command
# substitution's status kills the script under `set -e` one line before
Expand Down
50 changes: 48 additions & 2 deletions scripts/lib/bun.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,60 @@ function Install-Bun {
Log-Info 'Installing Bun...'
switch ($script:OsType) {
'windows' {
Invoke-RestMethod 'https://bun.sh/install.ps1' | Invoke-Expression
# Bun's own Windows installer, pinned to a tag and digest-checked
# before it runs. Running their script rather than reimplementing
# the download keeps their CPU baseline detection, which picks
# bun-windows-x64 vs -baseline; choosing wrong yields a binary that
# dies on older hardware.
#
# Note this is the tagged src/cli/install.ps1, which is NOT
# byte-identical to what bun.sh/install.ps1 serves — unlike the
# shell installer, where the two match. The tagged copy is the one
# belonging to this release, so it is the one worth pinning.
#
# Bump both lines together; required.yml re-checks the digest.
$bunUrl = 'https://github.com/@raw/oven-sh/bun/bun-v1.3.14/src/cli/install.ps1'
$bunSha = '54fd5c34e08d2e363e9ee4cc52f58eca72b3c307c170869eec1e394c16fb7744'
$stage = Join-Path ([System.IO.Path]::GetTempPath()) ([System.Guid]::NewGuid().ToString())
New-Item -ItemType Directory -Path $stage -Force | Out-Null
try {
$installer = Join-Path $stage 'install.ps1'
Invoke-WebRequest -Uri $bunUrl -OutFile $installer -UseBasicParsing -ErrorAction Stop
$got = (Get-FileHash -Path $installer -Algorithm SHA256).Hash.ToLowerInvariant()
if ($got -ne $bunSha) {
Log-Error 'Bun installer checksum mismatch — refusing to run it.'
Log-Error " expected $bunSha"
Log-Error " got $got"
return $false
}
& $installer
}
finally {
Remove-Item -Recurse -Force $stage -ErrorAction SilentlyContinue
}
}
{ $_ -in 'linux','macos' } {
if (-not (Test-Command 'bash')) {
Log-Error 'bash required for Bun install.'
return $false
}
bash -c "curl -fsSL --proto '=https' --proto-redir '=https' --tlsv1.2 https://bun.sh/install | bash"
# Same pinned installer scripts/lib/bun.sh uses, verified the same
# way. Kept as one bash invocation so the digest check and the run
# cannot be separated by a caller.
$shUrl = 'https://github.com/@raw/oven-sh/bun/bun-v1.3.14/src/cli/install.sh'
$shSha = 'bab8acfb046aac8c72407bdcce903957665d655d7acaa3e11c7c4616beae68dd'
bash -c @"
set -e
tmp=`$(mktemp -d)
trap 'rm -rf "`$tmp"' EXIT
curl -fsSL --proto '=https' --proto-redir '=https' --tlsv1.2 '$shUrl' -o "`$tmp/install.sh"
got=`$(sha256sum "`$tmp/install.sh" 2>/dev/null | cut -d' ' -f1 || shasum -a 256 "`$tmp/install.sh" | cut -d' ' -f1)
if [ "`$got" != '$shSha' ]; then
echo "Bun installer checksum mismatch: expected $shSha, got `$got" >&2
exit 1
fi
bash "`$tmp/install.sh"
"@
$env:BUN_INSTALL = "$HOME/.bun"
$env:PATH = "$env:BUN_INSTALL/bin:$env:PATH"
}
Expand Down
63 changes: 55 additions & 8 deletions scripts/lib/bun.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,66 @@ install_bun() {
log_info "Installing Bun..."
case "$OS_TYPE" in
linux|macos)
# --proto/--proto-redir pin the whole redirect chain to https, to
# match what install.sh does for its own vendor installer.
curl -fsSL --proto '=https' --proto-redir '=https' --tlsv1.2 \
https://bun.sh/install | bash
# Bun's own installer, pinned to a tag and digest-checked before it
# runs. bun.sh/install is byte-identical to this tagged path today,
# so pinning costs nothing.
#
# Running their script rather than reimplementing the download is
# deliberate: it does CPU baseline detection (bun-linux-x64 vs
# -baseline for machines without AVX2). Getting that wrong installs
# a binary that dies with SIGILL on older hardware — a worse and
# far more confusing failure than the one this guards against.
#
# Bump both lines together; required.yml re-checks the digest.
local bun_url="https://github.com/@raw/oven-sh/bun/bun-v1.3.14/src/cli/install.sh"
local bun_sha="bab8acfb046aac8c72407bdcce903957665d655d7acaa3e11c7c4616beae68dd"

# Subshell with an EXIT trap, not a RETURN trap in this function —
# same reasoning as scripts/lib/nix.sh. A RETURN trap set inside a
# function is not confined to it: under `set -T` it persists and
# fires on unrelated later returns, it silently replaces any RETURN
# trap the caller installed, and under `set -u` the out-of-scope
# temp variable makes it abort with "unbound variable". These are
# library functions meant to be sourced, so that is a realistic way
# for a caller to be broken by us.
if ! (
set -e
bun_tmp="$(mktemp -d)"
trap 'rm -rf "$bun_tmp"' EXIT

if ! curl -fsSL --proto '=https' --proto-redir '=https' --tlsv1.2 \
"$bun_url" -o "$bun_tmp/install.sh"; then
log_error "Could not download the Bun installer."
exit 1
fi
if command_exists sha256sum; then
bun_got="$(sha256sum "$bun_tmp/install.sh" | cut -d' ' -f1)"
else
bun_got="$(shasum -a 256 "$bun_tmp/install.sh" | cut -d' ' -f1)"
fi
if [ "$bun_got" != "$bun_sha" ]; then
log_error "Bun installer checksum mismatch — refusing to run it."
log_error " expected $bun_sha"
log_error " got $bun_got"
exit 1
fi
bash "$bun_tmp/install.sh"
); then
log_error "Bun installation failed."
return 1
fi
export BUN_INSTALL="$HOME/.bun"
export PATH="$BUN_INSTALL/bin:$PATH"
;;
windows)
if command_exists powershell.exe; then
# Scheme spelled out. This read `irm bun.sh/install.ps1`, and a
# bare host lets the request begin as plaintext http — for a
# response piped straight into iex.
powershell.exe -Command "irm https://bun.sh/install.ps1 | iex"
# Delegate to bun.ps1, which pins and verifies the Windows
# installer. This used to be `irm bun.sh/install.ps1 | iex` —
# unverified, and until recently schemeless as well, so the
# request could have begun over plaintext http.
local _bun_lib
_bun_lib="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/bun.ps1"
powershell.exe -NoProfile -Command ". '$_bun_lib'; Install-Bun"
else
log_error "PowerShell required for Bun installation on Windows."
return 1
Expand Down
Loading