diff --git a/src/domain/apple/appleApplication.test.ts b/src/domain/apple/appleApplication.test.ts index 00b365ef39..23eb46b460 100644 --- a/src/domain/apple/appleApplication.test.ts +++ b/src/domain/apple/appleApplication.test.ts @@ -600,6 +600,35 @@ describe("Apple application bridge candidates", () => { status: "complete", }); }); + + it("keeps an extensionless Mach-O without an application root as a native component", () => { + const result = project( + inventoryEvidence("ipa", "Fixture.ipa", [ + { path: "Payload/Fixture.app/Info.plist" }, + macho("Payload/Fixture.app/Fixture"), + { path: "SwiftSupport/outside.js", format: "javascript-bundle" }, + macho("SwiftSupport/HelperTool"), + macho("SwiftSupport/iphoneos/libswiftCore.dylib"), + ]), + ); + + expect(result.components.executables.map(({ path }) => path)).toEqual([ + "Payload/Fixture.app/Fixture", + "SwiftSupport/HelperTool", + ]); + expect(result.bridge_candidates).toEqual([ + { + source_path: "SwiftSupport/outside.js", + native_path: "SwiftSupport/HelperTool", + basis: "javascript-and-native-content", + }, + { + source_path: "SwiftSupport/outside.js", + native_path: "SwiftSupport/iphoneos/libswiftCore.dylib", + basis: "javascript-and-native-content", + }, + ]); + }); }); describe("Apple path conventions", () => { diff --git a/src/domain/apple/appleApplication.ts b/src/domain/apple/appleApplication.ts index 587eb5a027..7911559e2c 100644 --- a/src/domain/apple/appleApplication.ts +++ b/src/domain/apple/appleApplication.ts @@ -249,6 +249,9 @@ const appleRootFormat = (format: string): RootFormat => { return root; }; +const isNativeLibrary = (path: string): boolean => + /\.(?:dylib|so)$/iu.test(path); + const classifyComponents = ( entries: readonly AppleInventoryEntry[], all: readonly Component[], @@ -271,11 +274,13 @@ const classifyComponents = ( bundle_metadata: all.filter(({ path }) => anatomyNameIs(lastSegment(path), "Info.plist"), ), + // An unrooted IPA library is listed only under native_libraries. executables: all.filter( - ({ path, format }) => withinApp(path) && isMachOFormat(format), + ({ path, format }) => + isMachOFormat(format) && (withinApp(path) || !isNativeLibrary(path)), ), frameworks: all.filter(({ path }) => /\.framework\//u.test(path)), - native_libraries: all.filter(({ path }) => /\.(?:dylib|so)$/iu.test(path)), + native_libraries: all.filter(({ path }) => isNativeLibrary(path)), javascript: all.filter(({ format }) => format === "javascript-bundle"), signing: all.filter( ({ path }) =>