Skip to content

Bump to 4.4.2: Xcode build-provenance keys in iOS framework Info.plists - #242

Merged
FeodorFitsner merged 4 commits into
mainfrom
bump-4.4.2
Jul 27, 2026
Merged

Bump to 4.4.2: Xcode build-provenance keys in iOS framework Info.plists#242
FeodorFitsner merged 4 commits into
mainfrom
bump-4.4.2

Conversation

@FeodorFitsner

Copy link
Copy Markdown
Contributor

Re-pins the bundled python-build snapshot to 20260727 and bumps all six packages to 4.4.2.

What's in the snapshot

flet-dev/python-build#36 only. Every framework generated from a lib-dynload .so_ssl, _hashlib and the rest — previously shipped ten hand-written Info.plist keys and not a single DT* key, so to Apple's static analysis the bundle didn't look like anything Xcode produced. They now carry the same key set Xcode stamps into a real framework:

BuildMachineOSBuild   DTPlatformName      DTSDKName      UIDeviceFamily
DTCompiler            DTPlatformVersion   DTXcode        UIRequiredDeviceCapabilities (device only)
DTPlatformBuild       DTSDKBuild          DTXcodeBuild

Also fixes the simulator slice, which claimed CFBundleSupportedPlatforms=iPhoneOS.

No versions moved — Python 3.12.13 / 3.13.14 / 3.14.6, Pyodide, and dart_bridge 1.6.1 are unchanged from 20260726. The manifest diff between the two releases is one line: the release field.

Status of flet-dev/flet#6724

This is the next hypothesis under test for ITMS-91065: Missing signature, not a confirmed fix, and the changelogs say so. Two candidates have now been ruled out experimentally:

  • A signature applied at the source cannot matter. Re-signing a vendor-signed framework the way exportArchive does destroys the certificate chain, team identifier and secure timestamp; only the identifier string survives, and that's derived from CFBundleIdentifier for an unsigned framework anyway.
  • Privacy manifest content cannot be the differentiator. A third-party OpenSSL xcframework the App Store accepts ships an empty stub, and our frameworks already carry a manifest — Apple emits no ITMS-91061 alongside the rejection.

Testing this needs a new app submitted for App Store review. The check only fires for new apps or updates that newly add a listed SDK, which is why existing Flet apps — including Flet Studio — are unaffected and can't reproduce it.

Verification

  • gen_version_tables --release-date 20260727 regenerated all five tables; the no-arg re-run produces no further diff.
  • Only the release date moved in the generated files.
  • No stale 4.4.1 in any pubspec, build.gradle.kts, or podspec.

