diff --git a/helpers/scanner/client.js b/helpers/scanner/client.js index 795a10b..61c3d7a 100644 --- a/helpers/scanner/client.js +++ b/helpers/scanner/client.js @@ -5,7 +5,7 @@ import prettyBytes from 'pretty-bytes' import FileApi, { File } from 'file-api' import parseMacho from '~/helpers/macho/index.js' - +import { isString } from '~/helpers/check-types.js' // For some reason inline 'import()' works better than 'import from' // https://gildas-lormeau.github.io/zip.js/ @@ -33,11 +33,18 @@ export class AppScan { this.bundleFileEntries = [] this.infoPlist = {} this.machoExcutables = [] - this.machoMeta = {} + // Data that is derived after we've read the files and pulled out the infoPlist + this.appVersion = '' + this.displayName = '' + this.details = [] this.bundleExecutable = null this.displayBinarySize = '' this.binarySize = 0 + this.machoMeta = {} + this.binarySupportsNative = undefined + + this.info = {} } sendMessage ( details ) { @@ -58,6 +65,10 @@ export class AppScan { return Object.keys( this.machoMeta ).length > 0 } + get hasInfo () { + return Object.keys( this.info ).length > 0 + } + get bundleExecutablePath () { if ( !this.hasInfoPlist ) return '' @@ -143,6 +154,22 @@ export class AppScan { }) } + classifyBinaryEntryArchitecture ( binaryEntry ) { + // Find an ARM Architecture + const armArchitecture = binaryEntry.architectures.find( architecture => { + // if ( architecture.processorType === 0 ) return false + + // If processorType not a string + // then return false + if ( !isString(architecture.processorType) ) return false + + return architecture.processorType.toLowerCase().includes('arm') + }) + + // Was an ARM Architecture found + return (armArchitecture !== undefined) + } + matchesMachoExecutable ( entry ) { // Skip files that are deeper than 3 folders if ( entry.filename.split('/').length > 4 ) return false @@ -220,6 +247,31 @@ export class AppScan { }) } + storeResultInfo () { + this.info = { + filename: this.file.name, + appVersion: this.appVersion, + result: this.binarySupportsNative ? '✅' : '🔶', + machoMeta: { + ...this.machoMeta, + file: undefined, + architectures: this.machoMeta.architectures.map( architecture => { + return { + bits: architecture.bits, + fileType: architecture.fileType, + header: architecture.header, + loadCommandsInfo: architecture.loadCommandsInfo, + magic: architecture.magic, + offset: architecture.offset, + processorSubType: architecture.processorSubType, + processorType: architecture.processorType, + } + }) + }, + infoPlist: this.infoPlist, + } + } + storeMachoMeta = async ( fileEntry ) => { // Throw if we have more than one target file if ( this.hasMachoMeta ) { @@ -243,9 +295,6 @@ export class AppScan { } - - - targetFiles = { rootInfoPlist: { method: this.storeInfoPlist @@ -291,6 +340,26 @@ export class AppScan { // Now that we have the info.plist Determine our entry Macho Executable from the list of Macho Executables + this.appVersion = this.infoPlist.CFBundleShortVersionString + this.displayName = this.infoPlist.CFBundleDisplayName + + // We loop through possible details and add them to the details array + // if they are not empty + ;([ + [ 'Version', this.infoPlist.CFBundleShortVersionString ], + [ 'Bundle Identifier', this.infoPlist.CFBundleIdentifier ], + [ 'File Mime Type', this.file.type ], + [ 'Copyright', this.infoPlist.NSHumanReadableCopyright ], + // [ 'Version', info.CFBundleShortVersionString ], + ]).forEach( ([ label, value ]) => { + if ( !value || value.length === 0 ) return + + this.details.push({ + label, + value, + }) + } ) + // Set the bundleExecutable this.bundleExecutable = this.findMainExecutable() @@ -301,6 +370,8 @@ export class AppScan { await this.storeMachoMeta( this.bundleExecutable ) + + this.binarySupportsNative = this.classifyBinaryEntryArchitecture( this.machoMeta ) } async start () { @@ -323,6 +394,8 @@ export class AppScan { await this.findTargetFiles() + this.storeResultInfo() + this.sendMessage({ message: '🏁 Scan complete', status: 'complete' diff --git a/test/scanner/client.test.js b/test/scanner/client.test.js index 2b3a7fa..a57ac3e 100644 --- a/test/scanner/client.test.js +++ b/test/scanner/client.test.js @@ -79,6 +79,32 @@ describe.concurrent('Apps', async () => { expect( scan.hasMachoMeta ).toBe( true ) }) + + it( `Can provide scan info for ${ appName } bundle`, () => { + // console.log( 'machoMeta', scan.machoMeta ) + + expect( scan.hasInfo ).toBe( true ) + + // Expect the scan info to have a bundle name that matches the app name + expect( scan.info.filename ).toContain( appName ) + + // Expect app version is string + expect( typeof scan.info.appVersion ).toBe( 'string' ) + + // Expect that machoMeta is an object + expect( typeof scan.info.machoMeta ).toBe( 'object' ) + + // Expect that machoMeta.architectures is an array + expect( Array.isArray( scan.info.machoMeta.architectures ) ).toBe( true ) + + // Expect that first of machoMeta.architectures has processorSubType as string + expect( typeof scan.info.machoMeta.architectures[0].processorSubType ).toBe( 'string' ) + + // Export info.infoPlist to be none empty object + expect( typeof scan.info.infoPlist ).toBe( 'object' ) + expect( Object.keys( scan.info.infoPlist ).length ).toBeGreaterThan( 0 ) + + }) } })