Find and use app info file

This commit is contained in:
Sam Carlton 2021-02-02 22:17:48 -06:00
parent 48522c7ddd
commit 2338aeceb1
2 changed files with 187 additions and 30 deletions

View file

@ -12,6 +12,9 @@
// import EndianReader from 'endian-reader' // import EndianReader from 'endian-reader'
import parseMacho from './macho/index.js' import parseMacho from './macho/index.js'
const plist = require('plist')
// console.log('MachOParser', MachOParser) // console.log('MachOParser', MachOParser)
const knownArchiveExtensions = new Set([ const knownArchiveExtensions = new Set([
@ -191,11 +194,50 @@ export default class AppFilesScanner {
return entries return entries
} }
findMachOEntries ( entries ) { matchesMacho ( entry ) {
// Skip files that are deeper than 3 folders
if ( entry.filename.split('/').length > 4 ) return false
// Create a new set to store and search App Names // Skip folders
if ( entry.filename.endsWith('/') ) return false
// `${ appName }.app/Contents/MacOS/${ appName }`
// Does this entry path match any of our wanted paths
return [
// `${ appName }.app/Contents/MacOS/${ appName }`
`.app/Contents/MacOS/`
].some( pathToMatch => {
return entry.filename.includes(pathToMatch)
})
}
matchesRootInfo ( entry ) {
// Skip files that are deeper than 2 folders
if ( entry.filename.split('/').length > 3 ) return false
// Skip folders
if ( entry.filename.endsWith('/') ) return false
// Does this entry path match any of our wanted paths
return [
// `zoom.us.app/Contents/Info.plist`
`.app/Contents/Info.plist`,
`.zip/Contents/Info.plist`
].some( pathToMatch => {
return entry.filename.endsWith(pathToMatch)
})
}
findEntries ( entries, matchersObject ) {
const matches = {}
// const matcherKeys = Object.keys( matchers )
// Create a new set to store found App Names
const appNamesInArchive = new Set() const appNamesInArchive = new Set()
// Search App Names in entries
entries.forEach( entry => { entries.forEach( entry => {
// Look through filename parts // Look through filename parts
entry.filename.split('/').forEach( filenamePart => { entry.filename.split('/').forEach( filenamePart => {
@ -205,25 +247,26 @@ export default class AppFilesScanner {
appNamesInArchive.add( appName ) appNamesInArchive.add( appName )
} }
} ) } )
for ( const key in matchersObject ) {
// Deos it match the matcher method
const entryMatches = matchersObject[key]( entry )
if ( entryMatches ) {
// If we haven't set up an array for this key
// then create one
if ( !Array.isArray(matches[key]) ) matches[key] = []
// Push this entry to our matching list
matches[key].push( entry )
}
}
} ) } )
// Return any entries that match Mach-o file paths return matches
return entries.filter( entry => {
let matchesMachOPath = false
// Match possible Mach-o names against this entries' filename
appNamesInArchive.forEach( appName => {
const possibleMachOPath = `${ appName }.app/Contents/MacOS/${ appName }`
// Check if this possible Mach-o path is contained within this entry's filename
if ( entry.filename.includes( possibleMachOPath ) ) {
matchesMachOPath = true
}
})
return matchesMachOPath
})
} }
async parseMachOBlob ( machOBlob, fileName ) { async parseMachOBlob ( machOBlob, fileName ) {
@ -246,7 +289,11 @@ export default class AppFilesScanner {
Array.from(fileList).forEach( (fileInstance, index) => { Array.from(fileList).forEach( (fileInstance, index) => {
this.files.unshift( { this.files.unshift( {
status: 'loaded', status: 'loaded',
displayName: null,
statusMessage: '⏳ File Loaded and Queud', statusMessage: '⏳ File Loaded and Queud',
details: [],
appVersion: null,
name: fileInstance.name, name: fileInstance.name,
size: fileInstance.size, size: fileInstance.size,
type: fileList.item( index ).type, type: fileList.item( index ).type,
@ -256,7 +303,6 @@ export default class AppFilesScanner {
} ) } )
}) })
// Scan for archives // Scan for archives
await Promise.all( this.files.map( async (file, index) => { await Promise.all( this.files.map( async (file, index) => {
@ -269,7 +315,7 @@ export default class AppFilesScanner {
// console.log('file', file) // console.log('file', file)
await new Promise(r => setTimeout(r, 1000 * index)) await new Promise(r => setTimeout(r, 1500 * index))
file.statusMessage = '🗃 Decompressing file' file.statusMessage = '🗃 Decompressing file'
@ -289,10 +335,23 @@ export default class AppFilesScanner {
file.statusMessage = '👀 Scanning App Files' file.statusMessage = '👀 Scanning App Files'
file.machOEntries = this.findMachOEntries( entries ) const foundEntries = this.findEntries( entries, {
macho: this.matchesMacho,
rootInfo: this.matchesRootInfo
})
// Clean out entries now that we're done with them
entries = undefined
// console.log('foundEntries', foundEntries)
// file.machOEntries = this.findMachOEntries( entries )
file.machOEntries = foundEntries.macho
// If no Macho files were found
// then report and stop
if ( file.machOEntries.length === 0 ) { if ( file.machOEntries.length === 0 ) {
console.log('file.machOEntries', file.machOEntries) console.log(`No Macho files found for ${file.name}`, file.machOEntries)
file.statusMessage = `❔ Unkown app format` file.statusMessage = `❔ Unkown app format`
file.status = 'finished' file.status = 'finished'
@ -300,10 +359,93 @@ export default class AppFilesScanner {
return return
} }
// findPlistFile
// Maybe next time
// const matchesInfoPlist = path => path.endsWith(`/Info.plist`)
// const matchesRootInfoPlist = entryPath => {
// // Skip files that are deeper than 2 folders
// if ( entryPath.split('/').length > 3 ) return false
// // Does this entry path match any of our wanted paths
// return [
// // `zoom.us.app/Contents/Info.plist`
// `.app/Contents/Info.plist`,
// `.zip/Contents/Info.plist`
// ].some( pathToMatch => {
// return entryPath.endsWith(pathToMatch)
// })
// }
// const rootInfoFileEntries = entries.filter( entry => {
// return matchesRootInfoPlist( entry.filename )
// })
// Warn if Info.plist doesn't look right
if ( foundEntries.rootInfo.length > 1) {
console.warn('More than one root Info.plist found', foundEntries.rootInfo)
} else if ( foundEntries.rootInfo.length === 0 ) {
console.warn('No root Info.plist found', foundEntries.rootInfo)
}
// Break out root entry into a variable
const [ rootInfoEntry ] = foundEntries.rootInfo
// Get blob data from zip
const infoXml = await rootInfoEntry.getData(
// writer
// https://gildas-lormeau.github.io/zip.js/core-api.html#zip-writing
new zip.TextWriter(),
// options
{
useWebWorkers: true,
// onprogress: (index, max) => {
// const percentageNumber = (index / max * 100)
// // onprogress callback
// console.log(`Writer progress ${percentageNumber}`)
// }
}
)
// console.log('infoXml', infoXml)
// Parse the Info.plist data
const info = plist.parse( infoXml )
file.appVersion = info.CFBundleShortVersionString
file.displayName = info.CFBundleDisplayName
const detailsData = [
[ 'Version', info.CFBundleShortVersionString ],
[ 'Bundle Identifier', info.CFBundleIdentifier ],
[ 'File Mime Type', file.type ],
[ 'Copyright', info.NSHumanReadableCopyright ],
// [ 'Version', info.CFBundleShortVersionString ],
]
detailsData.forEach( ([ label, value ]) => {
if ( !value || value.length === 0 ) return
file.details.push({
label,
value,
})
} )
// console.log('infoFiles', file.name, {
// path: rootInfoEntry.filename,
// info
// })
// const machOBlob = await file.machOEntries // const machOBlob = await file.machOEntries
const parsedMachoEntries = await Promise.all( file.machOEntries.map( async machOEntry => { const parsedMachoEntries = await Promise.all( file.machOEntries.map( async machOEntry => {
// console.log('Parsing ', machOEntry.filename)
// Get blob data from zip // Get blob data from zip
const machOBlob = await machOEntry.getData( const machOBlob = await machOEntry.getData(
// writer // writer
@ -312,10 +454,12 @@ export default class AppFilesScanner {
new zip.BlobWriter(), new zip.BlobWriter(),
// options // options
{ {
onprogress: (index, max) => { useWebWorkers: true,
// onprogress callback // onprogress: (index, max) => {
console.log('Writer progress', index, max) // const percentageNumber = (index / max * 100)
} // // onprogress callback
// console.log(`Writer progress ${percentageNumber}`)
// }
} }
) )

View file

@ -92,7 +92,7 @@
<!-- app.endpoint: {{ app.endpoint }} --> <!-- app.endpoint: {{ app.endpoint }} -->
<div <div
:class="[ :class="[
'flex flex-col justify-center inset-x-0 hover:bg-darkest border-2 border-white border-opacity-0 hover:border-opacity-50 focus:outline-none focus:bg-gray-50 duration-300 ease-in-out rounded-lg space-y-2 -mx-5 pl-5 md:pl-20 pr-6 md:pr-64 py-5', 'flex flex-col justify-center inset-x-0 hover:bg-darkest border-2 border-white border-opacity-0 hover:border-opacity-50 focus:outline-none focus:bg-gray-50 duration-300 ease-in-out rounded-lg space-y-3 -mx-5 pl-5 md:pl-20 pr-6 md:pr-64 py-5',
(appScan.status !== 'finished') ? 'shimmer' : '' (appScan.status !== 'finished') ? 'shimmer' : ''
]" ]"
style="transition-property: border;" style="transition-property: border;"
@ -101,11 +101,24 @@
<div class="absolute hidden left-0 h-12 w-12 rounded-full md:flex items-center justify-center bg-darker"> <div class="absolute hidden left-0 h-12 w-12 rounded-full md:flex items-center justify-center bg-darker">
{{ appScan.name.charAt(0) }} {{ appScan.name.charAt(0) }}
</div> </div>
{{ appScan.name }} <code>{{ appScan.type }}</code> {{ appScan.displayName || appScan.name }} {{ appScan.appVersion ? `- v${appScan.appVersion}` : '' }}
<div class="text-sm leading-5 font-bold"> <div class="text-sm leading-5 font-bold">
{{ appScan.statusMessage }} {{ appScan.statusMessage }}
</div> </div>
<details class="w-full pt-6">
<summary class="cursor-pointer mb-3">Details</summary>
<div>
<div v-if="appScan.details.length === 0">No details available</div>
<ul v-else>
<li
v-for="( detail ) in appScan.details"
:key="`${appScan.name}-detail-${detail.label}`"
><strong>{{ detail.label }}</strong> <span v-html="detail.value" /></li>
</ul>
</div>
</details>
</div> </div>
</li> </li>
@ -124,7 +137,7 @@
</details> </details>
<pre class="w-full">{{ appsBeingScanned }}</pre> <!-- <pre class="w-full">{{ appsBeingScanned }}</pre> -->
<AllUpdatesSubscribe <AllUpdatesSubscribe
:input-class-groups="{ :input-class-groups="{