Re-pins the bundled python-build snapshot to 20260727, which stamps the DT*
build-provenance keys Xcode writes into a real framework's Info.plist into
every framework generated from a lib-dynload .so, and labels the simulator
slice as iPhoneSimulator instead of iPhoneOS (flet-dev/python-build#36).

No versions moved: Python 3.12.13 / 3.13.14 / 3.14.6, Pyodide, and
dart_bridge 1.6.1 are all unchanged from 20260726.

This is the next hypothesis under test for the ITMS-91065 rejection in
flet-dev/flet#6724, not a confirmed fix — see the serious_python_darwin 4.4.2
entry for what was ruled out experimentally and what remains open.
Every Python C-extension ships as its own embedded framework, and each carried
a fixed `org.python.<module>` CFBundleIdentifier -- org.python.ssl,
org.python.hashlib, ... -- byte-identical in every app ever built with
serious_python.

A framework's bundle identifier also becomes its CODE SIGNING identifier, and
that is the one field that survives the `codesign -f` Xcode applies at embed
and again at exportArchive (everything else -- certificate chain, team
identifier, secure timestamp -- is destroyed). A globally-shared identifier on
a framework Apple fingerprints as a listed third-party SDK is the leading
explanation for the ITMS-91065 rejection in flet-dev/flet#6724.

Setting SERIOUS_PYTHON_BUNDLE_ID rewrites them to `<app id>.<module>` with
underscores mapped to hyphens, matching CPython's own iOS support
(Platforms/Apple/testbed/Python.xcframework/build/utils.sh). The leading hyphen
for underscore-prefixed modules (`_ssl` -> `<app>.-ssl`) is deliberate: it is
what keeps `_ssl` distinct from `ssl`, and it is the form CPython ships.

The rewrite runs before reconcile_framework_install_names, whose ad-hoc re-sign
reseals the modified plists; the stdlib xcframeworks are unsigned at that point
so editing them invalidates nothing. Module names come from arbitrary wheels,
so anything outside CFBundleIdentifier's [A-Za-z0-9.-] is mapped to a hyphen,
and an invalid SERIOUS_PYTHON_BUNDLE_ID leaves the defaults in place with a
warning rather than emitting a plist that fails late at export.

flet passes the value from flet-dev/flet#6731.
@FeodorFitsner

Copy link
Copy Markdown
Contributor Author

Added the bundle-identifier change (abb6dbd), per discussion.

What

New SERIOUS_PYTHON_BUNDLE_ID env var. When set, rewrite_framework_bundle_ids rewrites every generated framework's CFBundleIdentifier from org.python.<module> to <app bundle id>.<module>, underscores mapped to hyphens — matching CPython's own iOS support in Platforms/Apple/testbed/Python.xcframework/build/utils.sh:

_ssl                              -> com.example.myapp.-ssl
_hashlib                          -> com.example.myapp.-hashlib
PIL._imaging                      -> com.example.myapp.PIL.-imaging
matplotlib.backends._backend_agg  -> com.example.myapp.matplotlib.backends.-backend-agg

Runs in sync_site_packages.sh before reconcile_framework_install_names, whose ad-hoc re-sign reseals the modified plists. The stdlib xcframeworks copied in from python-build are unsigned at that point, so editing their plists invalidates nothing.

flet passes the value: flet-dev/flet#6731.

Why this is the stronger hypothesis

A framework's bundle identifier also becomes its code signing identifier, which is the only field that survives codesign -f at embed and again at exportArchive — certificate chain, team identifier and secure timestamp are all destroyed. And org.python.ssl is byte-identical in every app ever built with serious_python.

It's also the only theory that accounts for every outcome we found:

framework identifier kind outcome
com.example.app.-ssl (BeeWare) namespaced under the app no reports
com.github.krzyzanowskim.OpenSSL known third-party package reported passing
org.python.ssl (ours) independent, unknown ITMS-91065
partout-io's openssl.framework independent, unknown ITMS-91065

The leading hyphen is deliberate

77 of the 93 frameworks in a representative dist_ios produce one, because most C extensions are underscore-prefixed. It's what keeps _ssl distinct from ssl, and it's the exact form CPython and BeeWare ship — so stripping it would both reintroduce collisions and deviate from the known-good configuration.

Validation

Module names come from arbitrary wheels, so anything outside CFBundleIdentifier's [A-Za-z0-9.-] maps to a hyphen (we+irdwe-ird). An invalid SERIOUS_PYTHON_BUNDLE_ID leaves the defaults in place with a warning rather than emitting a plist that fails late at export with an error naming a framework the developer has never heard of:

''                     -> WARN, unchanged
'com.example..app'     -> WARN, unchanged
'com.exa mple.app'     -> WARN, unchanged
'com_example'          -> WARN, unchanged
'com.example.app.'     -> accepted (trailing dot stripped)

Exercised against real .xcframeworks from dist_ios; both slices rewritten, plutil -lint clean after.

Caveat

4.4.2 now carries two independent unproven hypotheses. If a new-app submission passes, we won't know which one did it. Flagged in the changelogs.

Python.xcframework is staged straight out of dist/xcframeworks by stage_spm.sh
and vendored from there by the podspec, so it never passes through
site-xcframeworks and kept a shared `org.python.python` -- identical in every
app, the same class of identifier the previous commit fixed for the extension
frameworks.

Verified inert at runtime before renaming: `org.python.python` appears in no
shipped binary, and neither Python.framework nor App.framework contains a
CFBundleGetBundleWithIdentifier / bundleWithIdentifier lookup. serious_python
sets PYTHONHOME explicitly and resolves extensions through the .fwork/.origin
path mechanism, not bundle identifiers.

rewrite_framework_bundle_ids takes an optional skip list so dart_bridge keeps
`dev.flet.dartbridge` -- a vendor-owned reverse-DNS identifier is the shape
we're aiming for, not the problem we're fixing.

Confirmed against a real build (flet playground abc1.xcarchive): 93 of 98
frameworks were already namespaced, no duplicate identifiers; this covers
Python.framework, leaving only Flutter's own three (App, Flutter, objective_c)
and dart_bridge.
jni 1.0.1, published 2026-07-27T01:10Z, added global_jni_env.c, which passes a
`va_list` where a `void *` is expected. On x86_64 va_list is an array type that
decays to a pointer so it compiles; on aarch64 it is a struct, so it is a hard
error and `flutter build linux` fails with 19 of them. Tracked upstream at
dart-lang/native#3498.

This surfaced on the release PR rather than as a scheduled failure because the
examples depend on the serious_python packages by path: bumping their version
invalidates the committed pubspec.lock, pub re-resolves the whole graph, and
picks up the newest transitive versions. Any PR touching package versions after
2026-07-27T01:10Z would have hit it. jni itself arrives via
path_provider -> path_provider_android -> jni_flutter -> jni.

jni_flutter 1.0.1 declares `jni: ^1.0.0`, so 1.0.0 satisfies it -- the override
narrows within the allowed range rather than overriding a constraint. Applied to
all three examples; only bridge_example runs in CI, but the other two break
identically for anyone building them on an ARM64 Linux host.

The flask_example / run_example locks also pick up the current path-dependency
version (4.0.0 -> 4.4.1); they were simply stale.
@FeodorFitsner
FeodorFitsner merged commit 2792729 into main Jul 27, 2026
26 of 56 checks passed
@FeodorFitsner
FeodorFitsner deleted the bump-4.4.2 branch July 27, 2026 20:05
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