Skip to content

build(macos): bundle CPython 3.12.15 (OpenSSL 3.5.9) and drop the unused libpython dylib - #843

Open
OAraLabs wants to merge 2 commits into
mainfrom
claude/pbs-3-12-15-d21e78
Open

OAraLabs wants to merge 2 commits into
mainfrom
claude/pbs-3-12-15-d21e78

Conversation

@OAraLabs

Copy link
Copy Markdown
Owner

Why

The Mac app bundles its own CPython from python-build-standalone. Main still pins 20260325 / CPython 3.12.13 (OpenSSL 3.5.5), so the next tag, or any run of the release-macos job, would ship that old interpreter. The v0.9.16 draft was rebuilt by hand with a local edit. This PR makes the same change at the source.

CPython 3.12.15 (python.org, released 2026-09-30) fixes:

  • CVE-2026-19445 / gh-156293: use-after-free of a server-side SSLContext when sni_callback switches contexts
  • CVE-2026-19553 / gh-156793: SSLContext.wrap_bio() skips validating server_hostname
  • tarfile: CVE-2026-82049 / gh-157190, CVE-2026-19672 / gh-155999, CVE-2026-87910 / gh-157265 (ways around the extraction filter)
  • zipfile: CVE-2026-15310 / gh-156002 (memory exhaustion when decompressing bzip2, LZMA or Zstandard)
  • urllib: CVE-2026-15806 / gh-155694 (HTTPPasswordMgr sent credentials meant for one URL scheme over another)
  • Expat updated to 2.8.5, plus StringPrep (CVE-2026-17084)

The bundled OpenSSL goes from 3.5.5 to 3.5.9.

What changes

  1. packaging/macos/build_app.py: PBS_RELEASE 20260325 → 20261021, PBS_PYTHON 3.12.13 → 3.12.15, PBS_SHA256 → 7b8d00b7…a219. The new sha matches three independent sources:

    • the SHA256SUMS entry of release 20261021 for cpython-3.12.15+20261021-aarch64-apple-darwin-install_only_stripped.tar.gz
    • GitHub's asset digest for that file (25,017,408 bytes)
    • a local shasum -a 256 of the downloaded file

    docs/design/macos-app-installer.md now says 3.12.15. Nothing else in the repo named the old pin.

  2. The build drops libpython3.12.dylib, which nothing loads (its own commit).

    • In 20260325, bin/python3.12 was a 49 KB stub that loaded @rpath/libpython3.12.dylib (rpath @executable_path/../lib).
    • In 20261021, bin/python3.12 is an 18 MB executable with libpython linked in statically. otool -L lists only CoreFoundation, libSystem, libz, libedit, ncurses, panel and SystemConfiguration.
    • The 18 MB shared library was still shipped and signed, but nothing used it.

    How unused was established, on a built bundle:

    • Load commands: otool -L on all 74 Mach-O files (the interpreter, every .so/.dylib, and the Swift launcher Contents/MacOS/Prometheus) finds no reference to libpython. Every @loader_path/@rpath dependency is one of Pillow's own .dylibs.
    • Embedded strings: the string libpython appears in no binary except the dylib itself.
    • Launcher: it has no dlopen and execs only python/bin/python3.12.
    • Python code: in the stdlib and site-packages, libpython is named only in _sysconfigdata (a build-time config var) and in ctypes' Cygwin-only branch. On macOS, ctypes.pythonapi is PyDLL(None), and it resolves from the static executable.

    prune_python now removes the dylib. A new require_not_linked check runs after pruning: if any remaining Mach-O still links libpython, the build fails. So a future python-build-standalone release that goes back to a stub interpreter cannot ship an app that won't start. Tests cover both the prune and the check (the stub's otool -L output is refused, the static one passes).

Measured (local, unnotarized, 0.9.16, default extras)

zip (download) installed files Mach-O
3.12.15, dylib kept 78,965,108 B 232,893,388 B 8,234 74
3.12.15, dylib pruned (this PR) 71,475,053 B 214,740,607 B 8,233 73

For comparison, the v0.9.16 app built on 3.12.13 was 71,410,020 B, and the notarized 3.12.15 rebuild was 78,965,277 B. With the prune, the interpreter bump costs about 65 KB of download.

Verified on the pruned bundle

  • verify_app.py on the zip (not notarized, so without --notarized): OK. Same result for the dylib-kept build.
  • The bundled python3.12 -I reports 3.12.15 and OpenSSL 3.5.9. These imports succeed: prometheus and prometheus.web.server (0.9.16), fastapi, uvicorn, pypdfium2, PIL (with _imaging), cryptography (including Ed25519 key generation), pydantic/pydantic_core, ssl (create_default_context), sqlite3 (3.53.1) and ctypes.pythonapi.
  • Contents/MacOS/Prometheus --status answers {"ok":true,"state":"not_registered",...}.
  • The daemon boots with the launcher's exact argv (python3.12 -I -B -m prometheus daemon), a throwaway HOME and a loopback stub model. GET /api/status returns 200, a SIGTERM stops it with exit 0, and the log has no traceback.
  • tests/test_macos_app_build.py and the other macOS packaging tests: 198 passed. Ruff is clean.

Not done here: a notarized build of this commit (the release job does that) and a run on a clean Mac.

🤖 Generated with Claude Code

OAraLabs and others added 2 commits October 22, 2026 16:34
The Mac app's interpreter moves from python-build-standalone 20260325 /
CPython 3.12.13 (OpenSSL 3.5.5) to 20261021 / CPython 3.12.15 (OpenSSL
3.5.9), for the 3.12.15 security fixes (CVE-2026-19445, CVE-2026-19553).

PBS_SHA256 equals the release's SHA256SUMS entry for the aarch64
install_only_stripped asset, GitHub's asset digest, and a local hash of
the download.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
python-build-standalone 20261021 links libpython into bin/python3.12
statically (otool -L: CoreFoundation, libSystem, libz, libedit, ncurses,
panel, SystemConfiguration); the 20260325 interpreter was a 49 KB stub
that loaded @rpath/libpython3.12.dylib. In a built bundle no Mach-O (the
interpreter, every .so/.dylib, the Swift launcher) has a load command
naming it, and the launcher never dlopens anything, so the 18 MB shared
library was shipped and signed for nothing.

prune_python now removes it, and require_not_linked fails the build if
any remaining Mach-O links it, so a release that goes back to a stub
interpreter cannot ship one that does not start.

Measured on an unnotarized local build of 0.9.16: zip 78,965,108 ->
71,475,053 bytes, installed 232,893,388 -> 214,740,607 bytes, 74 -> 73
Mach-O.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant