@@ -123,36 +123,102 @@ function verifyFileList(files, packageType) {
123123 } ;
124124}
125125
126+ // Minimum expected AppImage file size (50 MB)
127+ const APPIMAGE_MIN_SIZE_MB = 50 ;
128+
126129/**
127- * Verify AppImage contents using bsdtar (libarchive)
130+ * Verify AppImage contents.
131+ * AppImages are ELF executables with an embedded SquashFS filesystem.
132+ * We try unsquashfs first (can list SquashFS contents), then fall back
133+ * to the AppImage's own --appimage-extract, and finally to a size check.
128134 */
129135function verifyAppImage ( appImagePath ) {
130136 logInfo ( `Verifying AppImage: ${ path . basename ( appImagePath ) } ` ) ;
131137
132- if ( ! commandExists ( 'bsdtar' ) ) {
133- logWarning ( 'bsdtar not found. Install with: sudo apt-get install libarchive-tools' ) ;
134- logWarning ( 'Skipping AppImage verification' ) ;
135- return { verified : false , reason : 'bsdtar not available' , critical : true } ;
138+ // Try unsquashfs -l (lists squashfs contents without extracting)
139+ if ( commandExists ( 'unsquashfs' ) ) {
140+ const result = spawnSync ( 'unsquashfs' , [ '-l' , appImagePath ] , {
141+ stdio : 'pipe' ,
142+ encoding : 'utf-8' ,
143+ maxBuffer : 50 * 1024 * 1024 ,
144+ } ) ;
145+
146+ if ( result . error ) {
147+ logWarning ( `unsquashfs failed: ${ result . error . message } , falling back to size check` ) ;
148+ } else if ( result . status !== 0 ) {
149+ logWarning ( `unsquashfs could not read AppImage, falling back to size check` ) ;
150+ } else {
151+ const files = result . stdout . split ( '\n' ) ;
152+ return verifyFileList ( files , 'AppImage' ) ;
153+ }
136154 }
137155
138- const result = spawnSync ( 'bsdtar' , [ '-t' , '-f' , appImagePath ] , {
156+ // Try self-extraction to list contents (AppImages support --appimage-extract-and-run)
157+ // Make the AppImage executable first
158+ try {
159+ fs . chmodSync ( appImagePath , 0o755 ) ;
160+ } catch ( _ ) {
161+ // Ignore chmod errors
162+ }
163+
164+ const extractResult = spawnSync ( appImagePath , [ '--appimage-extract' , '--stdout' ] , {
139165 stdio : 'pipe' ,
140166 encoding : 'utf-8' ,
141167 maxBuffer : 50 * 1024 * 1024 ,
168+ timeout : 30000 ,
169+ env : { ...process . env , APPIMAGE_EXTRACT_AND_RUN : '1' } ,
142170 } ) ;
143171
144- if ( result . error ) {
145- logError ( `Failed to execute bsdtar: ${ result . error . message } ` ) ;
146- return { verified : false , issues : [ `Command execution failed: ${ result . error . message } ` ] } ;
172+ // --appimage-extract creates a squashfs-root directory; check if it exists
173+ const squashfsRoot = path . join ( path . dirname ( appImagePath ) , 'squashfs-root' ) ;
174+ if ( fs . existsSync ( squashfsRoot ) ) {
175+ try {
176+ const collectFiles = ( dir , prefix = '' ) => {
177+ const entries = [ ] ;
178+ for ( const entry of fs . readdirSync ( dir , { withFileTypes : true } ) ) {
179+ const rel = prefix ? `${ prefix } /${ entry . name } ` : entry . name ;
180+ entries . push ( rel ) ;
181+ if ( entry . isDirectory ( ) ) {
182+ entries . push ( ...collectFiles ( path . join ( dir , entry . name ) , rel ) ) ;
183+ }
184+ }
185+ return entries ;
186+ } ;
187+ const files = collectFiles ( squashfsRoot ) ;
188+ const verifyResult = verifyFileList ( files , 'AppImage' ) ;
189+ // Clean up extracted directory
190+ fs . rmSync ( squashfsRoot , { recursive : true , force : true } ) ;
191+ return verifyResult ;
192+ } catch ( e ) {
193+ logWarning ( `Failed to read extracted AppImage contents: ${ e . message } ` ) ;
194+ fs . rmSync ( squashfsRoot , { recursive : true , force : true } ) ;
195+ }
147196 }
148197
149- if ( result . status !== 0 ) {
150- logError ( `Failed to read AppImage: ${ result . stderr } ` ) ;
151- return { verified : false , issues : [ 'Failed to extract file list' ] } ;
198+ // Fall back to basic size validation (same approach as Flatpak)
199+ logWarning ( 'Could not inspect AppImage contents (unsquashfs not available). Using size validation.' ) ;
200+ const issues = [ ] ;
201+ const stats = fs . statSync ( appImagePath ) ;
202+
203+ if ( stats . size === 0 ) {
204+ return { verified : false , issues : [ 'AppImage file is empty' ] } ;
152205 }
153206
154- const files = result . stdout . split ( '\n' ) ;
155- return verifyFileList ( files , 'AppImage' ) ;
207+ if ( stats . size < APPIMAGE_MIN_SIZE_MB * 1024 * 1024 ) {
208+ issues . push (
209+ `AppImage file seems too small (${ ( stats . size / 1024 / 1024 ) . toFixed ( 2 ) } MB, expected at least ${ APPIMAGE_MIN_SIZE_MB } MB)` ,
210+ ) ;
211+ }
212+
213+ if ( issues . length === 0 ) {
214+ logInfo ( 'AppImage passed size validation (content inspection was not possible)' ) ;
215+ }
216+
217+ return {
218+ verified : issues . length === 0 ,
219+ issues,
220+ size : stats . size ,
221+ } ;
156222}
157223
158224/**
@@ -315,7 +381,7 @@ function main() {
315381 if ( hasCriticalSkips ) {
316382 log ( 'Some packages could not be verified due to missing required tools.\n' , colors . red ) ;
317383 log ( 'Install required tools:\n' , colors . red ) ;
318- log ( ' - bsdtar : sudo apt-get install libarchive -tools\n' , colors . red ) ;
384+ log ( ' - unsquashfs : sudo apt-get install squashfs -tools\n' , colors . red ) ;
319385 log ( ' - dpkg-deb: sudo apt-get install dpkg\n' , colors . red ) ;
320386 }
321387 process . exit ( 1 ) ;
0 commit comments