The orchardprobe_core::ipa_catalog module combines the exact root and nested
bundle declarations with a deliberately bounded lowercase .dylib convention.
It streams every selected non-root candidate through the secured entry-copy
boundary and calls an entry code only after the bounded Mach-O parser succeeds.
Important
The stable coverage value is declared_standard_bundles. It covers the root
app, the framework and direct extension shapes resolved by
ipa_bundle, and lowercase dylibs in that same closed
ancestry. It does not claim arbitrary app-code completeness: nested .app,
Watch/App Clip content, unsupported bundle types, and executable-looking
resources remain outside the result. No current CLI accepts an IPA, and this
layer does not connect to a device, decrypt or rewrite bytes, package an IPA,
validate signatures, or prove plaintext.
inspect_ipa_code_inventory(reader, archive_size)
-> Result<IpaCodeInventory, IpaCodeInventoryError>IpaCodeInventory returns:
coverage: declared_standard_bundles;- the validated root
appmetadata; - canonical-path-sorted
nested_bundles, including the exact declarations used to select framework and extension executables; - canonical-path-sorted Mach-O-confirmed
binaries; and - canonical-path-sorted
rejected_candidatesfor selected non-root entries that cannot be classified as code.
Each code object preserves its exact archive path, semantic role, validated
entry sizes and CRC, and bounded MachOReport. A report's encryption metadata
does not prove plaintext; every slice remains not_proven without stronger
independent evidence.
All decisions use case-sensitive canonical inventory strings. No archive path is interpreted as a host path.
- The root main executable is the exact regular entry declared by the root
Info.plist. It is mandatory and always hasmain_executableprecedence. - Every in-scope nested framework or direct
PlugIns/*.appexcontributes the exact executable resolved from its directInfo.plist. A framework inside that extension is also in scope. - A regular entry whose final filename ends in lowercase
.dylibis selected only when its ancestry stays within the closed rules below. - Exact nested declarations override the
.dylibconvention for the same path. A declaredRenamed.dylibtherefore retainsframeworkorextension, neverdynamic_library. - A conventional bundle-stem file is not guessed. If
Kit.frameworkdeclaresWorker, a siblingKit.framework/Kitis neither confirmed nor rejected by this inventory unless another in-scope rule selects it. - Exact paths are deduplicated before count and aggregate-byte checks.
An otherwise valid lowercase .dylib path may be beneath the root app and may
optionally be beneath exactly one direct PlugIns/<non-empty>.appex. It may
traverse at most one non-empty .framework component.
The convention excludes a path containing:
- any component ending in
.app, including empty-stem, normal Watch, and App Clip app shapes; - an
.appexanywhere other than the directPlugIns/<name>.appexposition; - an empty-stem
.appexor.framework, or more than one.frameworkcomponent; or - an uppercase or mixed-case suffix such as
.DYLIB.
These exclusions are coverage boundaries, not claims that omitted bytes are safe or non-code.
- Inspect the exact root main executable and retain its complete IPA inventory as the single authoritative observation.
- Resolve all nested bundle plists against that same inventory. Missing, directory, unsafe, oversized, unreadable, or malformed declarations fail before any optional code-candidate payload is copied.
- Merge in-scope lowercase dylibs, exact nested declarations, and the root declaration in increasing precedence order.
- Reject more than 256 deduplicated paths and checked aggregate compressed or uncompressed totals above 8 GiB before optional candidate reads.
- Process non-root candidates sequentially through one automatically cleaned anonymous temporary file. Each copy repeats complete IPA preflight, reaches EOF for CRC, enforces observed length, and returns a complete inventory that must equal the authoritative one.
- Parse the temporary file with the bounded thin/FAT Mach-O parser. Sort confirmed and rejected paths independently before returning.
The source IPA is never modified. Candidate temporary bytes remain local but can reach host storage or swap, so the host and its temporary storage are part of the trusted analysis environment.
| Limit | Value |
|---|---|
| Distinct deduplicated candidates including main | 256 |
| Aggregate declared compressed candidate bytes | 8 GiB |
| Aggregate declared uncompressed candidate bytes | 8 GiB |
| One candidate compressed bytes | 512 MiB |
| One candidate uncompressed bytes | 512 MiB |
| Streaming transfer memory | 64 KiB |
| Concurrent candidate temporary files | 1 |
Nested plist count, byte, parser-event, collection, and string limits remain those documented in the nested-bundle contract.
A selected non-root path produces one visible rejection when:
entry_too_large: its declared compressed or uncompressed size exceeds the per-entry streaming profile;not_macho: the copied entry is shorter than a Mach-O magic or has an unsupported magic; orinvalid_macho: the entry starts as recognized Mach-O/FAT but fails bounded structural parsing.
Root app, root main, nested metadata, count, aggregate arithmetic, temporary file, bounded copy, CRC, compression, or complete-inventory failures stop the whole call. Root main Mach-O failure is also fatal. Returning a result means only that this declared standard-bundle scope was handled deterministically; it does not mean every executable in an arbitrary app was discovered.
Synthetic in-memory IPA tests cover deterministic repeated output, every role,
nonstandard declarations, a declared executable ending in .dylib, precedence,
conventional-stem exclusion, direct-extension and nested-framework behavior,
standalone dylibs, Watch/nested-app/out-of-scope-extension/multiply-nested-
framework exclusions, visible non-Mach-O and invalid-Mach-O rejections, count,
aggregate and per-entry limits, malformed or missing nested metadata, inventory
mismatch, and malformed archive propagation. Lower IPA, plist, entry-copy, and
Mach-O adversarial suites retain the detailed CRC, compression, parser, path,
FAT, load-command, and arithmetic checks.
Run the focused tests with:
cargo test -p orchardprobe-core ipa_catalog::tests --lockedThe later private-worktree and deterministic-package stages remain independent
from declaration discovery. HOST-010 reconnects them only through the
device-free package-evidence builder, which reproduces
this complete inventory before binding per-code source/output hashes.