From d61f8590d2ee7d27a7f72bea22d268e3c55bfe4f Mon Sep 17 00:00:00 2001 From: Eva Date: Mon, 13 Jul 2026 12:19:05 +0700 Subject: [PATCH 1/2] fix(nodejs): resolve OpenSSL through macOS rpaths --- .github/workflows/nodejs-workflow.yml | 8 +++++ scripts/relocate-macos-openssl.sh | 43 ++++++++++++++++++++++++++ scripts/verify-macos-openssl-rpaths.sh | 34 ++++++++++++++++++++ 3 files changed, 85 insertions(+) create mode 100755 scripts/relocate-macos-openssl.sh create mode 100755 scripts/verify-macos-openssl-rpaths.sh diff --git a/.github/workflows/nodejs-workflow.yml b/.github/workflows/nodejs-workflow.yml index c11fabcc3..3e2c46e24 100644 --- a/.github/workflows/nodejs-workflow.yml +++ b/.github/workflows/nodejs-workflow.yml @@ -313,6 +313,14 @@ jobs: working-directory: tools/nodejs_api run: npm test + - name: Make macOS OpenSSL lookup package-manager independent + if: ${{ matrix.platform == 'darwin' }} + working-directory: tools/nodejs_api + run: | + ../../scripts/relocate-macos-openssl.sh build/lbugjs.node + ../../scripts/verify-macos-openssl-rpaths.sh build/lbugjs.node + node -e "require('./build/lbugjs.node')" + - name: Move Node.js native module (Linux) if: ${{ matrix.platform == 'linux' }} working-directory: tools/nodejs_api diff --git a/scripts/relocate-macos-openssl.sh b/scripts/relocate-macos-openssl.sh new file mode 100755 index 000000000..fd1a2aeb1 --- /dev/null +++ b/scripts/relocate-macos-openssl.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [ "$#" -ne 1 ]; then + echo "usage: $0 " >&2 + exit 2 +fi + +binary="$1" +if [ ! -f "$binary" ]; then + echo "Mach-O binary not found: $binary" >&2 + exit 1 +fi + +dependency_for() { + local library="$1" + otool -L "$binary" | awk -v library="$library" \ + 'index($1, library) && substr($1, length($1) - length(library) + 1) == library { print $1; exit }' +} + +for library in libssl.3.dylib libcrypto.3.dylib; do + dependency="$(dependency_for "$library")" + if [ -z "$dependency" ]; then + echo "OpenSSL dependency $library not found in $binary" >&2 + exit 1 + fi + if [ "$dependency" != "@rpath/$library" ]; then + install_name_tool -change "$dependency" "@rpath/$library" "$binary" + fi +done + +for rpath in \ + /opt/homebrew/opt/openssl@3/lib \ + /usr/local/opt/openssl@3/lib \ + /opt/local/lib; do + existing_rpaths="$(otool -l "$binary" | awk '/cmd LC_RPATH/{getline; getline; print $2}')" + if ! grep -Fxq "$rpath" <<<"$existing_rpaths"; then + install_name_tool -add_rpath "$rpath" "$binary" + fi +done + +# install_name_tool invalidates the existing signature. +codesign --force --sign - "$binary" diff --git a/scripts/verify-macos-openssl-rpaths.sh b/scripts/verify-macos-openssl-rpaths.sh new file mode 100755 index 000000000..03bf8412b --- /dev/null +++ b/scripts/verify-macos-openssl-rpaths.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [ "$#" -ne 1 ]; then + echo "usage: $0 " >&2 + exit 2 +fi + +binary="$1" +dependencies="$(otool -L "$binary")" + +for library in libssl.3.dylib libcrypto.3.dylib; do + if ! grep -Fq "@rpath/$library" <<<"$dependencies"; then + echo "missing @rpath dependency for $library in $binary" >&2 + exit 1 + fi +done + +if grep -Eq '^[[:space:]]+/(opt/homebrew|usr/local|opt/local)/.*lib(ssl|crypto)\.3\.dylib' \ + <<<"$dependencies"; then + echo "package-manager-specific OpenSSL dependency remains in $binary" >&2 + exit 1 +fi + +rpaths="$(otool -l "$binary" | awk '/cmd LC_RPATH/{getline; getline; print $2}')" +for required in \ + /opt/homebrew/opt/openssl@3/lib \ + /usr/local/opt/openssl@3/lib \ + /opt/local/lib; do + if ! grep -Fxq "$required" <<<"$rpaths"; then + echo "missing OpenSSL rpath $required in $binary" >&2 + exit 1 + fi +done From 0ece84a925edaedf68c36e055a773b55f4589fbe Mon Sep 17 00:00:00 2001 From: Arun Sharma Date: Mon, 13 Jul 2026 11:20:59 -0700 Subject: [PATCH 2/2] fix: add file-existence check, comment on regex, and codesigning doc - verify-macos-openssl-rpaths.sh: add explicit Mach-O binary existence check (matching the relocate script's pattern) - verify-macos-openssl-rpaths.sh: add comment explaining how the '/...' regex alternation works with the leading '/' consumed by the whitespace pattern - relocate-macos-openssl.sh: document that ad-hoc signing is for dev/CI, and a Developer ID certificate is needed for production distribution (notarization) --- scripts/relocate-macos-openssl.sh | 3 +++ scripts/verify-macos-openssl-rpaths.sh | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/scripts/relocate-macos-openssl.sh b/scripts/relocate-macos-openssl.sh index fd1a2aeb1..c3f71a222 100755 --- a/scripts/relocate-macos-openssl.sh +++ b/scripts/relocate-macos-openssl.sh @@ -40,4 +40,7 @@ for rpath in \ done # install_name_tool invalidates the existing signature. +# Ad-hoc signing ("-") is sufficient for development and CI. If this binary is +# distributed to end-users (e.g., via npm), a proper Developer ID certificate +# should be used instead for notarization compatibility. codesign --force --sign - "$binary" diff --git a/scripts/verify-macos-openssl-rpaths.sh b/scripts/verify-macos-openssl-rpaths.sh index 03bf8412b..b6af6598b 100755 --- a/scripts/verify-macos-openssl-rpaths.sh +++ b/scripts/verify-macos-openssl-rpaths.sh @@ -7,6 +7,11 @@ if [ "$#" -ne 1 ]; then fi binary="$1" +if [ ! -f "$binary" ]; then + echo "Mach-O binary not found: $binary" >&2 + exit 1 +fi + dependencies="$(otool -L "$binary")" for library in libssl.3.dylib libcrypto.3.dylib; do @@ -16,6 +21,8 @@ for library in libssl.3.dylib libcrypto.3.dylib; do fi done +# The leading '/' is matched by the '^[[:space:]]+/' portion of the regex, so the +# alternatives within the group omit it: 'opt/homebrew', 'usr/local', 'opt/local'. if grep -Eq '^[[:space:]]+/(opt/homebrew|usr/local|opt/local)/.*lib(ssl|crypto)\.3\.dylib' \ <<<"$dependencies"; then echo "package-manager-specific OpenSSL dependency remains in $binary" >&2