Fix: Harden pinned runtime activation
This commit is contained in:
@@ -96,12 +96,12 @@ load_installer_provenance() {
|
|||||||
local -A parsed=() allowed=() required_values=()
|
local -A parsed=() allowed=() required_values=()
|
||||||
|
|
||||||
for name in \
|
for name in \
|
||||||
BUN_VERSION BUN_X86_64_URL BUN_X86_64_SHA256 BUN_X86_64_MAX_BYTES \
|
BUN_VERSION BUN_X86_64_URL BUN_X86_64_SHA256 BUN_X86_64_BINARY_SHA256 BUN_X86_64_MAX_BYTES \
|
||||||
BUN_AARCH64_URL BUN_AARCH64_SHA256 BUN_AARCH64_MAX_BYTES \
|
BUN_AARCH64_URL BUN_AARCH64_SHA256 BUN_AARCH64_BINARY_SHA256 BUN_AARCH64_MAX_BYTES \
|
||||||
NODE_VERSION NODE_X86_64_URL NODE_X86_64_SHA256 NODE_X86_64_MAX_BYTES \
|
NODE_VERSION NODE_X86_64_URL NODE_X86_64_SHA256 NODE_X86_64_BINARY_SHA256 NODE_X86_64_MAX_BYTES \
|
||||||
NODE_AARCH64_URL NODE_AARCH64_SHA256 NODE_AARCH64_MAX_BYTES \
|
NODE_AARCH64_URL NODE_AARCH64_SHA256 NODE_AARCH64_BINARY_SHA256 NODE_AARCH64_MAX_BYTES \
|
||||||
CODEX_VERSION CODEX_X86_64_URL CODEX_X86_64_SHA256 CODEX_X86_64_MAX_BYTES \
|
CODEX_VERSION CODEX_X86_64_URL CODEX_X86_64_SHA256 CODEX_X86_64_BINARY_SHA256 CODEX_X86_64_MAX_BYTES \
|
||||||
CODEX_AARCH64_URL CODEX_AARCH64_SHA256 CODEX_AARCH64_MAX_BYTES \
|
CODEX_AARCH64_URL CODEX_AARCH64_SHA256 CODEX_AARCH64_BINARY_SHA256 CODEX_AARCH64_MAX_BYTES \
|
||||||
RUSTDESK_VERSION RUSTDESK_X86_64_URL RUSTDESK_X86_64_SHA256 RUSTDESK_X86_64_MAX_BYTES \
|
RUSTDESK_VERSION RUSTDESK_X86_64_URL RUSTDESK_X86_64_SHA256 RUSTDESK_X86_64_MAX_BYTES \
|
||||||
FEDORA_RELEASE RPMFUSION_FREE_RELEASE_URL RPMFUSION_FREE_RELEASE_MAX_BYTES \
|
FEDORA_RELEASE RPMFUSION_FREE_RELEASE_URL RPMFUSION_FREE_RELEASE_MAX_BYTES \
|
||||||
RPMFUSION_NONFREE_RELEASE_URL RPMFUSION_NONFREE_RELEASE_MAX_BYTES TERRA_BASEURL \
|
RPMFUSION_NONFREE_RELEASE_URL RPMFUSION_NONFREE_RELEASE_MAX_BYTES TERRA_BASEURL \
|
||||||
|
|||||||
@@ -64,6 +64,14 @@ Artifact digests were checked with `sha256sum ARTIFACT` against the lowercase
|
|||||||
64-hex values in `installers.conf`; all runtime downloads also enforce the
|
64-hex values in `installers.conf`; all runtime downloads also enforce the
|
||||||
recorded byte limit before atomic replacement.
|
recorded byte limit before atomic replacement.
|
||||||
|
|
||||||
|
The `*_BINARY_SHA256` values are the SHA-256 digests of the exact executable
|
||||||
|
bytes selected from each verified runtime archive. They were derived without
|
||||||
|
executing the artifacts: `tar -xO` selected Node's `bin/node` and Codex's
|
||||||
|
`bin/codex`, while `unzip -p` selected Bun's `bun`. Runtime version directories
|
||||||
|
record both the reviewed archive digest and this reviewed installed-byte digest
|
||||||
|
in `.panama-provenance`; later runs require the receipt and re-hash the installed
|
||||||
|
binary before treating an existing directory as trusted.
|
||||||
|
|
||||||
## Rotation policy
|
## Rotation policy
|
||||||
|
|
||||||
Do not replace a key on an automated update. A key rotation is a reviewed
|
Do not replace a key on an automated update. A key rotation is a reviewed
|
||||||
|
|||||||
@@ -1,23 +1,29 @@
|
|||||||
BUN_VERSION=1.4.0
|
BUN_VERSION=1.4.0
|
||||||
BUN_X86_64_URL=https://github.com/oven-sh/bun/releases/download/bun-v1.4.0/bun-linux-x64.zip
|
BUN_X86_64_URL=https://github.com/oven-sh/bun/releases/download/bun-v1.4.0/bun-linux-x64.zip
|
||||||
BUN_X86_64_SHA256=2d03fb5fb83ac8b567aca0a281b2ce1a1a19d488f56c2968d88c3f25e92fe452
|
BUN_X86_64_SHA256=2d03fb5fb83ac8b567aca0a281b2ce1a1a19d488f56c2968d88c3f25e92fe452
|
||||||
|
BUN_X86_64_BINARY_SHA256=33d56b070be6a9e3da0ab013038b43d1645d0534ca811ecdba4472599117eb4b
|
||||||
BUN_X86_64_MAX_BYTES=67108864
|
BUN_X86_64_MAX_BYTES=67108864
|
||||||
BUN_AARCH64_URL=https://github.com/oven-sh/bun/releases/download/bun-v1.4.0/bun-linux-aarch64.zip
|
BUN_AARCH64_URL=https://github.com/oven-sh/bun/releases/download/bun-v1.4.0/bun-linux-aarch64.zip
|
||||||
BUN_AARCH64_SHA256=4b1a332ee861983eb93bcfe6f770fff94e3e31b2c388bdaea3c8ed35e58eed0e
|
BUN_AARCH64_SHA256=4b1a332ee861983eb93bcfe6f770fff94e3e31b2c388bdaea3c8ed35e58eed0e
|
||||||
|
BUN_AARCH64_BINARY_SHA256=086c4121c8738a8e0f5ed730e8a461bc3973b4444e372ddb77aef9a747fa2ae9
|
||||||
BUN_AARCH64_MAX_BYTES=67108864
|
BUN_AARCH64_MAX_BYTES=67108864
|
||||||
NODE_VERSION=24.20.0
|
NODE_VERSION=24.20.0
|
||||||
NODE_X86_64_URL=https://nodejs.org/dist/v24.20.0/node-v24.20.0-linux-x64.tar.xz
|
NODE_X86_64_URL=https://nodejs.org/dist/v24.20.0/node-v24.20.0-linux-x64.tar.xz
|
||||||
NODE_X86_64_SHA256=2f2c0da162318f0de47665410c7c8c2ed3d36c8f3105de4bbc61176c70a7cbf2
|
NODE_X86_64_SHA256=2f2c0da162318f0de47665410c7c8c2ed3d36c8f3105de4bbc61176c70a7cbf2
|
||||||
|
NODE_X86_64_BINARY_SHA256=89af8424dd53e560b1933f87ba650d8bf57c83ca5a04600eefb31f416aabbae7
|
||||||
NODE_X86_64_MAX_BYTES=67108864
|
NODE_X86_64_MAX_BYTES=67108864
|
||||||
NODE_AARCH64_URL=https://nodejs.org/dist/v24.20.0/node-v24.20.0-linux-arm64.tar.xz
|
NODE_AARCH64_URL=https://nodejs.org/dist/v24.20.0/node-v24.20.0-linux-arm64.tar.xz
|
||||||
NODE_AARCH64_SHA256=5f4ddab610c1ab2016b3c227cebdbf6d9495161487e4739c7b90090595f465f7
|
NODE_AARCH64_SHA256=5f4ddab610c1ab2016b3c227cebdbf6d9495161487e4739c7b90090595f465f7
|
||||||
|
NODE_AARCH64_BINARY_SHA256=23a5637c2470fde09fcc1acc77c1b92e04e3d7e3e6e80ff7df6f5831958d1477
|
||||||
NODE_AARCH64_MAX_BYTES=67108864
|
NODE_AARCH64_MAX_BYTES=67108864
|
||||||
CODEX_VERSION=0.150.1
|
CODEX_VERSION=0.150.1
|
||||||
CODEX_X86_64_URL=https://github.com/openai/codex/releases/download/rust-v0.150.1/codex-package-x86_64-unknown-linux-musl.tar.gz
|
CODEX_X86_64_URL=https://github.com/openai/codex/releases/download/rust-v0.150.1/codex-package-x86_64-unknown-linux-musl.tar.gz
|
||||||
CODEX_X86_64_SHA256=00aba704f029f6dc0d948be407a756e0c97cc840132fd691353b2c6b0a505b17
|
CODEX_X86_64_SHA256=00aba704f029f6dc0d948be407a756e0c97cc840132fd691353b2c6b0a505b17
|
||||||
|
CODEX_X86_64_BINARY_SHA256=abf1bb1643a79f73aa78ee627e111e02d4f8c98f25813a0cf6ce277709664386
|
||||||
CODEX_X86_64_MAX_BYTES=134217728
|
CODEX_X86_64_MAX_BYTES=134217728
|
||||||
CODEX_AARCH64_URL=https://github.com/openai/codex/releases/download/rust-v0.150.1/codex-package-aarch64-unknown-linux-musl.tar.gz
|
CODEX_AARCH64_URL=https://github.com/openai/codex/releases/download/rust-v0.150.1/codex-package-aarch64-unknown-linux-musl.tar.gz
|
||||||
CODEX_AARCH64_SHA256=1ecac3f87823efb98153233b076ea3d6e34a7a8cebe43c5285dc5f79e1514639
|
CODEX_AARCH64_SHA256=1ecac3f87823efb98153233b076ea3d6e34a7a8cebe43c5285dc5f79e1514639
|
||||||
|
CODEX_AARCH64_BINARY_SHA256=7a49aabe11fd95a1c968d79e16b5f1b17c3219002c5f7129d467f415f8460feb
|
||||||
CODEX_AARCH64_MAX_BYTES=134217728
|
CODEX_AARCH64_MAX_BYTES=134217728
|
||||||
RUSTDESK_VERSION=1.4.9
|
RUSTDESK_VERSION=1.4.9
|
||||||
RUSTDESK_X86_64_URL=https://github.com/rustdesk/rustdesk/releases/download/1.4.9/rustdesk-1.4.9-0.x86_64.rpm
|
RUSTDESK_X86_64_URL=https://github.com/rustdesk/rustdesk/releases/download/1.4.9/rustdesk-1.4.9-0.x86_64.rpm
|
||||||
|
|||||||
+163
-44
@@ -126,12 +126,17 @@ _set_artifact_arch() {
|
|||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_archive_path_is_safe() {
|
||||||
|
local member="$1"
|
||||||
|
[[ -n "$member" && "$member" != /* && "$member" != *'//'*
|
||||||
|
&& ! "$member" =~ (^|/)\.\.?(/|$) ]]
|
||||||
|
}
|
||||||
|
|
||||||
_archive_member_is_safe() {
|
_archive_member_is_safe() {
|
||||||
local member="$1" expected_top="$2"
|
local member="$1" expected_top="$2"
|
||||||
[[ -n "$member" && "$member" != /* && "$member" != *'//'*
|
_archive_path_is_safe "$member"
|
||||||
&& ! "$member" =~ (^|/)\.\.?(/|$)
|
[[ "$member" == "$expected_top" || "$member" == "$expected_top/" \
|
||||||
&& ( "$member" == "$expected_top" || "$member" == "$expected_top/" \
|
|| "$member" == "$expected_top/"* ]]
|
||||||
|| "$member" == "$expected_top/"* ) ]]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_tree_links_stay_inside() {
|
_tree_links_stay_inside() {
|
||||||
@@ -142,17 +147,59 @@ _tree_links_stay_inside() {
|
|||||||
done < <(find "$root" -type l -print0)
|
done < <(find "$root" -type l -print0)
|
||||||
}
|
}
|
||||||
|
|
||||||
_atomic_symlink() {
|
_tree_hardlinks_stay_inside() {
|
||||||
local target="$1" destination="$2" directory temporary
|
local root="$1" device inode link_count key
|
||||||
|
local -A names_in_tree=() inode_links=()
|
||||||
|
while read -r device inode link_count; do
|
||||||
|
key="$device:$inode"
|
||||||
|
names_in_tree["$key"]=$(( ${names_in_tree[$key]:-0} + 1 ))
|
||||||
|
inode_links["$key"]="$link_count"
|
||||||
|
done < <(find "$root" -type f -printf '%D %i %n\n')
|
||||||
|
for key in "${!names_in_tree[@]}"; do
|
||||||
|
[[ "${names_in_tree[$key]}" == "${inode_links[$key]}" ]] || return 1
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
_atomic_symlink() (
|
||||||
|
local target="$1" destination="$2" directory temporary=""
|
||||||
|
trap '[[ -z "$temporary" ]] || rm -f -- "$temporary"' EXIT
|
||||||
|
trap 'exit 130' INT
|
||||||
|
trap 'exit 143' TERM
|
||||||
directory="$(dirname -- "$destination")"
|
directory="$(dirname -- "$destination")"
|
||||||
mkdir -p -- "$directory" || return 1
|
mkdir -p -- "$directory" || return 1
|
||||||
temporary="$(mktemp "$directory/.$(basename -- "$destination").link.XXXXXX")" || return 1
|
temporary="$(mktemp "$directory/.$(basename -- "$destination").link.XXXXXX")" || return 1
|
||||||
rm -f -- "$temporary" || return 1
|
rm -f -- "$temporary" || return 1
|
||||||
ln -s -- "$target" "$temporary" || return 1
|
ln -s -- "$target" "$temporary" || return 1
|
||||||
if ! mv -Tf -- "$temporary" "$destination"; then
|
if ! mv -Tf -- "$temporary" "$destination"; then
|
||||||
rm -f -- "$temporary"
|
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
)
|
||||||
|
|
||||||
|
_activate_directory_no_replace() {
|
||||||
|
local staged="$1" destination="$2"
|
||||||
|
mv -Tn -- "$staged" "$destination" || return 1
|
||||||
|
[[ ! -e "$staged" && ! -L "$staged" && -d "$destination" && ! -L "$destination" ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
_write_runtime_receipt() {
|
||||||
|
local directory="$1" artifact_digest="$2" binary_digest="$3"
|
||||||
|
local receipt="$directory/.panama-provenance"
|
||||||
|
[[ -d "$directory" && ! -L "$directory" && ! -e "$receipt" && ! -L "$receipt" ]] \
|
||||||
|
|| return 1
|
||||||
|
( umask 077 && printf 'schema=1\nartifact_sha256=%s\nbinary_sha256=%s\n' \
|
||||||
|
"$artifact_digest" "$binary_digest" > "$receipt" )
|
||||||
|
}
|
||||||
|
|
||||||
|
_runtime_receipt_matches() {
|
||||||
|
local directory="$1" binary="$2" artifact_digest="$3" binary_digest="$4"
|
||||||
|
local receipt="$directory/.panama-provenance" actual
|
||||||
|
[[ -d "$directory" && ! -L "$directory"
|
||||||
|
&& -f "$receipt" && ! -L "$receipt"
|
||||||
|
&& -f "$binary" && ! -L "$binary" ]] || return 1
|
||||||
|
cmp -s "$receipt" <(printf 'schema=1\nartifact_sha256=%s\nbinary_sha256=%s\n' \
|
||||||
|
"$artifact_digest" "$binary_digest") || return 1
|
||||||
|
actual="$(sha256sum "$binary" | awk '{ print $1 }')" || return 1
|
||||||
|
[[ "$actual" == "$binary_digest" ]]
|
||||||
}
|
}
|
||||||
|
|
||||||
_load_nvm() {
|
_load_nvm() {
|
||||||
@@ -165,8 +212,13 @@ _load_nvm() {
|
|||||||
declare -F nvm >/dev/null
|
declare -F nvm >/dev/null
|
||||||
}
|
}
|
||||||
|
|
||||||
_install_node() {
|
_install_node() (
|
||||||
local artifact_arch machine_arch archive_top parent target stage archive extract listing member
|
local artifact_arch machine_arch archive_top parent target stage archive extract listing member
|
||||||
|
local artifact_digest binary_digest staged_binary
|
||||||
|
stage=""
|
||||||
|
trap '[[ -z "$stage" ]] || rm -rf -- "$stage"' EXIT
|
||||||
|
trap 'exit 130' INT
|
||||||
|
trap 'exit 143' TERM
|
||||||
_set_artifact_arch || return 1
|
_set_artifact_arch || return 1
|
||||||
_load_nvm || return 1
|
_load_nvm || return 1
|
||||||
case "$artifact_arch" in
|
case "$artifact_arch" in
|
||||||
@@ -176,8 +228,12 @@ _install_node() {
|
|||||||
archive_top="node-v${INSTALLER_PROVENANCE[NODE_VERSION]}-linux-$machine_arch"
|
archive_top="node-v${INSTALLER_PROVENANCE[NODE_VERSION]}-linux-$machine_arch"
|
||||||
parent="${NVM_DIR:-$HOME/.nvm}/versions/node"
|
parent="${NVM_DIR:-$HOME/.nvm}/versions/node"
|
||||||
target="$parent/v${INSTALLER_PROVENANCE[NODE_VERSION]}"
|
target="$parent/v${INSTALLER_PROVENANCE[NODE_VERSION]}"
|
||||||
|
artifact_digest="${INSTALLER_PROVENANCE[NODE_${artifact_arch}_SHA256]}"
|
||||||
|
binary_digest="${INSTALLER_PROVENANCE[NODE_${artifact_arch}_BINARY_SHA256]}"
|
||||||
if [[ -e "$target" || -L "$target" ]]; then
|
if [[ -e "$target" || -L "$target" ]]; then
|
||||||
[[ -d "$target" && ! -L "$target" && -x "$target/bin/node"
|
_runtime_receipt_matches "$target" "$target/bin/node" \
|
||||||
|
"$artifact_digest" "$binary_digest" || return 1
|
||||||
|
[[ -x "$target/bin/node"
|
||||||
&& "$($target/bin/node --version 2>/dev/null)" == "v${INSTALLER_PROVENANCE[NODE_VERSION]}" ]] \
|
&& "$($target/bin/node --version 2>/dev/null)" == "v${INSTALLER_PROVENANCE[NODE_VERSION]}" ]] \
|
||||||
|| return 1
|
|| return 1
|
||||||
nvm alias default "${INSTALLER_PROVENANCE[NODE_VERSION]}" >/dev/null 2>&1 || return 1
|
nvm alias default "${INSTALLER_PROVENANCE[NODE_VERSION]}" >/dev/null 2>&1 || return 1
|
||||||
@@ -204,17 +260,34 @@ _install_node() {
|
|||||||
done <<<"$listing"
|
done <<<"$listing"
|
||||||
tar -xJf "$archive" --no-same-owner --no-same-permissions -C "$extract" \
|
tar -xJf "$archive" --no-same-owner --no-same-permissions -C "$extract" \
|
||||||
|| { rm -rf -- "$stage"; return 1; }
|
|| { rm -rf -- "$stage"; return 1; }
|
||||||
[[ -d "$extract/$archive_top" && ! -L "$extract/$archive_top"
|
|
||||||
&& -x "$extract/$archive_top/bin/node"
|
|
||||||
&& "$($extract/$archive_top/bin/node --version 2>/dev/null)" \
|
|
||||||
== "v${INSTALLER_PROVENANCE[NODE_VERSION]}" ]] \
|
|
||||||
|| { rm -rf -- "$stage"; return 1; }
|
|
||||||
_tree_links_stay_inside "$extract/$archive_top" \
|
_tree_links_stay_inside "$extract/$archive_top" \
|
||||||
|| { rm -rf -- "$stage"; return 1; }
|
|| { rm -rf -- "$stage"; return 1; }
|
||||||
mv -- "$extract/$archive_top" "$target" || { rm -rf -- "$stage"; return 1; }
|
_tree_hardlinks_stay_inside "$extract/$archive_top" \
|
||||||
|
|| { rm -rf -- "$stage"; return 1; }
|
||||||
|
staged_binary="$extract/$archive_top/bin/node"
|
||||||
|
[[ "$(sha256sum "$staged_binary" | awk '{ print $1 }')" == "$binary_digest" ]] \
|
||||||
|
|| { rm -rf -- "$stage"; return 1; }
|
||||||
|
[[ -d "$extract/$archive_top" && ! -L "$extract/$archive_top"
|
||||||
|
&& -x "$staged_binary"
|
||||||
|
&& "$($staged_binary --version 2>/dev/null)" \
|
||||||
|
== "v${INSTALLER_PROVENANCE[NODE_VERSION]}" ]] \
|
||||||
|
|| { rm -rf -- "$stage"; return 1; }
|
||||||
|
_write_runtime_receipt "$extract/$archive_top" "$artifact_digest" "$binary_digest" \
|
||||||
|
|| { rm -rf -- "$stage"; return 1; }
|
||||||
|
_activate_directory_no_replace "$extract/$archive_top" "$target" \
|
||||||
|
|| { rm -rf -- "$stage"; return 1; }
|
||||||
|
_runtime_receipt_matches "$target" "$target/bin/node" \
|
||||||
|
"$artifact_digest" "$binary_digest" || { rm -rf -- "$stage"; return 1; }
|
||||||
|
[[ -x "$target/bin/node"
|
||||||
|
&& "$($target/bin/node --version 2>/dev/null)" \
|
||||||
|
== "v${INSTALLER_PROVENANCE[NODE_VERSION]}" ]] \
|
||||||
|
|| { rm -rf -- "$stage"; return 1; }
|
||||||
|
_tree_links_stay_inside "$target" || { rm -rf -- "$stage"; return 1; }
|
||||||
|
_tree_hardlinks_stay_inside "$target" || { rm -rf -- "$stage"; return 1; }
|
||||||
rm -rf -- "$stage"
|
rm -rf -- "$stage"
|
||||||
|
stage=""
|
||||||
nvm alias default "${INSTALLER_PROVENANCE[NODE_VERSION]}" >/dev/null 2>&1
|
nvm alias default "${INSTALLER_PROVENANCE[NODE_VERSION]}" >/dev/null 2>&1
|
||||||
}
|
)
|
||||||
|
|
||||||
install_node() {
|
install_node() {
|
||||||
_install_node || _record_installer_failure Node
|
_install_node || _record_installer_failure Node
|
||||||
@@ -226,15 +299,20 @@ setup_node() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
install_pnpm() {
|
install_pnpm() {
|
||||||
if sudo dnf install -y pnpm >/dev/null; then
|
if require_reviewed_fedora_release \
|
||||||
|
&& sudo dnf install -y --repo=fedora --repo=updates pnpm >/dev/null; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
_record_installer_failure pnpm
|
_record_installer_failure pnpm
|
||||||
}
|
}
|
||||||
|
|
||||||
_install_bun() {
|
_install_bun() (
|
||||||
local artifact_arch archive_top target bin_link parent stage archive listing
|
local artifact_arch archive_top target bin_link parent stage archive listing
|
||||||
local staged_binary version_dir
|
local staged_binary version_dir artifact_digest binary_digest
|
||||||
|
stage=""
|
||||||
|
trap '[[ -z "$stage" ]] || rm -rf -- "$stage"' EXIT
|
||||||
|
trap 'exit 130' INT
|
||||||
|
trap 'exit 143' TERM
|
||||||
_set_artifact_arch || return 1
|
_set_artifact_arch || return 1
|
||||||
case "$artifact_arch" in
|
case "$artifact_arch" in
|
||||||
X86_64) archive_top=bun-linux-x64 ;;
|
X86_64) archive_top=bun-linux-x64 ;;
|
||||||
@@ -243,8 +321,12 @@ _install_bun() {
|
|||||||
version_dir="$HOME/.bun/versions/${INSTALLER_PROVENANCE[BUN_VERSION]}"
|
version_dir="$HOME/.bun/versions/${INSTALLER_PROVENANCE[BUN_VERSION]}"
|
||||||
target="$version_dir/bin/bun"
|
target="$version_dir/bin/bun"
|
||||||
bin_link="$HOME/.bun/bin/bun"
|
bin_link="$HOME/.bun/bin/bun"
|
||||||
|
artifact_digest="${INSTALLER_PROVENANCE[BUN_${artifact_arch}_SHA256]}"
|
||||||
|
binary_digest="${INSTALLER_PROVENANCE[BUN_${artifact_arch}_BINARY_SHA256]}"
|
||||||
if [[ -e "$version_dir" || -L "$version_dir" ]]; then
|
if [[ -e "$version_dir" || -L "$version_dir" ]]; then
|
||||||
[[ -d "$version_dir" && ! -L "$version_dir" && -x "$target"
|
_runtime_receipt_matches "$version_dir" "$target" \
|
||||||
|
"$artifact_digest" "$binary_digest" || return 1
|
||||||
|
[[ -x "$target"
|
||||||
&& "$($target --version 2>/dev/null)" == "${INSTALLER_PROVENANCE[BUN_VERSION]}" ]] \
|
&& "$($target --version 2>/dev/null)" == "${INSTALLER_PROVENANCE[BUN_VERSION]}" ]] \
|
||||||
|| return 1
|
|| return 1
|
||||||
[[ -L "$bin_link" && "$(readlink -- "$bin_link")" == "$target" ]] \
|
[[ -L "$bin_link" && "$(readlink -- "$bin_link")" == "$target" ]] \
|
||||||
@@ -264,22 +346,35 @@ _install_bun() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
listing="$(unzip -Z1 "$archive")" || { rm -rf -- "$stage"; return 1; }
|
listing="$(unzip -Z1 "$archive")" || { rm -rf -- "$stage"; return 1; }
|
||||||
[[ "$listing" == "$archive_top/bun" ]] || { rm -rf -- "$stage"; return 1; }
|
[[ "$listing" == "$archive_top/"$'\n'"$archive_top/bun" ]] \
|
||||||
_archive_member_is_safe "$listing" "$archive_top" \
|
|
||||||
|| { rm -rf -- "$stage"; return 1; }
|
|| { rm -rf -- "$stage"; return 1; }
|
||||||
|
while IFS= read -r member; do
|
||||||
|
_archive_member_is_safe "$member" "$archive_top" \
|
||||||
|
|| { rm -rf -- "$stage"; return 1; }
|
||||||
|
done <<<"$listing"
|
||||||
mkdir -m 0700 "$stage/extract" "$stage/version" "$stage/version/bin" \
|
mkdir -m 0700 "$stage/extract" "$stage/version" "$stage/version/bin" \
|
||||||
|| { rm -rf -- "$stage"; return 1; }
|
|| { rm -rf -- "$stage"; return 1; }
|
||||||
unzip -q "$archive" -d "$stage/extract" || { rm -rf -- "$stage"; return 1; }
|
unzip -q "$archive" -d "$stage/extract" || { rm -rf -- "$stage"; return 1; }
|
||||||
staged_binary="$stage/extract/$archive_top/bun"
|
staged_binary="$stage/extract/$archive_top/bun"
|
||||||
[[ -f "$staged_binary" && ! -L "$staged_binary" && -x "$staged_binary"
|
[[ -f "$staged_binary" && ! -L "$staged_binary" && -x "$staged_binary"
|
||||||
|
&& "$(sha256sum "$staged_binary" | awk '{ print $1 }')" == "$binary_digest"
|
||||||
&& "$($staged_binary --version 2>/dev/null)" == "${INSTALLER_PROVENANCE[BUN_VERSION]}" ]] \
|
&& "$($staged_binary --version 2>/dev/null)" == "${INSTALLER_PROVENANCE[BUN_VERSION]}" ]] \
|
||||||
|| { rm -rf -- "$stage"; return 1; }
|
|| { rm -rf -- "$stage"; return 1; }
|
||||||
mv -- "$staged_binary" "$stage/version/bin/bun" \
|
mv -- "$staged_binary" "$stage/version/bin/bun" \
|
||||||
|| { rm -rf -- "$stage"; return 1; }
|
|| { rm -rf -- "$stage"; return 1; }
|
||||||
mv -- "$stage/version" "$version_dir" || { rm -rf -- "$stage"; return 1; }
|
_write_runtime_receipt "$stage/version" "$artifact_digest" "$binary_digest" \
|
||||||
|
|| { rm -rf -- "$stage"; return 1; }
|
||||||
|
_activate_directory_no_replace "$stage/version" "$version_dir" \
|
||||||
|
|| { rm -rf -- "$stage"; return 1; }
|
||||||
|
_runtime_receipt_matches "$version_dir" "$target" \
|
||||||
|
"$artifact_digest" "$binary_digest" || { rm -rf -- "$stage"; return 1; }
|
||||||
|
[[ -x "$target" && "$($target --version 2>/dev/null)" \
|
||||||
|
== "${INSTALLER_PROVENANCE[BUN_VERSION]}" ]] \
|
||||||
|
|| { rm -rf -- "$stage"; return 1; }
|
||||||
rm -rf -- "$stage"
|
rm -rf -- "$stage"
|
||||||
|
stage=""
|
||||||
_atomic_symlink "$target" "$bin_link"
|
_atomic_symlink "$target" "$bin_link"
|
||||||
}
|
)
|
||||||
|
|
||||||
install_bun() {
|
install_bun() {
|
||||||
_install_bun || _record_installer_failure Bun
|
_install_bun || _record_installer_failure Bun
|
||||||
@@ -292,20 +387,23 @@ _codex_version_matches() {
|
|||||||
[[ "$output" =~ (^|[^0-9])${version_pattern}([^0-9]|$) ]]
|
[[ "$output" =~ (^|[^0-9])${version_pattern}([^0-9]|$) ]]
|
||||||
}
|
}
|
||||||
|
|
||||||
_install_codex() {
|
_install_codex() (
|
||||||
local artifact_arch machine_arch archive_name version_dir target bin_link parent
|
local artifact_arch version_dir target bin_link parent expected_listing member
|
||||||
local stage archive listing staged_binary
|
local stage archive listing staged_binary artifact_digest binary_digest
|
||||||
|
stage=""
|
||||||
|
trap '[[ -z "$stage" ]] || rm -rf -- "$stage"' EXIT
|
||||||
|
trap 'exit 130' INT
|
||||||
|
trap 'exit 143' TERM
|
||||||
_set_artifact_arch || return 1
|
_set_artifact_arch || return 1
|
||||||
case "$artifact_arch" in
|
|
||||||
X86_64) machine_arch=x86_64 ;;
|
|
||||||
AARCH64) machine_arch=aarch64 ;;
|
|
||||||
esac
|
|
||||||
archive_name="codex-$machine_arch-unknown-linux-musl"
|
|
||||||
version_dir="$HOME/.local/lib/panama/codex/${INSTALLER_PROVENANCE[CODEX_VERSION]}"
|
version_dir="$HOME/.local/lib/panama/codex/${INSTALLER_PROVENANCE[CODEX_VERSION]}"
|
||||||
target="$version_dir/codex"
|
target="$version_dir/codex"
|
||||||
bin_link="$HOME/.local/bin/codex"
|
bin_link="$HOME/.local/bin/codex"
|
||||||
|
artifact_digest="${INSTALLER_PROVENANCE[CODEX_${artifact_arch}_SHA256]}"
|
||||||
|
binary_digest="${INSTALLER_PROVENANCE[CODEX_${artifact_arch}_BINARY_SHA256]}"
|
||||||
if [[ -e "$version_dir" || -L "$version_dir" ]]; then
|
if [[ -e "$version_dir" || -L "$version_dir" ]]; then
|
||||||
[[ -d "$version_dir" && ! -L "$version_dir" && -x "$target" ]] || return 1
|
_runtime_receipt_matches "$version_dir" "$target" \
|
||||||
|
"$artifact_digest" "$binary_digest" || return 1
|
||||||
|
[[ -x "$target" ]] || return 1
|
||||||
_codex_version_matches "$target" || return 1
|
_codex_version_matches "$target" || return 1
|
||||||
[[ -L "$bin_link" && "$(readlink -- "$bin_link")" == "$target" ]] \
|
[[ -L "$bin_link" && "$(readlink -- "$bin_link")" == "$target" ]] \
|
||||||
|| _atomic_symlink "$target" "$bin_link"
|
|| _atomic_symlink "$target" "$bin_link"
|
||||||
@@ -324,31 +422,47 @@ _install_codex() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
listing="$(tar -tzf "$archive")" || { rm -rf -- "$stage"; return 1; }
|
listing="$(tar -tzf "$archive")" || { rm -rf -- "$stage"; return 1; }
|
||||||
[[ "$listing" == "$archive_name" ]] || { rm -rf -- "$stage"; return 1; }
|
expected_listing=$'bin/\nbin/codex\nbin/codex-code-mode-host\ncodex-package.json\ncodex-path/\ncodex-path/rg\ncodex-resources/\ncodex-resources/bwrap\ncodex-resources/zsh/\ncodex-resources/zsh/bin/\ncodex-resources/zsh/bin/zsh'
|
||||||
[[ "$listing" != /* && ! "$listing" =~ (^|/)\.\.?(/|$) ]] \
|
[[ "$listing" == "$expected_listing" ]] || { rm -rf -- "$stage"; return 1; }
|
||||||
|| { rm -rf -- "$stage"; return 1; }
|
while IFS= read -r member; do
|
||||||
|
_archive_path_is_safe "$member" || { rm -rf -- "$stage"; return 1; }
|
||||||
|
done <<<"$listing"
|
||||||
mkdir -m 0700 "$stage/extract" "$stage/version" \
|
mkdir -m 0700 "$stage/extract" "$stage/version" \
|
||||||
|| { rm -rf -- "$stage"; return 1; }
|
|| { rm -rf -- "$stage"; return 1; }
|
||||||
tar -xzf "$archive" --no-same-owner --no-same-permissions -C "$stage/extract" \
|
tar -xzf "$archive" --no-same-owner --no-same-permissions -C "$stage/extract" \
|
||||||
|| { rm -rf -- "$stage"; return 1; }
|
|| { rm -rf -- "$stage"; return 1; }
|
||||||
staged_binary="$stage/extract/$archive_name"
|
_tree_links_stay_inside "$stage/extract" || { rm -rf -- "$stage"; return 1; }
|
||||||
[[ -f "$staged_binary" && ! -L "$staged_binary" && -x "$staged_binary" ]] \
|
_tree_hardlinks_stay_inside "$stage/extract" || { rm -rf -- "$stage"; return 1; }
|
||||||
|
staged_binary="$stage/extract/bin/codex"
|
||||||
|
[[ -f "$staged_binary" && ! -L "$staged_binary" && -x "$staged_binary"
|
||||||
|
&& "$(sha256sum "$staged_binary" | awk '{ print $1 }')" == "$binary_digest" ]] \
|
||||||
|| { rm -rf -- "$stage"; return 1; }
|
|| { rm -rf -- "$stage"; return 1; }
|
||||||
_codex_version_matches "$staged_binary" \
|
_codex_version_matches "$staged_binary" \
|
||||||
|| { rm -rf -- "$stage"; return 1; }
|
|| { rm -rf -- "$stage"; return 1; }
|
||||||
mv -- "$staged_binary" "$stage/version/codex" \
|
mv -- "$staged_binary" "$stage/version/codex" \
|
||||||
|| { rm -rf -- "$stage"; return 1; }
|
|| { rm -rf -- "$stage"; return 1; }
|
||||||
mv -- "$stage/version" "$version_dir" || { rm -rf -- "$stage"; return 1; }
|
_write_runtime_receipt "$stage/version" "$artifact_digest" "$binary_digest" \
|
||||||
|
|| { rm -rf -- "$stage"; return 1; }
|
||||||
|
_activate_directory_no_replace "$stage/version" "$version_dir" \
|
||||||
|
|| { rm -rf -- "$stage"; return 1; }
|
||||||
|
_runtime_receipt_matches "$version_dir" "$target" \
|
||||||
|
"$artifact_digest" "$binary_digest" || { rm -rf -- "$stage"; return 1; }
|
||||||
|
_codex_version_matches "$target" || { rm -rf -- "$stage"; return 1; }
|
||||||
rm -rf -- "$stage"
|
rm -rf -- "$stage"
|
||||||
|
stage=""
|
||||||
_atomic_symlink "$target" "$bin_link"
|
_atomic_symlink "$target" "$bin_link"
|
||||||
}
|
)
|
||||||
|
|
||||||
install_codex() {
|
install_codex() {
|
||||||
_install_codex || _record_installer_failure Codex
|
_install_codex || _record_installer_failure Codex
|
||||||
}
|
}
|
||||||
|
|
||||||
_install_rustdesk() {
|
_install_rustdesk() (
|
||||||
local artifact_arch installed_version="" work rpm_path status=0
|
local artifact_arch installed_version="" work rpm_path status=0
|
||||||
|
work=""
|
||||||
|
trap '[[ -z "$work" ]] || rm -rf -- "$work"' EXIT
|
||||||
|
trap 'exit 130' INT
|
||||||
|
trap 'exit 143' TERM
|
||||||
_set_artifact_arch || return 1
|
_set_artifact_arch || return 1
|
||||||
if [[ "$artifact_arch" == AARCH64 ]]; then
|
if [[ "$artifact_arch" == AARCH64 ]]; then
|
||||||
log "RustDesk ${INSTALLER_PROVENANCE[RUSTDESK_VERSION]} has no reviewed aarch64 RPM"
|
log "RustDesk ${INSTALLER_PROVENANCE[RUSTDESK_VERSION]} has no reviewed aarch64 RPM"
|
||||||
@@ -367,10 +481,14 @@ _install_rustdesk() {
|
|||||||
rm -rf -- "$work"
|
rm -rf -- "$work"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
sudo dnf install -y --setopt=localpkg_gpgcheck=1 "$rpm_path" >/dev/null || status=$?
|
# RustDesk 1.4.9's reviewed RPM is unsigned. Its exact SHA-256 is the trust
|
||||||
|
# assertion; this exception applies only to the verified private local file
|
||||||
|
# and does not change signature policy for any repository.
|
||||||
|
sudo dnf install -y --setopt=localpkg_gpgcheck=0 "$rpm_path" >/dev/null || status=$?
|
||||||
rm -rf -- "$work"
|
rm -rf -- "$work"
|
||||||
|
work=""
|
||||||
return "$status"
|
return "$status"
|
||||||
}
|
)
|
||||||
|
|
||||||
install_rustdesk() {
|
install_rustdesk() {
|
||||||
_install_rustdesk || _record_installer_failure RustDesk
|
_install_rustdesk || _record_installer_failure RustDesk
|
||||||
@@ -945,7 +1063,8 @@ _install_claude_code() {
|
|||||||
"$staged_key" /etc/pki/rpm-gpg/RPM-GPG-KEY-claude-code-panama \
|
"$staged_key" /etc/pki/rpm-gpg/RPM-GPG-KEY-claude-code-panama \
|
||||||
"$staged_repo" /etc/yum.repos.d/claude-code.repo || status=$?
|
"$staged_repo" /etc/yum.repos.d/claude-code.repo || status=$?
|
||||||
if (( status == 0 )); then
|
if (( status == 0 )); then
|
||||||
sudo dnf install -y claude-code || status=$?
|
sudo dnf install -y --repo=claude-code --repo=fedora --repo=updates \
|
||||||
|
claude-code || status=$?
|
||||||
fi
|
fi
|
||||||
rm -rf -- "$work"
|
rm -rf -- "$work"
|
||||||
return "$status"
|
return "$status"
|
||||||
|
|||||||
@@ -253,13 +253,19 @@ expect_success load_installer_provenance "$config"
|
|||||||
for reviewed_value in \
|
for reviewed_value in \
|
||||||
'NODE_VERSION 24.20.0' \
|
'NODE_VERSION 24.20.0' \
|
||||||
'NODE_X86_64_SHA256 2f2c0da162318f0de47665410c7c8c2ed3d36c8f3105de4bbc61176c70a7cbf2' \
|
'NODE_X86_64_SHA256 2f2c0da162318f0de47665410c7c8c2ed3d36c8f3105de4bbc61176c70a7cbf2' \
|
||||||
|
'NODE_X86_64_BINARY_SHA256 89af8424dd53e560b1933f87ba650d8bf57c83ca5a04600eefb31f416aabbae7' \
|
||||||
'NODE_AARCH64_SHA256 5f4ddab610c1ab2016b3c227cebdbf6d9495161487e4739c7b90090595f465f7' \
|
'NODE_AARCH64_SHA256 5f4ddab610c1ab2016b3c227cebdbf6d9495161487e4739c7b90090595f465f7' \
|
||||||
|
'NODE_AARCH64_BINARY_SHA256 23a5637c2470fde09fcc1acc77c1b92e04e3d7e3e6e80ff7df6f5831958d1477' \
|
||||||
'BUN_VERSION 1.4.0' \
|
'BUN_VERSION 1.4.0' \
|
||||||
'BUN_X86_64_SHA256 2d03fb5fb83ac8b567aca0a281b2ce1a1a19d488f56c2968d88c3f25e92fe452' \
|
'BUN_X86_64_SHA256 2d03fb5fb83ac8b567aca0a281b2ce1a1a19d488f56c2968d88c3f25e92fe452' \
|
||||||
|
'BUN_X86_64_BINARY_SHA256 33d56b070be6a9e3da0ab013038b43d1645d0534ca811ecdba4472599117eb4b' \
|
||||||
'BUN_AARCH64_SHA256 4b1a332ee861983eb93bcfe6f770fff94e3e31b2c388bdaea3c8ed35e58eed0e' \
|
'BUN_AARCH64_SHA256 4b1a332ee861983eb93bcfe6f770fff94e3e31b2c388bdaea3c8ed35e58eed0e' \
|
||||||
|
'BUN_AARCH64_BINARY_SHA256 086c4121c8738a8e0f5ed730e8a461bc3973b4444e372ddb77aef9a747fa2ae9' \
|
||||||
'CODEX_VERSION 0.150.1' \
|
'CODEX_VERSION 0.150.1' \
|
||||||
'CODEX_X86_64_SHA256 00aba704f029f6dc0d948be407a756e0c97cc840132fd691353b2c6b0a505b17' \
|
'CODEX_X86_64_SHA256 00aba704f029f6dc0d948be407a756e0c97cc840132fd691353b2c6b0a505b17' \
|
||||||
|
'CODEX_X86_64_BINARY_SHA256 abf1bb1643a79f73aa78ee627e111e02d4f8c98f25813a0cf6ce277709664386' \
|
||||||
'CODEX_AARCH64_SHA256 1ecac3f87823efb98153233b076ea3d6e34a7a8cebe43c5285dc5f79e1514639' \
|
'CODEX_AARCH64_SHA256 1ecac3f87823efb98153233b076ea3d6e34a7a8cebe43c5285dc5f79e1514639' \
|
||||||
|
'CODEX_AARCH64_BINARY_SHA256 7a49aabe11fd95a1c968d79e16b5f1b17c3219002c5f7129d467f415f8460feb' \
|
||||||
'RUSTDESK_VERSION 1.4.9' \
|
'RUSTDESK_VERSION 1.4.9' \
|
||||||
'RUSTDESK_X86_64_SHA256 eb1b053ac5b2f774f2271f7fbbfd2ea475899f7a55135c5e172bc54b9388f108'; do
|
'RUSTDESK_X86_64_SHA256 eb1b053ac5b2f774f2271f7fbbfd2ea475899f7a55135c5e172bc54b9388f108'; do
|
||||||
read -r name expected <<<"$reviewed_value"
|
read -r name expected <<<"$reviewed_value"
|
||||||
@@ -332,26 +338,46 @@ for arch_spec in \
|
|||||||
read -r machine node_arch bun_arch <<<"$arch_spec"
|
read -r machine node_arch bun_arch <<<"$arch_spec"
|
||||||
node_top="node-v24.20.0-linux-$node_arch"
|
node_top="node-v24.20.0-linux-$node_arch"
|
||||||
mkdir -p "$artifact_root/build/$node_top/bin"
|
mkdir -p "$artifact_root/build/$node_top/bin"
|
||||||
printf '#!/usr/bin/env bash\nprintf "v24.20.0\\n"\n' \
|
printf '#!/usr/bin/env bash\n# fixture %s\nprintf "v24.20.0\\n"\n' "$machine" \
|
||||||
> "$artifact_root/build/$node_top/bin/node"
|
> "$artifact_root/build/$node_top/bin/node"
|
||||||
chmod +x "$artifact_root/build/$node_top/bin/node"
|
chmod +x "$artifact_root/build/$node_top/bin/node"
|
||||||
|
cp "$artifact_root/build/$node_top/bin/node" "$artifact_root/node-$machine.bin"
|
||||||
tar -C "$artifact_root/build" -cJf "$artifact_root/node-$machine.tar.xz" "$node_top"
|
tar -C "$artifact_root/build" -cJf "$artifact_root/node-$machine.tar.xz" "$node_top"
|
||||||
rm -rf -- "$artifact_root/build/$node_top"
|
rm -rf -- "$artifact_root/build/$node_top"
|
||||||
|
|
||||||
bun_top="bun-linux-$bun_arch"
|
bun_top="bun-linux-$bun_arch"
|
||||||
mkdir -p "$artifact_root/build/$bun_top"
|
mkdir -p "$artifact_root/build/$bun_top"
|
||||||
printf '#!/usr/bin/env bash\nprintf "1.4.0\\n"\n' \
|
printf '#!/usr/bin/env bash\n# fixture %s\nprintf "1.4.0\\n"\n' "$machine" \
|
||||||
> "$artifact_root/build/$bun_top/bun"
|
> "$artifact_root/build/$bun_top/bun"
|
||||||
chmod +x "$artifact_root/build/$bun_top/bun"
|
chmod +x "$artifact_root/build/$bun_top/bun"
|
||||||
(cd "$artifact_root/build" && zip -q "$artifact_root/bun-$machine.zip" "$bun_top/bun")
|
cp "$artifact_root/build/$bun_top/bun" "$artifact_root/bun-$machine.bin"
|
||||||
|
(cd "$artifact_root/build" && zip -q "$artifact_root/bun-$machine.zip" \
|
||||||
|
"$bun_top/" "$bun_top/bun")
|
||||||
rm -rf -- "$artifact_root/build/$bun_top"
|
rm -rf -- "$artifact_root/build/$bun_top"
|
||||||
|
|
||||||
codex_name="codex-$machine-unknown-linux-musl"
|
mkdir -p "$artifact_root/build/bin" "$artifact_root/build/codex-path" \
|
||||||
printf '#!/usr/bin/env bash\nprintf "codex-cli 0.150.1\\n"\n' \
|
"$artifact_root/build/codex-resources/zsh/bin"
|
||||||
> "$artifact_root/build/$codex_name"
|
printf '#!/usr/bin/env bash\n# fixture %s\nprintf "codex-cli 0.150.1\\n"\n' "$machine" \
|
||||||
chmod +x "$artifact_root/build/$codex_name"
|
> "$artifact_root/build/bin/codex"
|
||||||
tar -C "$artifact_root/build" -czf "$artifact_root/codex-$machine.tar.gz" "$codex_name"
|
cp "$artifact_root/build/bin/codex" "$artifact_root/codex-$machine.bin"
|
||||||
rm -f -- "$artifact_root/build/$codex_name"
|
printf '#!/usr/bin/env bash\nprintf "code mode host\\n"\n' \
|
||||||
|
> "$artifact_root/build/bin/codex-code-mode-host"
|
||||||
|
printf '{"target":"%s"}\n' "$machine" > "$artifact_root/build/codex-package.json"
|
||||||
|
for package_binary in codex-path/rg codex-resources/bwrap codex-resources/zsh/bin/zsh; do
|
||||||
|
printf '#!/usr/bin/env bash\nprintf "package resource\\n"\n' \
|
||||||
|
> "$artifact_root/build/$package_binary"
|
||||||
|
done
|
||||||
|
chmod +x "$artifact_root/build/bin/codex" \
|
||||||
|
"$artifact_root/build/bin/codex-code-mode-host" \
|
||||||
|
"$artifact_root/build/codex-path/rg" "$artifact_root/build/codex-resources/bwrap" \
|
||||||
|
"$artifact_root/build/codex-resources/zsh/bin/zsh"
|
||||||
|
tar -C "$artifact_root/build" --no-recursion -czf "$artifact_root/codex-$machine.tar.gz" \
|
||||||
|
bin/ bin/codex bin/codex-code-mode-host codex-package.json \
|
||||||
|
codex-path/ codex-path/rg codex-resources/ codex-resources/bwrap \
|
||||||
|
codex-resources/zsh/ codex-resources/zsh/bin/ codex-resources/zsh/bin/zsh
|
||||||
|
rm -rf -- "$artifact_root/build/bin" "$artifact_root/build/codex-path" \
|
||||||
|
"$artifact_root/build/codex-resources"
|
||||||
|
rm -f -- "$artifact_root/build/codex-package.json"
|
||||||
done
|
done
|
||||||
mkdir -p "$artifact_root/build/wrong-node/bin" "$artifact_root/build/wrong-codex"
|
mkdir -p "$artifact_root/build/wrong-node/bin" "$artifact_root/build/wrong-codex"
|
||||||
printf '#!/usr/bin/env bash\nprintf "v24.20.0\\n"\n' > "$artifact_root/build/wrong-node/bin/node"
|
printf '#!/usr/bin/env bash\nprintf "v24.20.0\\n"\n' > "$artifact_root/build/wrong-node/bin/node"
|
||||||
@@ -364,8 +390,15 @@ printf '#!/usr/bin/env bash\nprintf "1.4.0\\n"\n' > "$artifact_root/build/bun-li
|
|||||||
printf 'unexpected\n' > "$artifact_root/build/bun-linux-x64/extra"
|
printf 'unexpected\n' > "$artifact_root/build/bun-linux-x64/extra"
|
||||||
chmod +x "$artifact_root/build/bun-linux-x64/bun"
|
chmod +x "$artifact_root/build/bun-linux-x64/bun"
|
||||||
(cd "$artifact_root/build" && zip -q "$artifact_root/bun-bad.zip" \
|
(cd "$artifact_root/build" && zip -q "$artifact_root/bun-bad.zip" \
|
||||||
bun-linux-x64/bun bun-linux-x64/extra)
|
bun-linux-x64/ bun-linux-x64/bun bun-linux-x64/extra)
|
||||||
printf 'reviewed rustdesk fixture\n' > "$artifact_root/rustdesk.rpm"
|
printf 'reviewed rustdesk fixture\n' > "$artifact_root/rustdesk.rpm"
|
||||||
|
printf '#!/usr/bin/env bash\nprintf "executed\\n" > "$OUTSIDE_EXECUTED"\nprintf "v24.20.0\\n"\n' \
|
||||||
|
> "$artifact_root/outside-node"
|
||||||
|
chmod +x "$artifact_root/outside-node"
|
||||||
|
node_escape_top='node-v24.20.0-linux-x64'
|
||||||
|
mkdir -p "$artifact_root/build/$node_escape_top/bin"
|
||||||
|
ln -s "$artifact_root/outside-node" "$artifact_root/build/$node_escape_top/bin/node"
|
||||||
|
tar -C "$artifact_root/build" -cJf "$artifact_root/node-symlink-escape.tar.xz" "$node_escape_top"
|
||||||
rm -rf -- "$artifact_root/build"
|
rm -rf -- "$artifact_root/build"
|
||||||
|
|
||||||
make_stub_commands() {
|
make_stub_commands() {
|
||||||
@@ -383,6 +416,69 @@ STUB
|
|||||||
cat > "$case_root/bin/uname" <<'STUB'
|
cat > "$case_root/bin/uname" <<'STUB'
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
printf '%s\n' "${STUB_ARCH:-x86_64}"
|
printf '%s\n' "${STUB_ARCH:-x86_64}"
|
||||||
|
STUB
|
||||||
|
|
||||||
|
cat > "$case_root/bin/tar" <<'STUB'
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
/usr/bin/tar "$@"
|
||||||
|
if [[ "${STUB_SIGNAL_PHASE:-}" == extract && "$*" == *'-x'* ]]; then
|
||||||
|
printf 'signal:extract\n' >> "$COMMAND_LOG"
|
||||||
|
pgid="$(ps -o pgid= -p $$ | tr -d ' ')"
|
||||||
|
kill -TERM -- "-$pgid"
|
||||||
|
sleep 2
|
||||||
|
fi
|
||||||
|
if [[ "${STUB_NODE_ESCAPE:-}" == hardlink && "$*" == *'-xJf'* ]]; then
|
||||||
|
destination=''
|
||||||
|
while (($#)); do
|
||||||
|
if [[ "$1" == -C ]]; then
|
||||||
|
destination="$2"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
[[ -n "$destination" ]] || exit 71
|
||||||
|
node="$destination/node-v24.20.0-linux-x64/bin/node"
|
||||||
|
rm -f -- "$node"
|
||||||
|
ln "$ARTIFACT_ROOT/outside-node" "$node"
|
||||||
|
fi
|
||||||
|
STUB
|
||||||
|
|
||||||
|
cat > "$case_root/bin/mv" <<'STUB'
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
destination="${!#}"
|
||||||
|
if [[ "${STUB_SIGNAL_PHASE:-}" == activation ]]; then
|
||||||
|
case "$destination" in
|
||||||
|
*/.nvm/versions/node/v24.20.0|*/.bun/versions/1.4.0|*/.local/lib/panama/codex/0.150.1)
|
||||||
|
printf 'signal:activation\n' >> "$COMMAND_LOG"
|
||||||
|
pgid="$(ps -o pgid= -p $$ | tr -d ' ')"
|
||||||
|
kill -TERM -- "-$pgid"
|
||||||
|
sleep 2
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
elif [[ "${STUB_SIGNAL_PHASE:-}" == link ]]; then
|
||||||
|
case "$destination" in
|
||||||
|
*/.bun/bin/bun|*/.local/bin/codex)
|
||||||
|
printf 'signal:link\n' >> "$COMMAND_LOG"
|
||||||
|
pgid="$(ps -o pgid= -p $$ | tr -d ' ')"
|
||||||
|
kill -TERM -- "-$pgid"
|
||||||
|
sleep 2
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
case "${STUB_LATE_COLLISION:-}:$destination" in
|
||||||
|
Node:*/.nvm/versions/node/v24.20.0|\
|
||||||
|
Bun:*/.bun/versions/1.4.0|\
|
||||||
|
Codex:*/.local/lib/panama/codex/0.150.1)
|
||||||
|
if [[ ! -e "$destination" ]]; then
|
||||||
|
mkdir -p -- "$destination"
|
||||||
|
printf 'preserved collision\n' > "$destination/collision-marker"
|
||||||
|
printf 'mv:late-collision:%s\n' "$STUB_LATE_COLLISION" >> "$COMMAND_LOG"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
exec /usr/bin/mv "$@"
|
||||||
STUB
|
STUB
|
||||||
|
|
||||||
cat > "$case_root/bin/rpm" <<'STUB'
|
cat > "$case_root/bin/rpm" <<'STUB'
|
||||||
@@ -427,6 +523,13 @@ output_name="$(basename "$output")"
|
|||||||
output_name="${output_name#.}"
|
output_name="${output_name#.}"
|
||||||
output_name="${output_name%.part.*}"
|
output_name="${output_name%.part.*}"
|
||||||
printf 'curl:%s:max=%s:output=%s\n' "$url" "$max_filesize" "$output_name" >> "$COMMAND_LOG"
|
printf 'curl:%s:max=%s:output=%s\n' "$url" "$max_filesize" "$output_name" >> "$COMMAND_LOG"
|
||||||
|
if [[ "${STUB_SIGNAL_PHASE:-}" == download ]]; then
|
||||||
|
printf 'partial' > "$output"
|
||||||
|
printf 'signal:download\n' >> "$COMMAND_LOG"
|
||||||
|
pgid="$(ps -o pgid= -p $$ | tr -d ' ')"
|
||||||
|
kill -TERM -- "-$pgid"
|
||||||
|
sleep 2
|
||||||
|
fi
|
||||||
if [[ "${STUB_DOWNLOAD_INTERRUPT:-}" == 1 ]]; then
|
if [[ "${STUB_DOWNLOAD_INTERRUPT:-}" == 1 ]]; then
|
||||||
printf 'partial' > "$output"
|
printf 'partial' > "$output"
|
||||||
exit 42
|
exit 42
|
||||||
@@ -436,8 +539,12 @@ case "$url" in
|
|||||||
*rpmfusion-nonfree*) cp "$SIGNED_RPM" "$output" ;;
|
*rpmfusion-nonfree*) cp "$SIGNED_RPM" "$output" ;;
|
||||||
*flathub.flatpakrepo) cp "$FLATHUB_DESCRIPTOR" "$output" ;;
|
*flathub.flatpakrepo) cp "$FLATHUB_DESCRIPTOR" "$output" ;;
|
||||||
*node-v24.20.0-linux-x64.tar.xz)
|
*node-v24.20.0-linux-x64.tar.xz)
|
||||||
|
if [[ "${STUB_NODE_ESCAPE:-}" == symlink ]]; then
|
||||||
|
cp "$ARTIFACT_ROOT/node-symlink-escape.tar.xz" "$output"
|
||||||
|
else
|
||||||
cp "$ARTIFACT_ROOT/${STUB_BAD_LAYOUT:+node-bad.tar.xz}" "$output" 2>/dev/null \
|
cp "$ARTIFACT_ROOT/${STUB_BAD_LAYOUT:+node-bad.tar.xz}" "$output" 2>/dev/null \
|
||||||
|| cp "$ARTIFACT_ROOT/node-x86_64.tar.xz" "$output"
|
|| cp "$ARTIFACT_ROOT/node-x86_64.tar.xz" "$output"
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
*node-v24.20.0-linux-arm64.tar.xz) cp "$ARTIFACT_ROOT/node-aarch64.tar.xz" "$output" ;;
|
*node-v24.20.0-linux-arm64.tar.xz) cp "$ARTIFACT_ROOT/node-aarch64.tar.xz" "$output" ;;
|
||||||
*bun-linux-x64.zip)
|
*bun-linux-x64.zip)
|
||||||
@@ -467,6 +574,7 @@ for spec in \
|
|||||||
'node-x86_64.tar.xz 2f2c0da162318f0de47665410c7c8c2ed3d36c8f3105de4bbc61176c70a7cbf2' \
|
'node-x86_64.tar.xz 2f2c0da162318f0de47665410c7c8c2ed3d36c8f3105de4bbc61176c70a7cbf2' \
|
||||||
'node-aarch64.tar.xz 5f4ddab610c1ab2016b3c227cebdbf6d9495161487e4739c7b90090595f465f7' \
|
'node-aarch64.tar.xz 5f4ddab610c1ab2016b3c227cebdbf6d9495161487e4739c7b90090595f465f7' \
|
||||||
'node-bad.tar.xz 2f2c0da162318f0de47665410c7c8c2ed3d36c8f3105de4bbc61176c70a7cbf2' \
|
'node-bad.tar.xz 2f2c0da162318f0de47665410c7c8c2ed3d36c8f3105de4bbc61176c70a7cbf2' \
|
||||||
|
'node-symlink-escape.tar.xz 2f2c0da162318f0de47665410c7c8c2ed3d36c8f3105de4bbc61176c70a7cbf2' \
|
||||||
'bun-x86_64.zip 2d03fb5fb83ac8b567aca0a281b2ce1a1a19d488f56c2968d88c3f25e92fe452' \
|
'bun-x86_64.zip 2d03fb5fb83ac8b567aca0a281b2ce1a1a19d488f56c2968d88c3f25e92fe452' \
|
||||||
'bun-aarch64.zip 4b1a332ee861983eb93bcfe6f770fff94e3e31b2c388bdaea3c8ed35e58eed0e' \
|
'bun-aarch64.zip 4b1a332ee861983eb93bcfe6f770fff94e3e31b2c388bdaea3c8ed35e58eed0e' \
|
||||||
'bun-bad.zip 2d03fb5fb83ac8b567aca0a281b2ce1a1a19d488f56c2968d88c3f25e92fe452' \
|
'bun-bad.zip 2d03fb5fb83ac8b567aca0a281b2ce1a1a19d488f56c2968d88c3f25e92fe452' \
|
||||||
@@ -480,6 +588,19 @@ for spec in \
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
for binary_spec in \
|
||||||
|
'node-x86_64.bin 89af8424dd53e560b1933f87ba650d8bf57c83ca5a04600eefb31f416aabbae7' \
|
||||||
|
'node-aarch64.bin 23a5637c2470fde09fcc1acc77c1b92e04e3d7e3e6e80ff7df6f5831958d1477' \
|
||||||
|
'bun-x86_64.bin 33d56b070be6a9e3da0ab013038b43d1645d0534ca811ecdba4472599117eb4b' \
|
||||||
|
'bun-aarch64.bin 086c4121c8738a8e0f5ed730e8a461bc3973b4444e372ddb77aef9a747fa2ae9' \
|
||||||
|
'codex-x86_64.bin abf1bb1643a79f73aa78ee627e111e02d4f8c98f25813a0cf6ce277709664386' \
|
||||||
|
'codex-aarch64.bin 7a49aabe11fd95a1c968d79e16b5f1b17c3219002c5f7129d467f415f8460feb'; do
|
||||||
|
read -r fixture digest <<<"$binary_spec"
|
||||||
|
if cmp -s "$file" "$ARTIFACT_ROOT/$fixture"; then
|
||||||
|
printf '%s %s\n' "$digest" "$file"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
/usr/bin/sha256sum "$@"
|
/usr/bin/sha256sum "$@"
|
||||||
STUB
|
STUB
|
||||||
|
|
||||||
@@ -571,6 +692,17 @@ for argument in "$@"; do
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
printf 'sudo:%s\n' "${logged[*]}" >> "$COMMAND_LOG"
|
printf 'sudo:%s\n' "${logged[*]}" >> "$COMMAND_LOG"
|
||||||
|
if [[ "$original" == *'/rustdesk.rpm'* ]]; then
|
||||||
|
[[ "$original" == *'--setopt=localpkg_gpgcheck=0'* \
|
||||||
|
&& "$original" != *'--setopt=localpkg_gpgcheck=1'* ]] || exit 70
|
||||||
|
fi
|
||||||
|
if [[ "$original" == *' pnpm' || "$original" == *' claude-code' ]]; then
|
||||||
|
case "$original" in
|
||||||
|
'dnf install -y --repo=fedora --repo=updates pnpm'|\
|
||||||
|
'dnf install -y --repo=claude-code --repo=fedora --repo=updates claude-code') ;;
|
||||||
|
*) exit 71 ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
if [[ -n "${STUB_DNF_FAIL_MATCH:-}" && "$original" == *"$STUB_DNF_FAIL_MATCH"* ]]; then
|
if [[ -n "${STUB_DNF_FAIL_MATCH:-}" && "$original" == *"$STUB_DNF_FAIL_MATCH"* ]]; then
|
||||||
exit 68
|
exit 68
|
||||||
fi
|
fi
|
||||||
@@ -731,7 +863,9 @@ write_flathub_descriptor() {
|
|||||||
run_installer_function() {
|
run_installer_function() {
|
||||||
local name="$1" function_name="$2" case_root
|
local name="$1" function_name="$2" case_root
|
||||||
case_root="$test_tmp/cases/$name"
|
case_root="$test_tmp/cases/$name"
|
||||||
|
if [[ "${STUB_REUSE_CASE:-}" != 1 ]]; then
|
||||||
rm -rf -- "$case_root"
|
rm -rf -- "$case_root"
|
||||||
|
fi
|
||||||
make_stub_commands "$case_root"
|
make_stub_commands "$case_root"
|
||||||
: > "$case_root/commands.log"
|
: > "$case_root/commands.log"
|
||||||
printf '0\n' > "$case_root/install-counter"
|
printf '0\n' > "$case_root/install-counter"
|
||||||
@@ -760,7 +894,7 @@ run_installer_function() {
|
|||||||
"$case_root/home/.local/bin/codex"
|
"$case_root/home/.local/bin/codex"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
case "${STUB_SEED_EXACT:-}" in
|
case "${STUB_SEED_LEGACY:-}" in
|
||||||
Node)
|
Node)
|
||||||
mkdir -p "$case_root/home/.nvm/versions/node/v24.20.0/bin"
|
mkdir -p "$case_root/home/.nvm/versions/node/v24.20.0/bin"
|
||||||
printf '#!/usr/bin/env bash\nprintf "v24.20.0\\n"\n' \
|
printf '#!/usr/bin/env bash\nprintf "v24.20.0\\n"\n' \
|
||||||
@@ -899,9 +1033,11 @@ run_installer_function() {
|
|||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
(
|
||||||
COMMAND_LOG="$case_root/commands.log" \
|
COMMAND_LOG="$case_root/commands.log" \
|
||||||
SOFT_LOG="$case_root/softly-failed" \
|
SOFT_LOG="$case_root/softly-failed" \
|
||||||
ARTIFACT_ROOT="$artifact_root" \
|
ARTIFACT_ROOT="$artifact_root" \
|
||||||
|
OUTSIDE_EXECUTED="$case_root/outside-executed" \
|
||||||
FIXTURE_ROOT="$installer_fixture" \
|
FIXTURE_ROOT="$installer_fixture" \
|
||||||
REVIEWED_KEYS="$repo_dir/setup/provenance/keys" \
|
REVIEWED_KEYS="$repo_dir/setup/provenance/keys" \
|
||||||
SIGNED_RPM="$test_tmp/signed-fixture.rpm" \
|
SIGNED_RPM="$test_tmp/signed-fixture.rpm" \
|
||||||
@@ -916,8 +1052,9 @@ run_installer_function() {
|
|||||||
TMPDIR="$case_root/tmp" \
|
TMPDIR="$case_root/tmp" \
|
||||||
PANAMA_PATH="$installer_fixture" \
|
PANAMA_PATH="$installer_fixture" \
|
||||||
PATH="$case_root/bin:/usr/bin:/bin" \
|
PATH="$case_root/bin:/usr/bin:/bin" \
|
||||||
bash -c 'source "$PANAMA_PATH/setup/scripts/install-packages"; PANAMA_SYSTEM_ETC="$STUB_ETC"; PANAMA_SYSTEM_FLATPAK_REPO="$STUB_FLATPAK_REPO"; declare -F "$1" >/dev/null; status=0; "$1" || status=$?; (( ${#softly_failed[@]} == 0 )) || printf "%s\n" "${softly_failed[@]}" > "$SOFT_LOG"; exit "$status"' \
|
setsid bash -c 'source "$PANAMA_PATH/setup/scripts/install-packages"; PANAMA_SYSTEM_ETC="$STUB_ETC"; PANAMA_SYSTEM_FLATPAK_REPO="$STUB_FLATPAK_REPO"; declare -F "$1" >/dev/null; status=0; "$1" || status=$?; (( ${#softly_failed[@]} == 0 )) || printf "%s\n" "${softly_failed[@]}" > "$SOFT_LOG"; exit "$status"' \
|
||||||
bash "$function_name" > "$case_root/output" 2>&1
|
bash "$function_name"
|
||||||
|
) > "$case_root/output" 2>&1
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_log() {
|
assert_log() {
|
||||||
@@ -944,8 +1081,9 @@ assert_no_download() {
|
|||||||
|
|
||||||
assert_no_runtime_staging() {
|
assert_no_runtime_staging() {
|
||||||
local name="$1"
|
local name="$1"
|
||||||
[[ -z "$(find "$test_tmp/cases/$name/home" "$test_tmp/cases/$name/tmp" \
|
[[ -z "$(find "$test_tmp/cases/$name/home" \
|
||||||
\( -name '*.part.*' -o -name '*.stage.*' -o -name '*.link.*' \) -print -quit)" ]] \
|
\( -name '*.part.*' -o -name '*.stage.*' -o -name '*.link.*' \) -print -quit)" \
|
||||||
|
&& -z "$(find "$test_tmp/cases/$name/tmp" -mindepth 1 -print -quit)" ]] \
|
||||||
|| fail "$name left private runtime staging behind"
|
|| fail "$name left private runtime staging behind"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -996,6 +1134,37 @@ for runtime_case in \
|
|||||||
assert_no_runtime_staging "$name"
|
assert_no_runtime_staging "$name"
|
||||||
done
|
done
|
||||||
|
|
||||||
|
for receipt_spec in \
|
||||||
|
'node-x86_64 .nvm/versions/node/v24.20.0 2f2c0da162318f0de47665410c7c8c2ed3d36c8f3105de4bbc61176c70a7cbf2 89af8424dd53e560b1933f87ba650d8bf57c83ca5a04600eefb31f416aabbae7' \
|
||||||
|
'node-aarch64 .nvm/versions/node/v24.20.0 5f4ddab610c1ab2016b3c227cebdbf6d9495161487e4739c7b90090595f465f7 23a5637c2470fde09fcc1acc77c1b92e04e3d7e3e6e80ff7df6f5831958d1477' \
|
||||||
|
'bun-x86_64 .bun/versions/1.4.0 2d03fb5fb83ac8b567aca0a281b2ce1a1a19d488f56c2968d88c3f25e92fe452 33d56b070be6a9e3da0ab013038b43d1645d0534ca811ecdba4472599117eb4b' \
|
||||||
|
'bun-aarch64 .bun/versions/1.4.0 4b1a332ee861983eb93bcfe6f770fff94e3e31b2c388bdaea3c8ed35e58eed0e 086c4121c8738a8e0f5ed730e8a461bc3973b4444e372ddb77aef9a747fa2ae9' \
|
||||||
|
'codex-x86_64 .local/lib/panama/codex/0.150.1 00aba704f029f6dc0d948be407a756e0c97cc840132fd691353b2c6b0a505b17 abf1bb1643a79f73aa78ee627e111e02d4f8c98f25813a0cf6ce277709664386' \
|
||||||
|
'codex-aarch64 .local/lib/panama/codex/0.150.1 1ecac3f87823efb98153233b076ea3d6e34a7a8cebe43c5285dc5f79e1514639 7a49aabe11fd95a1c968d79e16b5f1b17c3219002c5f7129d467f415f8460feb'; do
|
||||||
|
read -r name target_relative artifact_digest binary_digest <<<"$receipt_spec"
|
||||||
|
assert_file_bytes "$test_tmp/cases/$name/home/$target_relative/.panama-provenance" \
|
||||||
|
"$(printf 'schema=1\nartifact_sha256=%s\nbinary_sha256=%s\n' \
|
||||||
|
"$artifact_digest" "$binary_digest")"
|
||||||
|
done
|
||||||
|
|
||||||
|
# No-op trust comes only from directories produced and attested by a successful
|
||||||
|
# installer run, never from a handcrafted executable that prints the version.
|
||||||
|
for exact_spec in \
|
||||||
|
'node-x86_64 x86_64 install_node' \
|
||||||
|
'node-aarch64 aarch64 install_node' \
|
||||||
|
'bun-x86_64 x86_64 install_bun' \
|
||||||
|
'bun-aarch64 aarch64 install_bun' \
|
||||||
|
'codex-x86_64 x86_64 install_codex' \
|
||||||
|
'codex-aarch64 aarch64 install_codex'; do
|
||||||
|
read -r name arch function_name <<<"$exact_spec"
|
||||||
|
reset_installer_fixture
|
||||||
|
STUB_REUSE_CASE=1 STUB_ARCH="$arch" expect_success \
|
||||||
|
run_installer_function "$name" "$function_name"
|
||||||
|
assert_no_download "$name"
|
||||||
|
[[ ! -s "$test_tmp/cases/$name/softly-failed" ]] \
|
||||||
|
|| fail "$name rejected its installer-produced provenance receipt"
|
||||||
|
done
|
||||||
|
|
||||||
[[ "$($test_tmp/cases/node-x86_64/home/.nvm/versions/node/v24.20.0/bin/node --version)" \
|
[[ "$($test_tmp/cases/node-x86_64/home/.nvm/versions/node/v24.20.0/bin/node --version)" \
|
||||||
== v24.20.0 ]] || fail 'x86_64 Node activation has the wrong version'
|
== v24.20.0 ]] || fail 'x86_64 Node activation has the wrong version'
|
||||||
grep -qFx 'nvm:alias default 24.20.0' "$test_tmp/cases/node-x86_64/commands.log" \
|
grep -qFx 'nvm:alias default 24.20.0' "$test_tmp/cases/node-x86_64/commands.log" \
|
||||||
@@ -1026,6 +1195,41 @@ for unsupported_case in \
|
|||||||
assert_soft_failure "$name" "$component"
|
assert_soft_failure "$name" "$component"
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# A target can appear after the initial absence check. Activation must not
|
||||||
|
# replace it or move the staged directory inside it, and active older tools
|
||||||
|
# must stay selected.
|
||||||
|
for component_spec in \
|
||||||
|
'Node install_node .nvm/versions/node/v24.20.0' \
|
||||||
|
'Bun install_bun .bun/versions/1.4.0' \
|
||||||
|
'Codex install_codex .local/lib/panama/codex/0.150.1'; do
|
||||||
|
read -r component function_name target_relative <<<"$component_spec"
|
||||||
|
name="${component,,}-late-collision"
|
||||||
|
reset_installer_fixture
|
||||||
|
STUB_SEED_OLD="$component" STUB_LATE_COLLISION="$component" \
|
||||||
|
expect_failure run_installer_function "$name" "$function_name"
|
||||||
|
target="$test_tmp/cases/$name/home/$target_relative"
|
||||||
|
assert_file_bytes "$target/collision-marker" 'preserved collision'
|
||||||
|
[[ -z "$(find "$target" -mindepth 1 ! -name collision-marker -print -quit)" ]] \
|
||||||
|
|| fail "$name nested verified staging into the late collision"
|
||||||
|
assert_old_runtime_preserved "$name" "$component"
|
||||||
|
assert_soft_failure "$name" "$component"
|
||||||
|
assert_no_runtime_staging "$name"
|
||||||
|
done
|
||||||
|
|
||||||
|
# A staged Node must prove that links stay inside the verified tree before its
|
||||||
|
# executable can run. The hard-link adapter changes the extracted inode at the
|
||||||
|
# filesystem boundary, which catches checks that inspect tar names only.
|
||||||
|
for escape_kind in symlink hardlink; do
|
||||||
|
name="node-$escape_kind-escape"
|
||||||
|
reset_installer_fixture
|
||||||
|
STUB_NODE_ESCAPE="$escape_kind" expect_failure \
|
||||||
|
run_installer_function "$name" install_node
|
||||||
|
[[ ! -e "$test_tmp/cases/$name/outside-executed" ]] \
|
||||||
|
|| fail "$name executed a Node target outside the staged tree"
|
||||||
|
assert_soft_failure "$name" Node
|
||||||
|
assert_no_runtime_staging "$name"
|
||||||
|
done
|
||||||
|
|
||||||
# A bad digest or interrupted transfer cannot replace the previously active
|
# A bad digest or interrupted transfer cannot replace the previously active
|
||||||
# tool and cannot leave reusable bytes behind.
|
# tool and cannot leave reusable bytes behind.
|
||||||
for failure_mode in digest interrupted; do
|
for failure_mode in digest interrupted; do
|
||||||
@@ -1063,6 +1267,31 @@ for failure_mode in digest interrupted; do
|
|||||||
assert_no_runtime_staging "$name"
|
assert_no_runtime_staging "$name"
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# Deliver a real signal to each isolated installer process group while private
|
||||||
|
# state exists. Cleanup must run for download, extraction, directory activation,
|
||||||
|
# and active-link replacement without changing an older selected runtime.
|
||||||
|
for signal_spec in \
|
||||||
|
'node-signal-extract install_node Node extract' \
|
||||||
|
'bun-signal-activation install_bun Bun activation' \
|
||||||
|
'codex-signal-download install_codex Codex download' \
|
||||||
|
'rustdesk-signal-download install_rustdesk RustDesk download' \
|
||||||
|
'bun-signal-link install_bun Bun link' \
|
||||||
|
'codex-signal-link install_codex Codex link'; do
|
||||||
|
read -r name function_name component phase <<<"$signal_spec"
|
||||||
|
reset_installer_fixture
|
||||||
|
if [[ "$component" == RustDesk ]]; then
|
||||||
|
STUB_RUSTDESK_VERSION=1.4.8 STUB_SIGNAL_PHASE="$phase" \
|
||||||
|
expect_failure run_installer_function "$name" "$function_name" 2>/dev/null
|
||||||
|
else
|
||||||
|
STUB_SEED_OLD="$component" STUB_SIGNAL_PHASE="$phase" \
|
||||||
|
expect_failure run_installer_function "$name" "$function_name" 2>/dev/null
|
||||||
|
assert_old_runtime_preserved "$name" "$component"
|
||||||
|
fi
|
||||||
|
grep -qFx "signal:$phase" "$test_tmp/cases/$name/commands.log" \
|
||||||
|
|| fail "$name did not deliver its real process-group signal"
|
||||||
|
assert_no_runtime_staging "$name"
|
||||||
|
done
|
||||||
|
|
||||||
# Successful updates keep the old version directory and switch only the active
|
# Successful updates keep the old version directory and switch only the active
|
||||||
# symlink after the replacement binary has passed its version check.
|
# symlink after the replacement binary has passed its version check.
|
||||||
for component_spec in 'Bun install_bun .bun/bin/bun .bun/versions/1.4.0/bin/bun' \
|
for component_spec in 'Bun install_bun .bun/bin/bun .bun/versions/1.4.0/bin/bun' \
|
||||||
@@ -1083,6 +1312,45 @@ for component_spec in 'Bun install_bun .bun/bin/bun .bun/versions/1.4.0/bin/bun'
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# Receipt or executable tampering preserves the directory and leaves an older
|
||||||
|
# active link unchanged. A matching version string is not an attestation.
|
||||||
|
for tamper_spec in \
|
||||||
|
'Node install_node .nvm/versions/node/v24.20.0 bin/node v24.20.0' \
|
||||||
|
'Bun install_bun .bun/versions/1.4.0 bin/bun 1.4.0' \
|
||||||
|
'Codex install_codex .local/lib/panama/codex/0.150.1 codex codex-cli_0.150.1'; do
|
||||||
|
read -r component function_name target_relative binary_relative version_text <<<"$tamper_spec"
|
||||||
|
for tamper_kind in receipt binary; do
|
||||||
|
name="${component,,}-$tamper_kind-tamper"
|
||||||
|
reset_installer_fixture
|
||||||
|
STUB_SEED_OLD="$component" expect_success \
|
||||||
|
run_installer_function "$name" "$function_name"
|
||||||
|
home="$test_tmp/cases/$name/home"
|
||||||
|
target="$home/$target_relative"
|
||||||
|
if [[ "$component" == Bun ]]; then
|
||||||
|
ln -sfn "$home/.bun/versions/1.3.0/bin/bun" "$home/.bun/bin/bun"
|
||||||
|
elif [[ "$component" == Codex ]]; then
|
||||||
|
ln -sfn "$home/.local/lib/panama/codex/0.149.0/codex" "$home/.local/bin/codex"
|
||||||
|
fi
|
||||||
|
if [[ "$tamper_kind" == receipt ]]; then
|
||||||
|
sed -i 's/^artifact_sha256=.*/artifact_sha256=0000000000000000000000000000000000000000000000000000000000000000/' \
|
||||||
|
"$target/.panama-provenance"
|
||||||
|
else
|
||||||
|
version_text="${version_text//_/ }"
|
||||||
|
printf '#!/usr/bin/env bash\nprintf "%s\\n"\n' "$version_text" \
|
||||||
|
> "$target/$binary_relative"
|
||||||
|
chmod +x "$target/$binary_relative"
|
||||||
|
fi
|
||||||
|
STUB_REUSE_CASE=1 expect_failure \
|
||||||
|
run_installer_function "$name" "$function_name"
|
||||||
|
assert_no_download "$name"
|
||||||
|
assert_soft_failure "$name" "$component"
|
||||||
|
if [[ "$component" != Node ]]; then
|
||||||
|
assert_old_runtime_preserved "$name" "$component"
|
||||||
|
fi
|
||||||
|
assert_no_runtime_staging "$name"
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
# A reviewed digest does not excuse a malformed archive. Reject the wrong top
|
# A reviewed digest does not excuse a malformed archive. Reject the wrong top
|
||||||
# level or any extra member before a version path or active link appears.
|
# level or any extra member before a version path or active link appears.
|
||||||
for layout_case in \
|
for layout_case in \
|
||||||
@@ -1098,9 +1366,9 @@ for layout_case in \
|
|||||||
assert_no_runtime_staging "$name"
|
assert_no_runtime_staging "$name"
|
||||||
done
|
done
|
||||||
|
|
||||||
# A valid collision is a no-download no-op. An invalid collision is preserved
|
# Legacy version-only and mismatched collisions are preserved and reported;
|
||||||
# and reported instead of being deleted and recreated.
|
# neither can be deleted and recreated or treated as installer provenance.
|
||||||
for collision_mode in exact collision; do
|
for collision_mode in legacy collision; do
|
||||||
for component_spec in \
|
for component_spec in \
|
||||||
'Node install_node' \
|
'Node install_node' \
|
||||||
'Bun install_bun' \
|
'Bun install_bun' \
|
||||||
@@ -1108,11 +1376,10 @@ for collision_mode in exact collision; do
|
|||||||
read -r component function_name <<<"$component_spec"
|
read -r component function_name <<<"$component_spec"
|
||||||
name="${component,,}-$collision_mode"
|
name="${component,,}-$collision_mode"
|
||||||
reset_installer_fixture
|
reset_installer_fixture
|
||||||
if [[ "$collision_mode" == exact ]]; then
|
if [[ "$collision_mode" == legacy ]]; then
|
||||||
STUB_SEED_EXACT="$component" expect_success \
|
STUB_SEED_LEGACY="$component" expect_failure \
|
||||||
run_installer_function "$name" "$function_name"
|
run_installer_function "$name" "$function_name"
|
||||||
[[ ! -s "$test_tmp/cases/$name/softly-failed" ]] \
|
assert_soft_failure "$name" "$component"
|
||||||
|| fail "$name reported a failure for the exact installed version"
|
|
||||||
else
|
else
|
||||||
STUB_SEED_COLLISION="$component" expect_failure \
|
STUB_SEED_COLLISION="$component" expect_failure \
|
||||||
run_installer_function "$name" "$function_name"
|
run_installer_function "$name" "$function_name"
|
||||||
@@ -1128,7 +1395,7 @@ STUB_ARCH=x86_64 STUB_RUSTDESK_VERSION=1.4.8 \
|
|||||||
assert_log rustdesk-x86_64 "$(cat <<'EXPECTED'
|
assert_log rustdesk-x86_64 "$(cat <<'EXPECTED'
|
||||||
rpm:query:rustdesk
|
rpm:query:rustdesk
|
||||||
curl:https://github.com/rustdesk/rustdesk/releases/download/1.4.9/rustdesk-1.4.9-0.x86_64.rpm:max=134217728:output=rustdesk.rpm
|
curl:https://github.com/rustdesk/rustdesk/releases/download/1.4.9/rustdesk-1.4.9-0.x86_64.rpm:max=134217728:output=rustdesk.rpm
|
||||||
sudo:dnf install -y --setopt=localpkg_gpgcheck=1 RUSTDESK_LOCAL
|
sudo:dnf install -y --setopt=localpkg_gpgcheck=0 RUSTDESK_LOCAL
|
||||||
EXPECTED
|
EXPECTED
|
||||||
)"
|
)"
|
||||||
assert_no_runtime_staging rustdesk-x86_64
|
assert_no_runtime_staging rustdesk-x86_64
|
||||||
@@ -1140,7 +1407,11 @@ assert_log rustdesk-exact 'rpm:query:rustdesk'
|
|||||||
|
|
||||||
reset_installer_fixture
|
reset_installer_fixture
|
||||||
expect_success run_installer_function pnpm install_pnpm
|
expect_success run_installer_function pnpm install_pnpm
|
||||||
assert_log pnpm 'sudo:dnf install -y pnpm'
|
assert_log pnpm "$(cat <<'EXPECTED'
|
||||||
|
rpm:release
|
||||||
|
sudo:dnf install -y --repo=fedora --repo=updates pnpm
|
||||||
|
EXPECTED
|
||||||
|
)"
|
||||||
|
|
||||||
reset_installer_fixture
|
reset_installer_fixture
|
||||||
STUB_DNF_FAIL_MATCH=pnpm expect_failure run_installer_function pnpm-failure install_pnpm
|
STUB_DNF_FAIL_MATCH=pnpm expect_failure run_installer_function pnpm-failure install_pnpm
|
||||||
@@ -1232,7 +1503,7 @@ rpm:release
|
|||||||
gpg:fingerprint:31DDDE24DDFAB679F42D7BD2BAA929FF1A7ECACE
|
gpg:fingerprint:31DDDE24DDFAB679F42D7BD2BAA929FF1A7ECACE
|
||||||
sudo:install:claude-code.asc:/etc/pki/rpm-gpg/RPM-GPG-KEY-claude-code-panama
|
sudo:install:claude-code.asc:/etc/pki/rpm-gpg/RPM-GPG-KEY-claude-code-panama
|
||||||
sudo:install:claude-code.repo:/etc/yum.repos.d/claude-code.repo
|
sudo:install:claude-code.repo:/etc/yum.repos.d/claude-code.repo
|
||||||
sudo:dnf install -y claude-code
|
sudo:dnf install -y --repo=claude-code --repo=fedora --repo=updates claude-code
|
||||||
EXPECTED
|
EXPECTED
|
||||||
)"
|
)"
|
||||||
cmp -s "$installer_fixture/setup/provenance/keys/claude-code.asc" \
|
cmp -s "$installer_fixture/setup/provenance/keys/claude-code.asc" \
|
||||||
@@ -1427,7 +1698,7 @@ done
|
|||||||
# A Fedora version outside the reviewed policy stops every public transaction
|
# A Fedora version outside the reviewed policy stops every public transaction
|
||||||
# before curl, sudo, Flatpak, or repository inspection can act.
|
# before curl, sudo, Flatpak, or repository inspection can act.
|
||||||
for function_name in install_rpmfusion_repositories install_terra_repository \
|
for function_name in install_rpmfusion_repositories install_terra_repository \
|
||||||
configure_hyprland_repository ensure_flathub_remote install_claude_code \
|
configure_hyprland_repository ensure_flathub_remote install_pnpm install_claude_code \
|
||||||
install_claude_desktop_if_trusted; do
|
install_claude_desktop_if_trusted; do
|
||||||
reset_installer_fixture
|
reset_installer_fixture
|
||||||
name="wrong-fedora-${function_name}"
|
name="wrong-fedora-${function_name}"
|
||||||
@@ -1435,6 +1706,8 @@ for function_name in install_rpmfusion_repositories install_terra_repository \
|
|||||||
assert_log "$name" 'rpm:release'
|
assert_log "$name" 'rpm:release'
|
||||||
if [[ "$function_name" == install_claude_code ]]; then
|
if [[ "$function_name" == install_claude_code ]]; then
|
||||||
assert_soft_failure "$name" 'Claude Code'
|
assert_soft_failure "$name" 'Claude Code'
|
||||||
|
elif [[ "$function_name" == install_pnpm ]]; then
|
||||||
|
assert_soft_failure "$name" pnpm
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user