Apple Platform updates for July 2025

I usually post this collection of links as part of the weekly MacAdmins.news summary, but that is currently on a slower bi-weekly “Summer Camp” schedule. So I am posting this here for a change and will link in the issue next week.

The dot-six updates, as is common are mostly bug fixes and security patches. The enterprise notes have a bit of relevant information, especially for macOS 15.6. (More so than the general “What’s new in” articles.

And with this, we say good-bye to macOS Ventura 13. Barring some terrible security vulnerability, this will be the last update for the macOS release with the poppy. (One of my favorite default desktop pictures.)

macOS

iOS and iPadOS

Other Platforms

Inspecting Packages

The macOS installation process installs a pkg file with root credentials. Because of this high level of privileges, it is essential for a Mac system administrator or security expert to be able to inspect the files and scripts.

macOS comes with several tools to work with package files. Most of them are command line tools. pkgutil lets you examine a pkg file and its contents before the actual installation. It also lets you inspect which packages and files have already been installed on a given system.

Installed Packages

You can use the pkgutil command to list packages that have been installed on the system.

$ pkgutil --pkgs

This will list all packages that have been installed on the system. On a freshly installed macOS 15.5 system the list is very short:

com.apple.files.data-template

But depending on the version of macOS and how long the system has been running the list may have hundreds of entries. You can use grep to filter the output, but pkgutil has its own filter option: (I ran this on a system with a few more things installed.)

> pkgutil --pkgs='com.scriptingosx.*'
com.scriptingosx.Installomator
com.scriptingosx.swift-prefs-tools
com.scriptingosx.utiluti
com.scriptingosx.desktoppr

Note that you need to quote the search term otherwise the shell will attempt to expand the wildcard.

Information for an Installed Package

pkgutil --pkgs lists the identifiers of the packages. Identifiers are chosen by developer, but should generally follow the “reverse DNS notation” scheme.

When properly used, identifiers allow the installer process to distinguish between new installations, upgrades and packages that have already been installed. There is another piece of information necessary to determine this and that is the version of a package. To get the version and other information on a specific package run

> pkgutil --info com.scriptingosx.desktoppr 
package-id: com.scriptingosx.desktoppr
version: 0.5-218
volume: /
location: 
install-time: 1720421876 

The install time is logged in epoch time (seconds since January 1, 1970). To convert it into something readable by humans you can use the date command:

> date -r 1720421876
Mon Jul  8 08:57:56 CEST 2024

Note: in earlier versions of Mac OS X the information on installed packages were stored in individual files called receipts. While the information is now stored in a database, the data is still referred to as a receipt.

Listing the Files a Package Installed

The --files option lists all the files that were installed by a package. The file paths are given relative to the packages install-location. Usually, but not always, the install location is the root of the file system/.

> pkgutil --files com.scriptingosx.desktoppr
usr
usr/local
usr/local/bin
usr/local/bin/desktoppr

The --file-info option does the reverse and looks up which package installed a specific file. If a file was placed there by multiple packages with different package identifiers, you will get a list.

> pkgutil --file-info /usr/local/bin/desktoppr
volume: /
path: /usr/local/bin/desktoppr

pkgid: com.scriptingosx.desktoppr
pkg-version: 0.5-218
install-time: 1720421876
uid: 0
gid: 0
mode: 100755

The installer receipt remembers the file’s owner (uid, 0 is root) and group (gid, 0 is wheel) and the permission mode that was stored in the package. Along with the actual files, the installer package also contains the owner, group, and mode (access privileges) for each file.

The metadata in the receipt may not match the file’s metadata on the disk. This indicates that the file was changed since installation. But it is difficult, if not impossible to know whether the change was intentional, accidental or even malicious. You will have to use your good judgement.

Unfortunately, some developers do not know or understand that installation packages also set the metadata for payload files and you often see changes to the owner, group and file mode applied in a postinstall script. When evaluating whether an installed file has been tampered with after installation, it is necessary to check postinstall scripts for such actions.

When a file was not installed by a package installer the --file-info option will return the path and volume, but no package information:

> pkgutil --file-info /Applications/Notes.app       
volume: /
path: /Applications/Notes.app

This is also the case for files that were copied, moved or created by a package’s preinstall or postinstall script. You only get the package data for files that were placed from a package’s payload.

Forgetting an installed package

The installation system on macOS uses the package identifier and version in the receipt to determine if an installation is new to the system, a different or new version of an already installed package, or a re-installation of a package of the same version. The behavior of the installation may change between theses scenarios.

You may notice that when you delete files or apps that were installed from a package and then re-install the same version of the package, that the files may not re-appear on the system. This happens often when you are testing an installation workflow over and over again on the same system.

You can use pkgutil --forget to remove the receipt of a package from the system. The --forget option will not delete any files that were installed on the system. All it removes is the installation receipt. If you then install the same package again, the system will consider it a fresh, new installation and the payload should be installed correctly.

Uninstalling

The macOS installation system does not have an option to uninstall or remove files and apps that were installed with an installation package. You can get a list of files that were installed from the package payload or the receipt. This will be a good starting point, but an app or tool might also create daemons, agents, preferences, configuration files, and other resources in various places across the file system. All these files weren’t part of the package payload and wouldn’t be tracked in the receipt. You will have to inspect all of these and judge whether you need to remove them, as well. Daemon and agents will need to be properly unloaded and quit before deleting their files.

Once you have built a script that performs the un-installation to your satisfaction, you should also run pkgutil --forget and remove the record of the package being installed to ensure a future re-installation will run smoothly.

Inspecting Package Payload Files

Sometimes you want to see what a package file will do without actually installing it. pkgutil has some options for that, too.

Our example file will be the installer I provide for one of my projects: desktoppr. Desktoppr is a command line tool to set the desktop picture or wallpaper on macOS.

You don’t have to actually install desktoppr to inspect the pkg. Though if you want to, you can install the pkg and use pkgutil to determine what it installed and then delete that single file later.

You can download the latest package installer file for desktoppr from the ‘Releases’ section on the GitHub repository. Note that, like many other projects, desktoppr has a pkg and a zip download. For this, we are only interesting in the pkg file.

The file command reports a pkg file is a xar archive:

$ file ~/Downloads/desktoppr-0.5-218.pkg
/Users/armin/Downloads/desktoppr-0.5-218.pkg: xar archive compressed TOC: 4389, SHA-1 checksum

(The exact output of this command may vary depending on your version of macOS and the version of the pkg.)

Packages are compressed into a single archive file. This ‘flat’ package format was introduced in Mac OS X Leopard 10.5 in 2007, replacing and deprecating the previous ‘bundle-style’ packages. Bundle-style packages were finally made defunct in macOS Sequoia 15.0 in 2024. Unless you have to support legacy Macs you should only encounter flat packages.

To expand the package, we can use pkgutil --expand

> pkgutil --expand desktoppr-0.5-218.pkg Desktoppr
> ls Desktoppr
Distribution  desktoppr.pkg

This will create a folder named Desktoppr with the expanded contents of the package file.

Inside this folder, you will see a file named Distribution. Open this file with a text editor. (open -e Desktoppr/Distribution will open the file in TextEdit if you don’t have another editor available.)

This XML file contains the metadata for the installer process. The most interesting elements are pkg-ref, which has the version and the identifier for the package and the components. It also shows the options or components that are available from the user in the Installer application.

There is a sub-folder called desktoppr.pkg inside the expanded folder. The pkgutil --expand command has already expanded this component, so we don’t need to expand it again.

> ls Desktoppr/desktoppr.pkg/
Bom         PackageInfo Payload

Note: When you are inspecting the expanded file structure in Finder, it will show this subdirectory with the package icon. The folder name ends with .pkg which Finder erroneously interprets as a file extension for a bundle-style installation package. If you want to see the contents of this folder in Finder, choose ‘Show Package Contents’ from the context menu.

Inside the sub-directory or component, you will find three more files.

PackageInfo is another XML file with metadata on the component. The most relevant information in here is right in the first pkg-info tag, which has attributes for identifier, version and install-location.

The Payload file is another archive with the actual files inside it. If you wanted to extract the files manually you can do so with:

> tar xvf Desktoppr/desktoppr.pkg/Payload
x .
x ./usr
x ./usr/local
x ./usr/local/bin
x ./usr/local/bin/desktoppr

The folder structure of the payload is relative to the package’s install-location.

Bill of Materials

The last file is called Bom which is short for ‘Bill of material’. It contains an entry for each file in the Payload with additional metadata: owner, group, and file mode (access privileges). It is stored in a binary format, so it cannot be read with a text editor, but you can read the content with the lsbom command.

> lsbom Desktoppr/desktoppr.pkg/Bom
.    40755   0/0
./usr    40755   0/0
./usr/local    40755   0/0
./usr/local/bin    40755   0/0
./usr/local/bin/desktoppr    100755  0/0 271792  550451430

This will output one line per item in the package. The entries or columns per line are: path, file mode, owner id/group id, file size and a CRC 32-bit checksum (only for files).

There are many options to control the output of the lsbom command. You can find them all in its man page.

Since the bill of material (Bom) is very interesting pkgutil provides a shortcut to get it without having to expand the entire pkg file.

> pkgutil --bom desktoppr-0.5-218.pkg
/tmp/desktoppr-0.5-218.pkg.boms.vVNvMz/desktoppr.pkg/Bom

This command will extract the Bom into a temporary file and output the path. You will use this most commonly together with lsbom.

pkgutil also has a --payload-files option:

pkgutil --payload-files desktoppr-0.5-218.pkg 
.
./usr
./usr/local
./usr/local/bin
./usr/local/bin/desktoppr

This output shows only the file path. If you require more information, use the --bom option to export the Bom file and use lsbom.

More Complex Packages

The desktoppr installation package is a very simple package. It installs a single binary file.

For a slightly more complex package, you can download the installer pkg for Setup Manager. Setup Manager is an enrollment tool that works with Jamf Pro and Jamf Connect.

Again, you do not have to actually run the installer to inspect. In this case, the tool will only work on a Mac managed with a Jamf management server at enrollment. Nevertheless inspecting this package will be instructive.

First, use pkgutil to list the payload files.

> pkgutil --payload-files Setup\ Manager-1.3.1-610.pkg
.
./Library
./Library/LaunchAgents
./Library/LaunchAgents/com.jamf.setupmanager.loginwindow.plist
./Library/LaunchDaemons
./Library/LaunchDaemons/com.jamf.setupmanager.plist
./Library/LaunchDaemons/com.jamf.setupmanager.finished.plist
./Applications
./Applications/Utilities
./Applications/Utilities/Setup Manager.app

There are more files that are listed, but they are all files and folders in the Setup Manager.app bundle. This package installs two LaunchDaemons and a LaunchAgent, as well as the Setup Manager application in /Applications/Utilities

To learn more, expand the package file with pkgutil:

> pkgutil --expand Setup\ Manager-1.3.1-610.pkg SetupManager
> ls SetupManager                     
Distribution      Resources         Setup Manager.pkg
> ls SetupManager/Resources        
License.rtf Readme.rtf

We see a new subfolder named Resources which contains two rich text files. These are shown in the respective panes when the pkg file is opened with the Installer application. You can double-click the Setup Manager pkg to open it in the Installer application and see the two panes. You don’t need to follow through with the installation.

When we dig further into the expanded Setup Manager we see another folder we did not have before:

ls SetupManager/Setup\ Manager.pkg/
Bom PackageInfo Payload Scripts
ls SetupManager/Setup\ Manager.pkg/Scripts
postinstall preinstall

The Scripts folder in the component contains two scripts: preinstall and postinstall. The installation process will run these scripts before and after the payload files are installed on the system.

When you open the script files in a text editor, you can see that these unload and load the LaunchAgents and Daemons in the payload.

You can use pkgutil and lsbom to inspect all kinds of packages. If you want to practice, the Microsoft installers are a very good exercise.

Component Packages

There is a simpler type of packages. As an example, download the installer pkg for an early version of desktoppr.

When you expand this pkg file with pkgutil, you will see no Distribution XML file or sub-component folders.

$ pkgutil --expand desktoppr-0.3.pkg Desktoppr0.3
ls Desktoppr0.3/
Bom PackageInfo Payload

Instead you see the three files we saw earlier in the component subfolder of the main pkg: Bom, PackageInfo, and Payload. Nevertheless, if you were to install this package, it would work just fine and install its payload.

This is a component package. Generally, component packages are built as an intermediate step to assemble the distribution package format we saw earlier. Nevertheless, component package files will work fine on their own, as well.

Distribution Packages, Product Archives, and Component Packages

Most of the pkg files you will encounter are distribution packages. Distribution packages do not have a payload or installation scripts of their own. Distribution packages contain one or more components. Each component will have a payload and (possibly) installation scripts.

Distribution packages are wrappers for their components and can have some extra data, such as the License and ReadMe file we saw earlier.

Apple’s developer documentation often refers to “product archives.” Product Archives are a different name for distribution packages with a specific set of metadata. Most relevantly, product archives have an identifier and version set.

Distribution packages and product archives allow the developer to customize the interactive installation process in the Installer application. Product archives are also a requirement for publishing in the Mac App Store. For these reasons, product archives are the recommended choice for developers to distribute their software.

Component packages already provide the most relevant feature for package installers: they install files. They are quite simple to create, which makes them popular with Mac system administrators who often need to build custom installers that are installed silently from a management system. There are, however, some situations where distribution packages are required with management systems, too.

Suspicious Package

Understanding the command line tools and workflows to expand and inspect pkg files is a good exercise and an important foundation to building packages. Nevertheless, it can be tedious when all you want is to just to see the files inside or some metadata for the package.

The application ‘Suspicious Package‘ provides a powerful and useful graphical interface for inspecting installation packages and their payloads. It gives an overview of package’s metadata, including signature and notarization status. It will show a detailed graphical view of the payload, the metadata files and installations scripts. When necessary, you can preview or extract individual files for further analysis

There will still be situations where you will need pkgutil, but Suspicious Package is an indispensable tool for any Mac Admin and Mac security professional. You can download Suspicious Package for free from Mothers Ruin Software.

Updates: Setup Manager and utiluti

Setup Manager 1.3

We have released Setup Manager 1.3 today. You can see the release notes and download the pkg installer here.

Most of the changes to Setup Manager in the update do not change the workflow directly. The focus for this update was to improve logging and information provided for trouble-shooting.

With the 1.3 update, Setup Manager provides richer logging information. You will find some entries in the Setup Manager log that were not initiated by the Setup Manager workflow, but are still very relevant to troubleshooting the enrollment workflow. You can see all installation packages that are installed during the enrollment, as well as network changes. This allows an admin to see when managed App Store installations or other installations initiated from the MDM or Jamf App Installers are happening in the enrollment workflow.

These can be very helpful to determine what might be delaying or interrupting certain other installations.

When we started building the “enrollment tool we wanted to use ourselves” more than two years ago, we chose to build a full application, rather than a script-based solution which remote controls some interface. One of the immediate benefits is that we could make the user interface richer and more specialized. Localizing the app into different languages was easier, too. Setup Manager adds Polish localization, bringing the total number of languages to ten!

(We use the help of volunteers from the community to localize to other languages, if you want to help localize Setup Manager into your language, please contact me.)

There was another goal, which took a bit longer to realize.

Swift apps allow us to dive deeper into the capabilities and information available in the operating system. A full blown app is also more capable at analyzing and displaying multiple sources of information at the same time. For example, Setup Manager will display a big warning when the battery level drops below a critical threshold.

These kinds of workflows and user interfaces would be nearly impossible or, at the very least, extremely complex to build and maintain with shell scripts. In this case, Setup Manager is monitoring and parsing other log files and summarizing them down to some important events in the background, while it is working through its main purpose of running through the action list from the profile.

This feature will not be seen by most users or even techs who are sitting in front of the Mac, waiting for the base installation to finish. But when you are trouble shooting problems during your enrollment workflow, these extra log entries can be very insightful. Even during testing, it unveiled some surprises in our testing environments.

We hope you like the new features. But, we are also not done yet and have plenty more ideas planned for Setup Manager!

utiluti 1.2

Since we are talking updates, I have also released an update to my CLI tool to set default apps for urls and file types (uniform type identifiers/UTI). utiluti 1.2 adds a manage verb which can read a list of default app assignments from plist files or a configuration profile. You can see the documentation for the new manage verb here and download the latest pkg installer here.

This allows you to define lists of default apps and push them with your device management system. Then you can run utiluti from a script in the same management system. This should greatly simplify managing default apps.

Note, that while you can set the default browser with utiluti, whether you are using the manage option or not, the system will prompt the user to confirm the new default browser. For this use case, you will want to put the utiluti command in a context where the user is prepared and ready for that extra dialog (such as a Self Service app). There are other tools, such as Graham Gilbert’s make-default CLI tool, which bypass the system dialog. In my experience, tools like this work well in fairly clean setup and require a logout or reboot after the change. This might fit your workflow, but you need to test.

I hope utiluti will find a place in your MacAdmin’s toolbox!