Files
VerseVDI-Data-Plane/core/tests/packaging.sh
T
sechmachine 9c27a1ebf5
Verify Data Plane / gateway (push) Failing after 1m2s
fix(core): harden framework packaging
2026-08-13 00:27:58 +07:00

220 lines
7.9 KiB
Bash
Executable File

#!/bin/sh
set -eu
ROOT=$(CDPATH= cd -- "$(dirname "$0")/../.." && pwd)
BUILDER="$ROOT/core/scripts/build-xcframework.sh"
EXPECTED_EXPORTS='_verse_core_abi_version
_verse_core_cancel_v1
_verse_core_connect_v1
_verse_core_create_v1
_verse_core_destroy_v1
_verse_core_request_idr_v1
_verse_core_send_input_v1'
test "$(uname -s)" = Darwin
test "$(uname -m)" = arm64
test -x "$BUILDER"
WORK=$(mktemp -d "${TMPDIR:-/tmp}/versevdi-core-package.XXXXXX")
trap 'rm -rf "$WORK"' EXIT HUP INT TERM
expect_path_rejected() {
error=$1
shift
if "$BUILDER" "$@" >"$WORK/path.stdout" 2>"$WORK/path.stderr"; then
echo "unsafe packaging path was accepted" >&2
exit 1
fi
grep -F "$error" "$WORK/path.stderr" >/dev/null
}
mkdir "$WORK/parents" "$WORK/physical-parent"
mkdir "$WORK/existing-output"
expect_path_rejected "must not exist" \
--output "$WORK/existing-output" --target-dir "$WORK/parents/existing-target"
ln -s "$WORK/physical-parent" "$WORK/parent-alias"
ln -s "$WORK/physical-parent" "$WORK/second-parent-alias"
ln -s "$WORK/missing" "$WORK/dangling-output"
expect_path_rejected "must not exist" \
--output "$WORK/dangling-output" --target-dir "$WORK/parents/dangling-target"
expect_path_rejected "must be separate" \
--output "$WORK/parents/same" --target-dir "$WORK/parents/same"
expect_path_rejected "must be separate" \
--output "$WORK/parent-alias/aliased-same" \
--target-dir "$WORK/second-parent-alias/aliased-same"
ln -s "$ROOT" "$WORK/repository-alias"
expect_path_rejected "outside the repository" \
--output "$WORK/repository-alias/core/forbidden-output" \
--target-dir "$WORK/parents/repository-alias-target"
"$BUILDER" --output "$WORK/one" --target-dir "$WORK/target-one"
mkdir "$WORK/hostile-bin"
mkdir -p "$WORK/hostile-cargo-home" "$WORK/hostile-home/.cargo"
ln -s /usr/bin/false "$WORK/hostile-bin/cargo"
ln -s /usr/bin/false "$WORK/hostile-bin/rustup"
ln -s /usr/bin/false "$WORK/hostile-bin/xcodebuild"
cat >"$WORK/hostile-cargo-home/config.toml" <<'EOF'
[build]
rustc-wrapper = "/usr/bin/false"
EOF
cat >"$WORK/hostile-home/.cargo/config.toml" <<'EOF'
[build]
rustc = "/usr/bin/false"
EOF
env \
AR=/usr/bin/false \
CARGO_BUILD_RUSTC=/usr/bin/false \
CARGO_BUILD_RUSTC_WRAPPER=/usr/bin/false \
CARGO_ENCODED_RUSTFLAGS=--cfghostile \
CARGO_HOME="$WORK/hostile-cargo-home" \
CARGO_TARGET_AARCH64_APPLE_DARWIN_LINKER=/usr/bin/false \
CC=/usr/bin/false \
DEVELOPER_DIR="$WORK/hostile-xcode" \
HOME="$WORK/hostile-home" \
PATH="$WORK/hostile-bin:/usr/bin:/bin" \
RUSTC=/usr/bin/false \
RUSTC_WRAPPER=/usr/bin/false \
RUSTFLAGS=--cfg=hostile \
RUSTUP_HOME="$WORK/hostile-rustup-home" \
RUSTUP_TOOLCHAIN=bogus \
"$BUILDER" \
--output "$WORK/parent-alias/two" \
--target-dir "$WORK/parents/target-two"
framework_one="$WORK/one/VerseVDICore.xcframework"
framework_two="$WORK/physical-parent/two/VerseVDICore.xcframework"
library_one=$(find "$framework_one" -type f -name libversevdi_core.a -print)
library_two=$(find "$framework_two" -type f -name libversevdi_core.a -print)
test "$(printf '%s\n' "$library_one" | grep -c .)" -eq 1
test "$(printf '%s\n' "$library_two" | grep -c .)" -eq 1
platform=$(/usr/libexec/PlistBuddy -c 'Print :AvailableLibraries:0:SupportedPlatform' "$framework_one/Info.plist")
architecture=$(/usr/libexec/PlistBuddy -c 'Print :AvailableLibraries:0:SupportedArchitectures:0' "$framework_one/Info.plist")
available_libraries=$(/usr/libexec/PlistBuddy -c 'Print :AvailableLibraries' "$framework_one/Info.plist")
test "$platform" = macos
test "$architecture" = arm64
test "$(printf '%s\n' "$available_libraries" | grep -c 'Dict {')" -eq 1
library_archs=$(xcrun lipo -archs "$library_one")
library_identity=$(file "$library_one")
test "$library_archs" = arm64
printf '%s\n' "$library_identity" | grep -F 'current ar archive' >/dev/null
consumer="$WORK/consumer"
link_consumer() {
output=$1
library=$2
xcrun clang \
-arch arm64 \
-mmacosx-version-min=14.0 \
-std=c11 \
-Wall -Wextra -Werror -Wpedantic \
-fmodules \
-fmodules-cache-path="$WORK/module-cache" \
-I"$(dirname "$library_one")/Headers" \
"$ROOT/core/tests/ffi/abi_smoke.c" \
-Wl,-force_load,"$library" \
-framework Security \
-framework SystemConfiguration \
-framework CoreFoundation \
-lresolv \
-o "$output"
}
link_consumer "$consumer" "$library_one"
consumer_archs=$(xcrun lipo -archs "$consumer")
consumer_identity=$(file "$consumer")
test "$consumer_archs" = arm64
printf '%s\n' "$consumer_identity" | grep -F 'Mach-O 64-bit executable arm64' >/dev/null
symbols=$(xcrun nm -gjU "$consumer")
actual_exports=$(printf '%s\n' "$symbols" | grep '^_verse_core_' | LC_ALL=C sort -u)
test "$actual_exports" = "$EXPECTED_EXPORTS"
cat >"$WORK/unexpected.c" <<'EOF'
void verse_core_unexpected_v1(void) {}
EOF
xcrun clang -arch arm64 -mmacosx-version-min=14.0 -c "$WORK/unexpected.c" -o "$WORK/unexpected.o"
cp "$library_one" "$WORK/libunexpected.a"
ZERO_AR_DATE=1 xcrun ar -r "$WORK/libunexpected.a" "$WORK/unexpected.o"
link_consumer "$WORK/unexpected-consumer" "$WORK/libunexpected.a"
unexpected_symbols=$(xcrun nm -gjU "$WORK/unexpected-consumer")
if test "$(printf '%s\n' "$unexpected_symbols" | grep '^_verse_core_' | LC_ALL=C sort -u)" = "$EXPECTED_EXPORTS"; then
echo "force-loaded export inspection missed an unreferenced ABI symbol" >&2
exit 1
fi
dependencies=$(xcrun otool -L "$consumer")
unexpected_dependencies=$(printf '%s\n' "$dependencies" | tail -n +2 | awk '{print $1}' | grep -Ev '^(/usr/lib/(libSystem\.B|libresolv\.9)\.dylib|/System/Library/Frameworks/(CoreFoundation|Security|SystemConfiguration)\.framework/Versions/A/[^/]+)$' || true)
test -z "$unexpected_dependencies"
"$consumer"
i=0
pids=
while test "$i" -lt 32; do
"$consumer" &
pids="$pids $!"
i=$((i + 1))
done
for pid in $pids; do
wait "$pid"
done
canonical_tree() {
(
cd "$1"
find . -print >"$WORK/tree.entries"
LC_ALL=C sort "$WORK/tree.entries" >"$WORK/tree.sorted"
while IFS= read -r path; do
metadata=$(stat -f '%HT|%Sp' "$path")
case "$metadata" in
'Regular File|'*) digest=$(sha256_file "$path") ;;
'Symbolic Link|'*) digest=$(readlink "$path") ;;
*) digest=- ;;
esac
printf '%s|%s|%s\n' "$metadata" "$digest" "$path"
done <"$WORK/tree.sorted"
)
}
sha256_file() {
checksum=$(shasum -a 256 "$1")
set -- $checksum
printf '%s\n' "$1"
}
canonical_tree "$framework_one" >"$WORK/one.tree"
canonical_tree "$framework_two" >"$WORK/two.tree"
cmp "$WORK/one.tree" "$WORK/two.tree"
test "$(sha256_file "$library_one")" = "$(sha256_file "$library_two")"
source_epoch=$(git -C "$ROOT" show -s --format=%ct HEAD)
find "$WORK/one" "$WORK/physical-parent/two" -exec stat -f '%m' {} \; >"$WORK/mtimes"
while IFS= read -r epoch; do
test "$epoch" = "$source_epoch"
done <"$WORK/mtimes"
expected_rustc='rustc 1.97.1 (8bab26f4f 2026-07-14)'
expected_cargo='cargo 1.97.1 (c980f4866 2026-06-30)'
expected_xcode='Xcode 26.6
Build version 17F113'
grep -Fx "rustc=$expected_rustc" "$WORK/one/build-environment.txt" >/dev/null
grep -Fx "cargo=$expected_cargo" "$WORK/one/build-environment.txt" >/dev/null
test "$(sed -n 's/^xcode=//p; /^Build version /p' "$WORK/one/build-environment.txt")" = "$expected_xcode"
cmp "$WORK/one/build-environment.txt" "$WORK/physical-parent/two/build-environment.txt"
strings "$library_one" >"$WORK/library.strings"
if grep -F "$ROOT" "$WORK/library.strings" >/dev/null; then
echo "repository path leaked into static archive" >&2
exit 1
fi
if grep -F "$WORK/target-one" "$WORK/library.strings" >/dev/null; then
echo "target directory path leaked into static archive" >&2
exit 1
fi
archive_members=$(xcrun ar -tv "$library_one")
printf '%s\n' "$archive_members" | awk '$7 != "1970" { exit 1 }'
printf 'framework_sha256=%s\n' "$(sha256_file "$WORK/one.tree")"
printf 'library_sha256=%s\n' "$(sha256_file "$library_one")"