Skip to content
Open
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
62 changes: 62 additions & 0 deletions pkg/mac/build-functions.sh
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,68 @@ _prune_dangling_symlinks() {
fi
}

_verify_bundle_linkage() {
# Belt and braces: make sure nothing in the finished bundle links against
# a library that lives outside it on the build host. Such a reference
# resolves on the build machine but the path is absent on an end user's
# Mac (or, for a Homebrew dylib, present-but-rejected by hardened-runtime
# library validation for having a different Team ID), so the app dies on
# startup before it can even import config.
#
# The known offender is the Python cryptography module. When no binary
# wheel is published for the build architecture (cryptography 49 dropped
# the Intel/universal2 macOS wheel, leaving arm64 only), pip compiles it
# from source, and its openssl-sys crate links whatever OpenSSL it
# discovers on the build host rather than the one we ship: it ignores the
# CFLAGS/LDFLAGS the build exports and falls back to Homebrew, baking
# e.g. /usr/local/opt/openssl@3/lib/libssl.3.dylib into _rust.abi3.so.
# _fixup_imports deliberately skips _rust.abi3.so, so the dangling
# reference would otherwise sail through to a shipped DMG. See issue
# #10123.
#
# Everything legitimate in the bundle is either an OS library (/usr/lib,
# /System) or a bundle-relative reference (@loader_path, @rpath,
# @executable_path), so any absolute install-name under a build-host
# prefix is, by definition, wrong.
#
# NB: run after _complete_bundle (which does the install-name rewriting
# via _fixup_imports) so we validate the final, relocated state.

echo "Verifying bundle library references..."

# Build-host prefixes that must never appear in a shipped bundle. SLAVE_HOME
# (the Jenkins workspace root, under which the self-built OpenSSL and
# PostgreSQL live) is only added when set, i.e. on the CI builders.
local PREFIXES='/usr/local|/opt/homebrew|/opt/local'
if [ -n "${SLAVE_HOME}" ]; then
PREFIXES="${PREFIXES}|${SLAVE_HOME}"
fi

local found="" f deps
while IFS= read -r f; do
# otool prints the filename header then one line per dependency; drop
# the header, take the install-name column, and keep only build-host
# paths. grep exits non-zero (no match) for a clean binary, which is
# the common case, so fall through to the next file.
deps=$(otool -L "${f}" 2>/dev/null | tail -n +2 | awk '{print $1}' | \
grep -E "^(${PREFIXES})") || continue
echo "ERROR: ${f} links against build-host libraries:" >&2
echo "${deps}" | sed 's/^/ /' >&2
found="yes"
done < <(find "${BUNDLE_DIR}" \( -name '*.so' -o -name '*.dylib' \) -type f)
Comment on lines +498 to +508

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm the verifier is suffix-only and that the macOS packaging logic includes framework payloads.
sed -n '460,520p' pkg/mac/build-functions.sh
rg -n -C2 'Python\.framework|\.framework|Contents/MacOS' pkg/mac

Repository: pgadmin-org/pgadmin4

Length of output: 8552


🏁 Script executed:

#!/bin/bash
sed -n '460,520p' pkg/mac/build-functions.sh
sed -n '160,220p' pkg/mac/build-functions.sh
sed -n '360,450p' pkg/mac/build-functions.sh

Repository: pgadmin-org/pgadmin4

Length of output: 10457


🏁 Script executed:

#!/bin/bash
# List Mach-O files in the shipped bundle locations that are not .so/.dylib
# and inspect whether the packaging code already rewrites their imports.
rg -n -C2 'find .*Mach-O 64-bit|otool -L|_fixup_imports|_verify_bundle_linkage' pkg/mac/build-functions.sh

Repository: pgadmin-org/pgadmin4

Length of output: 2201


Scan all Mach-O binaries, not just .so/.dylib files.

This loop misses shipped executables and framework binaries (for example Contents/MacOS/* and Python.framework payloads), so a bundle can still contain host-linked Mach-O files without tripping this check. Broaden the scan to all Mach-O files, not just suffix-matched libraries.

🧰 Tools
🪛 Shellcheck (0.11.0)

[style] 506-506: See if you can use ${variable//search/replace} instead.

(SC2001)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/mac/build-functions.sh` around lines 498 - 508, The Mach-O dependency
scan in the bundle check is too narrow because the loop only inspects files
matched by the .so and .dylib suffixes, so shipped executables and framework
binaries can be missed. Update the file discovery in the build-functions.sh scan
to target all Mach-O files under BUNDLE_DIR, then keep the existing otool -L,
grep, and error reporting logic in the same loop so any host-linked binary is
caught regardless of extension.


if [ -n "${found}" ]; then
echo "ERROR: the bundle links against libraries outside it; those paths" >&2
echo " will not exist (or will fail library validation) on end-user" >&2
echo " machines and the app will not start. See issue #10123." >&2
echo " Ensure the affected module links the bundled OpenSSL, e.g." >&2
echo " set OPENSSL_DIR (and OPENSSL_STATIC) for the cryptography" >&2
echo " build so openssl-sys does not pick up Homebrew's copy." >&2
exit 1
fi
echo "Bundle library references OK."
}

_generate_sbom() {
echo "Generating SBOM..."
syft "${BUNDLE_DIR}/Contents/" -o cyclonedx-json > "${BUNDLE_DIR}/Contents/sbom.json"
Expand Down
1 change: 1 addition & 0 deletions pkg/mac/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ _build_docs
_complete_bundle
_strip_architecture
_prune_dangling_symlinks
_verify_bundle_linkage
_generate_sbom
_codesign_binaries
_codesign_bundle
Expand Down
Loading