package file_test import ( "archive/zip" "os" "path/filepath" "testing" "github.com/fleetdm/fleet/v4/pkg/file" "github.com/fleetdm/fleet/v4/server/fleet" "github.com/stretchr/testify/require" ) func infoPlist(bundleID string, iOS bool) string { requiresIPhoneOS := "" if iOS { requiresIPhoneOS = "LSRequiresIPhoneOS" } return ` CFBundleIdentifier` + bundleID + ` CFBundleNameTest CFBundleShortVersionString1.0 ` + requiresIPhoneOS + ` ` } // writeZip builds a zip at a temp path with the given entries in order. func writeZip(t *testing.T, entries [][2]string) string { t.Helper() path := filepath.Join(t.TempDir(), "pkg.zip") f, err := os.Create(path) require.NoError(t, err) defer f.Close() zw := zip.NewWriter(f) for _, e := range entries { w, err := zw.Create(e[0]) require.NoError(t, err) _, err = w.Write([]byte(e[1])) require.NoError(t, err) } require.NoError(t, zw.Close()) return path } func TestExtractZIPMetadata(t *testing.T) { // a valid ipa returns metadata without error tfr, err := fleet.NewKeepFileReader(filepath.Join("testdata", "software-installers", "ipa_test.ipa")) require.NoError(t, err) defer tfr.Close() meta, err := file.ExtractZIPMetadata(tfr) require.NoError(t, err) require.NotNil(t, meta) // a zip-based package with no Info.plist at all is not an ipa. This has the // same magic bytes as a Windows .zip installer and likewise has no Info.plist, // so it covers that case too; we use a real .msix here. msixTfr, err := fleet.NewKeepFileReader(filepath.Join("testdata", "software-installers", "msix_test.msix")) require.NoError(t, err) defer msixTfr.Close() meta, err = file.ExtractZIPMetadata(msixTfr) require.ErrorIs(t, err, file.ErrInvalidType) require.Nil(t, meta) // the same msix renamed to a .msi extension still returns invalid type obfuscatedPath := filepath.Join(t.TempDir(), "not-really-an.msi") require.NoError(t, file.Copy(filepath.Join("testdata", "software-installers", "msix_test.msix"), obfuscatedPath, 0o644)) obfuscatedTfr, err := fleet.NewKeepFileReader(obfuscatedPath) require.NoError(t, err) defer obfuscatedTfr.Close() meta, err = file.ExtractZIPMetadata(obfuscatedTfr) require.ErrorIs(t, err, file.ErrInvalidType) require.Nil(t, meta) // a macOS .app zip has an Info.plist but no LSRequiresIPhoneOS, so it is not an ipa macTfr, err := fleet.NewKeepFileReader(writeZip(t, [][2]string{ {"MacApp.app/Contents/Info.plist", infoPlist("com.example.mac", false)}, })) require.NoError(t, err) defer macTfr.Close() meta, err = file.ExtractZIPMetadata(macTfr) require.ErrorIs(t, err, file.ErrInvalidType) require.Nil(t, meta) // once LSRequiresIPhoneOS is seen it stays set, so a framework plist without // the key coming after the app plist doesn't undo ipa detection latchTfr, err := fleet.NewKeepFileReader(writeZip(t, [][2]string{ {"Payload/App.app/Info.plist", infoPlist("com.example.ios", true)}, {"Payload/App.app/Frameworks/Bar.framework/Info.plist", infoPlist("com.example.framework", false)}, })) require.NoError(t, err) defer latchTfr.Close() meta, err = file.ExtractZIPMetadata(latchTfr) require.NoError(t, err) require.NotNil(t, meta) }