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
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
| XcodeGen | 2.46.0 exactly | The generated project differs between versions, so `scripts/generate-project.sh` warns on any other |
| SwiftLint | 0.65.1 | The version CI runs |
| GitHub CLI | any | Optional: `scripts/download-libs.sh` falls back to `curl` |
| Go | 1.27.1 | SAP HANA plugin only: run `scripts/build-hana.sh` with no argument before building `HanaDriver` or `AllPlugins`. The script fetches 1.27.1 when the installed `go` is another release |
| Rust | 1.91.1 | Dameng plugin only, through [rustup](https://rustup.rs): run `scripts/build-dameng.sh arm64` before building `DamengDriver` or `AllPlugins` |
| Go | `toolchain` in `Native/HanaBridge/go.mod` | SAP HANA plugin only: run `scripts/build-hana.sh` with no argument before building `HanaDriver` or `AllPlugins`. When the installed `go` is another release, `go` switches to that toolchain itself |
| Rust | `Native/DamengBridge/rust-toolchain.toml` | Dameng plugin only, through [rustup](https://rustup.rs), which installs it: run `scripts/build-dameng.sh arm64` before building `DamengDriver` or `AllPlugins` |

Fork the repo on GitHub, then:

Expand Down
1 change: 1 addition & 0 deletions Native/DamengBridge/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,6 @@ To change protocol behaviour, commit to the fork's `tablepro` branch, then bump
scripts/build-dameng.sh [arm64|x86_64|both]
```

`rust-toolchain.toml` pins the Rust release and both targets; the script has rustup install them.
Fetching a git dependency needs network access. Xcode does not build this; the plugin target
links the archive `build-dameng.sh` produces, so build it before building `DamengDriver`.
4 changes: 2 additions & 2 deletions Native/HanaBridge/THIRD_PARTY_NOTICES.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ everything below is statically linked into it. The helper ships inside the SAP H
| --- | --- | --- | --- |
| [go-hdb](https://github.com/SAP/go-hdb) | v1.18.12 | Apache-2.0 | 2014-2026 SAP SE or an SAP affiliate company and go-hdb contributors |
| [golang.org/x/text](https://pkg.go.dev/golang.org/x/text) | v0.42.0 | BSD-3-Clause | 2009 The Go Authors |
| [Go runtime and standard library](https://go.dev) | go1.27.1 | BSD-3-Clause | 2009 The Go Authors |
| [Go runtime and standard library](https://go.dev) | go1.27.2 | BSD-3-Clause | 2009 The Go Authors |

The Go standard library vendors golang.org/x/crypto, x/net, x/sys and x/text. They carry the same BSD-3-Clause notice
from The Go Authors as the standard library itself.
Expand All @@ -20,6 +20,6 @@ The full license texts:

- go-hdb: <https://github.com/SAP/go-hdb/blob/v1.18.12/LICENSE.md>
- golang.org/x/text: <https://github.com/golang/text/blob/v0.42.0/LICENSE>
- Go: <https://github.com/golang/go/blob/go1.27.1/LICENSE>
- Go: <https://github.com/golang/go/blob/go1.27.2/LICENSE>

The app's acknowledgements carry the same three texts in `TablePro/Resources/ThirdPartyLicenses/texts/`.
1 change: 1 addition & 0 deletions changelog.d/3513.security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SAP HANA plugin 1.0.1 to 1.0.3 shipping a helper built with Go 1.27.1, without the crypto/tls fix for GO-2026-6607.
57 changes: 22 additions & 35 deletions scripts/build-dameng.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,59 +7,46 @@ BRIDGE_DIR="$ROOT_DIR/Native/DamengBridge"
# download-libs.sh, and publish-libs.sh treats anything unrecognised there as an error.
# The bridge is compiled from in-tree Rust instead, so its output stays beside its source.
LIBS_DIR="${LIBS_DIR:-$BRIDGE_DIR/lib}"
OUTPUT="$LIBS_DIR/libdameng_bridge.a"
ARCH="${1:-both}"
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/lib/deployment-target.sh"
MACOS_TARGET="$DEPLOY_TARGET"
RUST_TOOLCHAIN="1.91.1"

if ! rustup toolchain list | grep -q "^${RUST_TOOLCHAIN}"; then
rustup toolchain install "$RUST_TOOLCHAIN" --profile minimal
fi
# rustup takes the toolchain, profile and targets from rust-toolchain.toml in the working directory.
(cd "$BRIDGE_DIR" && rustup toolchain install --no-self-update)

build_slice() {
local arch="$1"
local target="$2"
local output="$LIBS_DIR/libdameng_bridge_${arch}.a"

if ! rustup target list --installed --toolchain "$RUST_TOOLCHAIN" | grep -qx "$target"; then
rustup target add "$target" --toolchain "$RUST_TOOLCHAIN"
fi

env RUSTUP_TOOLCHAIN="$RUST_TOOLCHAIN" MACOSX_DEPLOYMENT_TARGET="$MACOS_TARGET" \
cargo build \
--manifest-path "$BRIDGE_DIR/Cargo.toml" \
--locked \
--release \
--target "$target"
archive() {
echo "$BRIDGE_DIR/target/$1/release/libtablepro_dameng_bridge.a"
}

cp "$BRIDGE_DIR/target/$target/release/libtablepro_dameng_bridge.a" "$output"
nm -gU "$output" | grep 'T _tp_dm_connect' > /dev/null
build_slice() {
(
cd "$BRIDGE_DIR"
env MACOSX_DEPLOYMENT_TARGET="$DEPLOY_TARGET" cargo build --locked --release --target "$1"
)
nm -gU "$(archive "$1")" | grep 'T _tp_dm_connect' > /dev/null
}

mkdir -p "$LIBS_DIR"

case "$ARCH" in
arm64)
build_slice arm64 aarch64-apple-darwin
cp "$LIBS_DIR/libdameng_bridge_arm64.a" "$LIBS_DIR/libdameng_bridge.a"
build_slice aarch64-apple-darwin
cp "$(archive aarch64-apple-darwin)" "$OUTPUT"
;;
x86_64)
build_slice x86_64 x86_64-apple-darwin
cp "$LIBS_DIR/libdameng_bridge_x86_64.a" "$LIBS_DIR/libdameng_bridge.a"
build_slice x86_64-apple-darwin
cp "$(archive x86_64-apple-darwin)" "$OUTPUT"
;;
both|universal)
build_slice arm64 aarch64-apple-darwin
build_slice x86_64 x86_64-apple-darwin
lipo -create \
"$LIBS_DIR/libdameng_bridge_arm64.a" \
"$LIBS_DIR/libdameng_bridge_x86_64.a" \
-output "$LIBS_DIR/libdameng_bridge_universal.a"
cp "$LIBS_DIR/libdameng_bridge_universal.a" "$LIBS_DIR/libdameng_bridge.a"
both)
build_slice aarch64-apple-darwin
build_slice x86_64-apple-darwin
lipo -create "$(archive aarch64-apple-darwin)" "$(archive x86_64-apple-darwin)" -output "$OUTPUT"
;;
*)
echo "Usage: $0 [arm64|x86_64|both]" >&2
exit 1
;;
esac

file "$LIBS_DIR"/libdameng_bridge*.a
lipo -info "$OUTPUT"
verify_deployment_target "$OUTPUT"
50 changes: 11 additions & 39 deletions scripts/build-hana.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ BRIDGE_DIR="$ROOT_DIR/Native/HanaBridge"
BIN_DIR="$BRIDGE_DIR/bin"
HELPER_NAME="tablepro-hana-helper"
OUTPUT="$BIN_DIR/$HELPER_NAME"
GO_TOOLCHAIN="go1.27.1"
ARCH="${1:-both}"
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/lib/deployment-target.sh"

Expand All @@ -15,6 +14,13 @@ if ! command -v go > /dev/null; then
exit 1
fi

# Named exactly: the default GOTOOLCHAIN=auto treats this line as a minimum and keeps a newer local go.
GO_TOOLCHAIN="$(awk '$1 == "toolchain" { print $2 }' "$BRIDGE_DIR/go.mod")"
if [ -z "$GO_TOOLCHAIN" ]; then
echo "$BRIDGE_DIR/go.mod has no toolchain line" >&2
exit 1
fi

go_arch() {
case "$1" in
arm64) echo arm64 ;;
Expand All @@ -32,48 +38,14 @@ build_slice() {
)
}

version_at_most() {
awk -v actual="$1" -v limit="$2" 'BEGIN {
split(actual, a, ".")
split(limit, l, ".")
for (i = 1; i <= 3; i++) {
if ((a[i] + 0) < (l[i] + 0)) exit 0
if ((a[i] + 0) > (l[i] + 0)) exit 1
}
exit 0
}'
}

slice_minimum_os() {
vtool -arch "$2" -show-build "$1" | awk '$1 == "minos" { print $2; exit }'
}

verify_minimum_os() {
local binary="$1"
local arch
local version
for arch in $(lipo -archs "$binary"); do
version="$(slice_minimum_os "$binary" "$arch")"
if [ -z "$version" ]; then
echo "$binary ($arch) records no minimum macOS" >&2
exit 1
fi
if ! version_at_most "$version" "$DEPLOY_TARGET"; then
echo "$binary ($arch) targets macOS $version, above the deployment target $DEPLOY_TARGET" >&2
exit 1
fi
echo "$arch: minimum macOS $version, deployment target $DEPLOY_TARGET"
done
}

mkdir -p "$BIN_DIR"

case "$ARCH" in
arm64|x86_64)
build_slice "$ARCH"
cp "$BIN_DIR/$HELPER_NAME-$ARCH" "$OUTPUT"
;;
both|universal)
both)
build_slice arm64
build_slice x86_64
lipo -create "$BIN_DIR/$HELPER_NAME-arm64" "$BIN_DIR/$HELPER_NAME-x86_64" -output "$OUTPUT"
Expand All @@ -84,6 +56,6 @@ case "$ARCH" in
;;
esac

codesign --force -s - "$OUTPUT"
file "$OUTPUT"
verify_minimum_os "$OUTPUT"
codesign --force -o runtime -s - "$OUTPUT"
lipo -info "$OUTPUT"
verify_deployment_target "$OUTPUT"
43 changes: 36 additions & 7 deletions scripts/lib/deployment-target.sh
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
# The macOS deployment target every native library is built for, read from the one place that
# decides it.
#
# Sourced, never executed, and it defines nothing but DEPLOY_TARGET so a script with its own
# LIBS_DIR or REPO_ROOT can pick it up without inheriting anything else:
#
# source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/lib/deployment-target.sh"
# The macOS deployment target every native library and bridge is built for, read from project.yml,
# and the check that a built Mach-O stays at or below it. Sourced, never executed. Unlike common.sh
# it sets no LIBS_DIR or REPO_ROOT, so a script with its own can source it.
#
# shellcheck shell=bash
# shellcheck disable=SC2034 # DEPLOY_TARGET is read by the scripts that source this
Expand All @@ -26,3 +22,36 @@ if [ -z "$DEPLOY_TARGET" ]; then
echo "Could not read deploymentTarget.macOS from $TABLEPRO_DEPLOY_TARGET_ROOT/project.yml" >&2
exit 1
fi

# The highest minimum macOS across every slice and archive member, from LC_BUILD_VERSION or the
# older LC_VERSION_MIN_MACOSX: one newer object is enough to keep the whole file off the floor.
macho_min_version() {
otool -arch all -l "$1" 2> /dev/null | awk '
$1 == "cmd" { cmd = $2; next }
cmd == "LC_BUILD_VERSION" && $1 == "minos" { print $2 }
cmd == "LC_VERSION_MIN_MACOSX" && $1 == "version" { print $2 }
' | sort -V | tail -1
}

verify_deployment_target() {
local file name version failed=0
echo ""
echo "🔍 Verifying deployment targets..."
for file in "$@"; do
[ -f "$file" ] || continue
name=$(basename "$file")
if ! version=$(macho_min_version "$file") || [ -z "$version" ]; then
echo " ❌ $name records no minimum macOS"
failed=1
elif [ "$(printf '%s\n' "$DEPLOY_TARGET" "$version" | sort -V | tail -1)" != "$DEPLOY_TARGET" ]; then
echo " ❌ $name targets macOS $version (expected $DEPLOY_TARGET)"
failed=1
else
echo " ✅ $name targets macOS $version"
fi
done
if [ "$failed" -eq 1 ]; then
echo "❌ FATAL: some libraries have incorrect deployment targets" >&2
return 1
fi
}
37 changes: 0 additions & 37 deletions scripts/lib/macos.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,43 +32,6 @@ make_universal() {
done
}

# Reads the macOS version a Mach-O was built for. Prefers LC_BUILD_VERSION and falls back to the
# older LC_VERSION_MIN_MACOSX. `tail -1` takes the highest across the archive's members, which is
# the one that decides whether the whole library can run on the app's floor.
macho_min_version() {
local lib="$1" version
version=$(otool -l "$lib" 2>/dev/null |
awk '/LC_BUILD_VERSION/{found=1} found && /minos/{print $2; found=0}' | sort -V | tail -1)
if [ -z "$version" ]; then
version=$(otool -l "$lib" 2>/dev/null |
awk '/LC_VERSION_MIN_MACOSX/{found=1} found && /version/{print $2; found=0}' | sort -V | tail -1)
fi
printf '%s' "$version"
}

# Fails on a library built for a newer macOS than DEPLOY_TARGET; an older one runs fine.
verify_deployment_target() {
local lib name version failed=0
echo ""
echo "🔍 Verifying deployment targets..."
for lib in "$@"; do
[ -f "$lib" ] || continue
name=$(basename "$lib")
version=$(macho_min_version "$lib")
[ -n "$version" ] || continue
if [ "$(printf '%s\n' "$DEPLOY_TARGET" "$version" | sort -V | tail -1)" != "$DEPLOY_TARGET" ]; then
echo " ❌ $name targets macOS $version (expected $DEPLOY_TARGET)"
failed=1
else
echo " ✅ $name targets macOS $version"
fi
done
if [ "$failed" -eq 1 ]; then
echo "❌ FATAL: some libraries have incorrect deployment targets" >&2
return 1
fi
}

fetch_openssl() {
local tarball="$BUILD_DIR/openssl-$OPENSSL_VERSION.tar.gz"
if [ ! -f "$tarball" ]; then
Expand Down
Loading