Frequently Asked Questions
Looking for something specific? The User Guide and FAQ are indexed within the app — open Apparency, click on the Help menu (Command-?), and type in the Search field. Results listed under Help Topics link directly to the appropriate User Guide section or FAQ entry here.
What versions of macOS does Apparency support?
- macOS 14 (Sonoma)
- macOS 13 (Ventura)
- macOS 12 (Monterey)
If you're using an older version of macOS, you can download an old version of Apparency as follows:
- For macOS 11 (Big Sur), use Version 1.6.1.
- For macOS 10.15 (Catalina), use Version 1.4.1.
- For macOS 10.14 (Mojave), use Version 1.3.
We make these older versions available for those who need them, but we have not tested them since they were current, and can't make any claims about how well they might work.
Ensuring that Apparency works properly on even a single version of macOS takes a great deal of work, especially for our one-person company. Since we are not willing to claim support for a macOS version that we have not tested, and since Apple releases a new macOS version every year, we must periodically drop support for older versions.
Does Apparency automatically check for updates?
Yes, once every 2 weeks — or less, if you use it less often — Apparency will download a small file from our website to get the current version number. If a newer version is available, you'll see an Update Available button on the right side of the app's title bar:
Click on Update Available to open the Apparency download page, where you can get the latest version.
If you want to change the frequency with which Apparency checks for updates, or turn off automatic checking entirely, use Apparency > Preferences > Update:
Apparency never automatically downloads or installs the actual updated app. You make the decision about when or if to download it, and where and how to install it.
Why should I care about these details of an app?
You shouldn't need to care about these details. And maybe you don't need to care about them — that is clearly Apple's position on the matter.
But if you do care about them, shouldn't it be easier to get the information?
Apparency is primarily for people who already know that they wanted something like this. If it doesn't sound like something you want, we won't try to convince you. After all, we make the same $0 whether you use it or not.
“But isn't [security feature X] just more Apple security theater?”
Maybe. Maybe not. But what is theater without an audience? ¯\_(ツ)_/¯
Seriously, we don't claim to be authorities on this. There is no shortage of other people who have opinions on the matter. Apparency simply provides visibility into the mechanisms that exist. You can decide for yourself how much trust to put in them.
“Won't [some command in Terminal] give me the same information?”
Yes, absolutely. There's nothing that Apparency shows that isn't available through some Terminal command or another.
For example, the information about the code signature, entitlements and such can
be discovered using the
codesign(1) command. But you have to wrangle codesign's arcane command line
options, and run it multiple times different ways to get all of the relevant information. If you want Gatekeeper information,
you also need to use the
spctl(1) command. And if you want to actually examine the signing certificate, you
need to grok codesign's
--extract-certificates option, hunt down the (buried) Certificate Assistant app, click through
its certificate evaluation flow, and drag the extracted certificates into it.
Apparency is simply aiming to make this all easier and more convenient.
Is Apparency “sandboxed”? What entitlements does it have?
Yes, Apparency is sandboxed. In fact, you can easily see this using Apparency itself.
To quickly open Apparency using Apparency, use Cmd-Option-O. You can see that App Sandbox is enabled.
If you use Component > Show Entitlements, you'll see that the top-level Apparency app has the following entitlements:
|This allows Apparency to see the app that you tell it to open — even if it's in a place that is not otherwise accessible to sandboxed apps, such as inside your home folder.
|This allows Apparency to talk to the /usr/libexec/syspolicyd daemon, which is required to successfully check an app's notarization status. (Apparency uses the macOS Security.framework APIs to check for notarization, but that framework's sandbox configuration isn't complete.)
|This allows Apparency to open executables in the system /usr/libexec directory, when selected from the File > Open System Component dialog. Executables started by macOS launchd jobs tend to live in this location, which is not readable to sandboxed apps by default.
|These allow Apparency to send AppleScript (Apple Events) to the Hopper Disassembler, which is required for the Component > Analyze Executable Using Hopper Disassembler command.
|This allows Apparency to compile AppleScript for the Hopper Disassembler, even when that app is installed in the per-user Applications folder. The above entitlements allow us to send Apple Events, but if our process can't read Hopper's scripting dictionary, our AppleScript won't compile.
|This allows Apparency to read per-user launchd agent definitions, so that it can show these under Component > Show Launch Information > Launchd Job Definitions when the referenced executable is examined.
Within Apparency, there are two other components that are also sandboxed, and thus have explicit entitlements.
The ApparencyPreviewExtension.appex component provides the Quick Look Preview to the Finder, and has the following entitlements:
|As above, this allows the Quick Look extension to see the app to preview.
|As above, this allows the Quick Look extension to check an app's notarization status.
|On macOS 11 (Big Sur) or later, this allows the Open With Apparency button to work. That button does the equivalent of opening a custom URL, which is handled by the Apparency app. The sandboxing of Quick Look Preview extensions on Big Sur is such that, by default, this operation — opening a URL via LaunchServices — is not allowed.
Finally, under the MRSFoundation.framework component, you'll find a com.mothersruin.MRSFoundation.UpdateCheckingService.xpc XPC service. This is the component that performs the periodic check for updates, and is entitled as follows:
|This allows the XPC service to make an outgoing network connection, in order to fetch this file, which contains the information about the current version of Apparency available for download.
Why don't I get a Quick Look preview? What if I prefer the standard preview instead?
Once you've opened Apparency for the first time, macOS ought to enable its Quick Look Preview extension, so that previewing an app from the Finder will show the enhanced version.
However, if you don't get the expected preview, open System Settings and go to Privacy & Security > Others > Extensions > Quick Look and make sure Apparency is checked. (On macOS 12 or earlier, open System Preferences and go to Extensions > Quick Look.)
If you'd rather not use the Apparency preview — but want to keep the Apparency app — you can disable the previews here as well.
Why the name “Apparency” anyway?
It's a portmanteau of “app” and “transparency.”
Yeah, we know, but naming things is hard. At least we didn't give it a name that makes people think it's malware, like last time.
Using the Component Browser and Info Pane
What do the different Notarization statuses mean?
In the info pane, the Notarization status is described with one of the following:
|The app or component was submitted to Apple's notarization service and approved for distribution.
|Granted — Stapled Ticket
|The app or component was submitted to Apple's notarization service and approved for distribution, and a
notarization ticket was “stapled” to the app. A notarization ticket is cryptographically signed by Apple,
and stapling it to the app allows Gatekeeper to verify the notarization, even when your computer isn't
connected to the internet. (However, stapling the ticket to the app is not strictly required. Sometimes,
the ticket is stapled to the disk image that contains the app: this serves the same purpose but doesn't
leave behind a stapled ticket that Apparency will find. If neither is stapled, macOS will
query Apple's servers as needed to check for notarization.)
If a ticket is stapled to the app, you can use Component > Show Notarization Ticket to see details, including the notarization time and the Apple certificate that signed the ticket.
|The app or component does not appear to be notarized. This might be caused by an inability to connect to Apple's servers. However, a network connection is required only the first time that macOS checks for notarization of a given app, and then only if the notarization is not “stapled” to the downloaded copy. So even in the absence of Internet connectivity, it is likely the app is not actually notarized.
|The app or component is signed by Apple directly, and therefore is not required nor expected to be notarized. This typically applies to apps installed with macOS, downloaded from the Apple web site, or installed from the Mac App Store.
|The app or component supports multiple processor architectures, which have different notarization statuses. See more below.
|Apparency is still verifying the signature on the app or component, and can't evaluate the notarization status until this is complete.
|Multiple apps or components are selected in the browser, and these have different notarization statuses. Select a single component to see its notarization status.
What do the different Gatekeeper statuses mean?
In the info pane, the Gatekeeper status is described with one of the following:
|The app or component was signed by Apple, and is always allowed to run. This applies to apps pre-installed with macOS and to some software downloaded from the Apple website — including most developer tools.
|Mac App Store
|The app or component was signed by Apple for distribution via the Mac App Store, and is always allowed to run.
|Notarized Developer ID
|The app or component was signed with an Apple-issued Developer ID certificate, and subsequently
approved for distribution by Apple's notarization service. The code will be allowed to
run by default, unless disabled via System Settings, under
Privacy & Security > Security > Allow apps downloaded from
(or on macOS 12 or earlier, in System Preferences under
Security & Privacy > General).
Note that “identified developers” here refers to Developer ID certificates.
The system policy might also be changed through the
|The app or component was signed with an Apple-issued Developer ID certificate, but does not appear to be notarized. However, macOS will allow this code to run, because (a) the developer began distributing apps under this Developer ID prior to the release of macOS 10.14.5 (April 8, 2019), and (b) the app or component has a verified signing time no later than June 1, 2019. [macOS 10.15 (Catalina) and later enforce both of these requirements on unnotarized code, while macOS 10.14 (Mojave) enforces only the first.]
|Apparency can't evaluate the Gatekeeper status because the signature itself is not valid, as shown by the Signed By identity (and documented below). Since the identity of the signing certificate can't be relied upon, it doesn't make sense to evaluate that identity against any Gatekeeper policy.
|Apparency didn't evaluate the app or component against the Gatekeeper policies, because macOS doesn't rely on Gatekeeper for this type of component. See more about these known Gatekeeper exceptions below.
|The app or component was signed with a certificate that is not trusted by Gatekeeper (or perhaps not even by macOS; see below). This might be case the if the component was signed with a third-party certificate (which would be uncommon) or perhaps with an Apple-issued certificate that is not of the Developer ID variety (such as an App Store distribution certificate, which is only supposed to be used for submission to Apple, but is sometimes mistakenly used elsewhere).
|Unnotarized Developer ID
|The app or component was signed with an Apple-issued Developer ID certificate, but does not appear to be notarized. Since the exceptions described above for the Developer ID status are not met, macOS will not allow this code to run (unless it is found to be notarized when Gatekeeper runs).
|The app or component supports multiple processor architectures, which have different Gatekeeper statuses. See more below.
|Apparency is still verifying the signature on the app or component, and can't evaluate the Gatekeeper status until this is complete.
|Multiple apps or components are selected in the browser, and these have different Gatekeeper statuses. Select a single component to see its Gatekeeper status.
For more information about how Apparency determines the Gatekeeper status, see below.
What do the different Signed By identities mean?
In the info pane, the Signed By identity will resemble one of the following:
|The app or component was signed by Apple. This Apple certificate is used to sign the components of macOS itself (including those delivered through Software Update), as well as some software distributed through the Apple website, including most developer tools.
|Apple Mac OS Application Signing
|The app or component was signed by Apple, prior to making it available on the Mac App Store. This applies to all apps from the store, whether made by Apple or by other developers. (All apps submitted to the Mac App Store are re-signed with this certificate before being made available for sale.)
|Johnny Appleseed (123DE678IJ)
|The app or component was signed with the named certificate. The exact format varies with the type of certificate and the issuer. If it's an Apple-issued Developer ID certificate, there will usually be a parenthesized string of numbers and letters at the end, as in this example: this is the team identifier that Apple assigned to that developer's account. Click Show Code Signature to see details about the signing certificate.
|The app or component was not signed at all. This is often the case with older apps, or with developers who aren't particularly excited about paying Apple US$99 per year for the privilege of shipping software for macOS.
|The app or component was signed without using a certificate. This scheme gives the component a unique code signing digest (or cdhash), which macOS can use to detect changes. For example, ad-hoc signatures are used for otherwise-unsigned code on Apple Silicon (see below), and for “Web Apps” on macOS 14 (Sonoma). However, it's important to note that ad-hoc signatures don't tell you anything about the developer's identity.
|The component was automatically signed by the macOS linker. This behavior was introduced in macOS 11 (Big Sur), to
fulfill the Apple Silicon requirement
that all executables have some sort of code signature — and thus have a code signing digest
(or cdhash). This linker signature is a form of ad-hoc signature (and so doesn't tell you anything
about the developer's identity); it is usually applied only to the Apple Silicon architecture
(with the Intel architecture being left unsigned). This automatic signing is useful only for
standalone Mach-O executables, since it doesn't seal any of the bundle resources that would be covered by a
codesign(1)-generated signature (even an ad-hoc one).
|The app or component was signed with a certificate that is now expired, and the signature doesn't have a verified signing time that predates the expiration. See more on macOS handling of expired certificates below.
|The app or component was signed with a certificate that is not trusted by macOS. That is, the certificate was issued by a certificate authority that is not included in the macOS “system anchors.” This is unusual, especially since most modern apps are signed with certificates that were issued by Apple's own certificate authority (which is obviously trusted by macOS). See more on untrusted certificates and code-signing below.
|Can't verify signature
|The app or component appears to have been corrupted or tampered with since being signed. As such, the identity implied by the signing certificate is not reliable. This could be a result of the app being compromised at the point of download. Or it could simply be the result of errors occurring during signing, or in a software update process — the latter is depressingly common, even and especially for built-in macOS apps. (For iOS apps installed on Apple Silicon Macs, read more below.)
|Missing or inaccessible bundle
|The component is the main executable of a bundle (e.g. X.app/Contents/MacOS/X), but
the enclosing bundle is either missing, or Apparency can't access it. Apparency can read only
the file or folder you select in the File > Open dialog, not the
folders above it: this is how the App Sandbox works (unless the app is in the shared Applications folder,
which all apps can read). If Apparency can't read the bundle resources (such as the Info.plist file),
it can't verify the code signature. You will likely get more useful information by opening the enclosing bundle instead.
Another reason you might see this error is if you're using Suspicious Package to Quick Look an executable within a macOS Installer package. If you preview the executable directly, Suspicious Package will export only that file. Using Quick Look on the enclosing bundle will probably yield more complete information.
|Skipped verification due to size
|The app or component is so large that Apparency suspects it would take an unusually long time to verify, and so did not try to do so automatically: see more on this below.
|The app or component supports multiple processor architectures, which were signed with different certificates. See more below.
|Failed to read signature
|A low-level error occurred in the macOS security subsystem while attempting to read the signature. If this occurs, it probably indicates corruption of the app that fundamentally damaged the signature, and is unlikely to be recoverable.
|Removed to DYLD shared cache
|The component is part of macOS itself, and the signature was removed when the Mach-O binary was moved into the DYLD shared cache — an optimization used in macOS 11 (Big Sur) and later.
|Apparency is still verifying the signature on the app or component, and can't report the signing identity until this is complete.
|Multiple apps or components are selected in the browser, and these have different signing identities. Select a single component to see its signing identity.
How does Apparency determine the required macOS version?
macOS has a few different mechanisms for an app to declare the oldest version of macOS that it supports:
- For an app bundle, the
LSMinimumSystemVersionkey in Info.plist gives a single requirement that applies to all processor architectures.
- Less common, the
LSMinimumSystemVersionByArchitecturekey gives a (potentially) different requirement for each processor architecture.
- For a Mach-O executable, the deployment target (e.g. the
MACOSX_DEPLOYMENT_TARGETXcode build setting) is stored in the Mach-O
LC_BUILD_VERSIONload command (or the older
LC_VERSION_MIN_MACOSXload command). For a Universal executable, there will be a distinct version for each processor architecture, and these are sometimes adjusted by Xcode (e.g. bumped to a minimum of 11.0 for Apple Silicon, since nothing earlier supports that architecture).
For quick reference, Apparency tries to reconcile all of these sources into a single macOS version, and shows this as the Requires version on the info pane. It does this by choosing the oldest of all the Info.plist specifications, or if there are none (such as for a standalone Mach-O executable), it chooses the oldest of the deployment targets for all architectures. Whether this makes sense depends on how you intend to use this information.
If you need more detail, all of the individual bits of information are still available via Apparency:
- To see the Info.plist properties, use Component > Show Info Property List (Shift-Cmd-I),
and look for the
- To see the deployment targets, use Component > Show Executable Information (Shift-Cmd-X), and look at the Deployment Version. Use the Processor Architecture pop-up to see how this differs between architectures.
For iOS apps installed on Apple Silicon Macs, there is instead a minimum iOS version requirement, which is (presumably) checked against the version of the iOS frameworks bundled with that version of macOS. Apparency uses the
MinimumOSVersionkey from the Info.plist, as well as the Mach-O
LC_BUILD_VERSIONload commands, to determine what to show for the Requires version.
Why would Apparency tell me an app was “downloaded” when it clearly wasn't?
When you download a file in your web browser, macOS records how and where it was downloaded. If the file is an app, this tells macOS to activate Gatekeeper when the app is first opened. (If the file contains an app, as in the case of a disk image or zip file, the download information gets copied along with the app. So even after you've dragged the app to your Applications folder, macOS still knows that the app was downloaded.) Usually, the result of Gatekeeper is an alert, saying that the app was “downloaded from the Internet,” and asking you if you still want to open it.
In between when you download a file, and when you click OK in that Gatekeeper alert, macOS considers the file to be in quarantine (this terminology was more whimsical before 2020). Once you've allowed Gatekeeper to open the app, it is no longer considered quarantined, but there may still be enough information left behind for Apparency to detect when it was downloaded and by what app (i.e. your web browser). Apparency shows this information in the info pane; you can click on the download date once to see the downloading app name, and a second time to see the kind of download (e.g. from the Internet).
This is all probably what you'd expect “downloaded” to mean. But macOS also applies the quarantine mechanism elsewhere. Most significantly, when an app uses the App Sandbox, all files saved by that app are considered quarantined. For example, say that you receive an email with a zip file attached, and that zip file contains an app. When you ask the Apple Mail app to save that attachment, the zip file gets quarantined: as a result, opening the zip file and then the app inside will activate Gatekeeper. This happens because Apple Mail uses the App Sandbox, and not because of any special knowledge about the source of the files being saved. (Contrast this with Safari or Chrome, which explicitly tell macOS to quarantine a downloaded file, because it came from the Internet.)
Of course, for an app like Apple Mail, almost every file it saves was essentially downloaded from the Internet. Other sandboxed apps might not be downloading anything, but the files they save will still be quarantined — for example, simply save an image in the Preview app and it will be quarantined. This is because macOS does not rely on the (sandboxed) app to decide if content is potentially unsafe, but instead quarantines pretty much everything, and leaves it to Gatekeeper to decide what should be checked and how. (Obviously, Gatekeeper is going to check a quarantined app, but even some kinds of documents can be unsafe and will be subject to Gatekeeper checks.)
So, if an app that you open with Apparency was quarantined — even if it wasn't strictly “downloaded” in the traditional sense — you might still see a Downloaded date in the info pane. Apparency can detect that the quarantine came from a sandboxed app, but it doesn't know whether there was any actual download involved. If you click on the date, Apparency will show the app that saved it, and if you click again, from App Sandbox (save or open).
Sometimes, simply opening a file, without ever changing or saving it, will cause a quarantine to be added.
When you use the standard File > Open dialog from a sandboxed app, macOS gives the app permission to open and/or save that file, depending on the app's entitlements. But, when the Resume feature of macOS saves the open windows of the app, it saves the app's permissions in a way that does not preclude changing the file, and this triggers a quarantine on the file. [More precisely, in a document-based app, AppKit creates a security-scoped bookmark for each open document URL, and if the URL is user-writable, it omits the
NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccessoption — even if the app has declared its read-only intent via both the
com.apple.security.files.user-selected.read-only=YESentitlement and the
CFBundleTypeRole=Viewerdocument type declaration. This creation of a read-write bookmark triggers a quarantine on the file, at least in the absence of the
This surprising behavior applies to Apparency itself: if you open a standalone executable with Apparency, you might find that Apparency claims that it was downloaded by Apparency itself! This is simply because Apparency opened it, even though it never changes or saves anything. This can be baffling, but it only happens when you open a standalone executable (not an app or other bundle); we don't try to fudge or hide it, because you should be able to trust that Apparency is showing you the actual quarantine information.
What are “code signing options” and how can I view them?
The macOS code signing mechanism has a number of additional options that can be applied to a component. These flags change the way that macOS handles certain aspects of the code signature when the app is used.
For example, there's a Library Validation option that restricts the app to using only those dynamic libraries or frameworks provided by macOS, or those signed by the same developer. And there is a Kill option that tells macOS that it should terminate the app if the code signature should become invalid while open.
To our knowledge, the only Apple documentation of these options are in the
codesign(1)man page (see the Option Flags section), and in the
Security/CSCommon.hheader (see the
SecCodeSignatureFlagsenumeration). The latter contains some options not mentioned in the former.
Apparency doesn't show these options by default, because they are not terribly common, and (quite frankly) because we don't understand the nuances of them ourselves, and can't really provide any guidance on interpreting them! However, if you want to see them in Apparency, you can enable them using Apparency > Preferences > General > Show code signature options for selected component. With this enabled, Apparency will show the options at the bottom of the info pane. (Note that Apparency won't show the Runtime or Adhoc options here, though, since these are already represented elsewhere in the info pane — the first under Hardening and the second under Signed By.)
What is a de facto “code place” and how does Apparency handle it?
When you open an app (or other component), Apparency tries to find all of the meaningful components within the app — such as plug-ins that add to system functionality, frameworks that the app uses, or standalone executables that it runs (or provides for you to run from the Terminal). As a general rule, these components either have their own code signatures — and possibly, entitlements — or are types that at least could be signed.
By default, Apparency looks for components only in well-defined places, rather than examining every file and folder inside the app (which could become quite slow for large apps). Apple historically published some guidelines about where such components (or nested code) should be placed. However, these guidelines have not been updated in many years, and were never followed very closely, even by Apple itself.
As such, Apparency looks in both the “standard” code places — such as Contents/PlugIns or Contents/XPCServices — as well as other places that usually contain code — such as Contents/Library/QuickLook or Contents/Library/SystemExtensions. It doesn't look in places that mostly contain non-code items, such as Contents/Resources, because it would likely spend a lot of time examining unrelated files, like nib, image and strings files.
However, for some apps, this approach might omit some components. The Contents/Resources folder, especially, is pretty much a de facto code place, in that developers (including Apple) have put code there for years, without much consequence. (The only known consequence is that it slows down verification of the code signature, because the nested code gets “sealed” twice — once through its own code signature, and once as a resource of the parent component's code signature. This redundancy is generally noticeable only on huge apps, though.)
In any case, you can ask Apparency to try harder to find components: when you select the app in the File > Open dialog, check Look for components in de facto “code places” before clicking Open. (If you don't see this checkbox, click Show Options.)
With “de facto code places” enabled, Apparency will search the Contents/Resources folder, as well as any non-standard folders directly under Contents — most apps don't have these, but some use them quite extensively (Xcode being a primary example). The tradeoff here is that Apparency may take longer to analyze the app and open the window. (Unfortunately, Apparency isn't currently designed to do this work in the background and show appropriate progress information, which is one reason that this isn't enabled by default!)
Alternatively, if the app is already open in Apparency, use File > Re-open with Options (Cmd-Shift-O) to quickly re-open it. Apparency will remember this setting on a per-app basis, so if you open the same app again — say, using Finder > Services > Open With Apparency or File > Open Recent — it will find components the same way.
Examining and Understanding Code Signatures
What does it mean when a code signature has a “verified signing time”?
All certificates — including Apple-issued Developer ID certificates — expire after some amount of time. (This is a security precaution, since the underlying technology and the attacks against it are always moving forward.) In the ideal case, developers replace certificates before they expire, and an app you download today will be signed with a still-valid certificate. But in the real world, developers don't always get ahead of expiring certificates — or you might just have an older version of an app and still want to use it after the certificate has expired.
However, an expired certificate does not necessarily make the code signature invalid.
When an app is signed on macOS, the current date and time can be incorporated into the signature in a way that is
independently verified by Apple. That is, instead of the app just claiming to have been signed at a specific date
and time, the actual signing time is securely supplied by Apple, and bound to the code signature cryptographically.
(This is known as a
timestamp,” with an Apple server as the “Time Stamping Authority.”)
This typically happens automatically when the developer signs the app, but it might have been disabled (see the
man page, and the
This becomes relevant when Apparency evaluates the trustworthiness of the code signature. If there is a verified signing time, macOS will evaluate the signing certificate as of that date and time. If the certificate is expired now, but was not expired (and was otherwise valid) at the time the app was signed, macOS might decide that the signature is still trusted. (There are exceptions; for example, a certificate might have been explicitly revoked, and might not be considered valid for any signature on any date.)
Even in the absence of a verified signing time, Gatekeeper is quite lax about expired certificates in code signatures. See more on this below.
Unfortunately, the way that this scenario is presented in the standard macOS certificate trust sheet is less than clear: if you examine the certificate details, you'll see that the certificate has expired, but macOS still says “this certificate is valid.” Of course, the certificate itself is no longer valid, but since it was valid at the verified signing time, the signature is nevertheless valid. However, macOS doesn't show the verified signing time anywhere, nor even note its existence.
Apparency attempts to improve matters by explicitly noting when a verified signing time is found in the code signature. When you use Component > Show Code Signature, if the code signature includes a verified signing time, it will be shown in the sheet:
If you click Show Certificate, you may still see the certificate-is-expired-but-valid confusion described above — since Apparency uses the standard macOS certificate trust sheet, and can't change this bit — but at least this gives some context to the confusion.
Why does Apparency complain that a certificate is expired when Gatekeeper doesn't seem to mind it?
If an app was signed with a certificate that has since expired — and the signature lacks a verified signing time — Apparency will show the Signed By identity as Expired certificate. However, macOS generally does not consider an expired certificate to invalidate a code signature, such as when evaluating Gatekeeper policies.
We can only speculate why this is the case. Presumably, too much would break if expired certificates invalidated a code signature, especially if it prevented otherwise-valid apps from being used. The existence of verified signing times ought to solve this problem, but in fact, these are not used consistently enough. Most third-party apps have them, but most Apple apps — and apps distributed via the App Store — do not.
In any case, Apparency shows expired certificates as such, so you can decide whether or not you care.
Why does Apparency say that a certificate is untrusted when codesign(1) doesn't complain?
If an app was signed with a certificate that is not trusted by macOS, Apparency will show the Signed By identity as
Untrusted certificate. However, if you run
codesign(1) with the
option, it might not report any error. That's because the macOS code-signing mechanism does not, by default, impose any
trust requirements on the certificate used to sign an app. Features built on top of code-signing, such as Gatekeeper, can and do impose
such trust requirements, but when you use
codesign(1), you are interacting with the bare code-signing mechanism only.
To understand why this is the case, we have to go back in time. The macOS code-signing mechanism dates to Mac OS X 10.5 (Leopard), way back in 2007. In those early days, signed third-party apps were rare. Apple was code-signing components of macOS itself, but even those signatures were frequently broken by the Software Update process, with little ill effect. Indeed, before the introduction of Gatekeeper in Mac OS X 10.7 (Lion), Apple wasn't issuing code-signing certificates to third-party macOS developers. (Certificates issued to App Store developers, even with the advent of the Mac App Store, were never intended to be used for signing apps to be installed on macOS, but only for signing apps being submitted to App Review. App Store apps are always re-signed by Apple prior to being offered on the store.)
In those ancient times, a third-party developer could have gotten a signing certificate from some non-Apple certificate authority — any of the ones generally trusted via the macOS “system anchors” — but that would've been a lot of trouble and expense for no real benefit. In fact, the only benefit was that a code-signed app could be updated without losing access to any keychain items it created: as long as each new version of the app was signed with the same certificate as the original version of the app, it would maintain access. But even this could be achieved with a self-signed certificate, which can be created freely by a developer, and doesn't involve any third-party validation. It didn't matter that the self-signed certificate wasn't trusted by macOS; all that mattered is that the same self-signed certificate was used to sign subsequent app versions, indicating that they came from the same developer as the original.
Anyway, given that code signing was optional and uncommon for so many years after introduction, it is not so surprising that the
basic code-signing mechanism doesn't enforce much actual policy — that's what features such as Gatekeeper added. As such,
when you run
codesign ‑‑verify, you are asking it to verify only two things:
- The app has not been modified since being signed, denoted by
valid on disk. This means that the executable, the Info.plist and all of the other resources (images, nib files and so on) are identical to the versions that were present when the app was signed. (The signature is formed by calculating a digest of these files — called the cdhash, or code directory hash — and cryptographically signing that.)
- The code signing identity is matched, denoted by
satisfies its Designated Requirement. The designated requirement is a set of rules that identifies the app in some way. By default, the designated requirement specifies a trusted certificate, issued by Apple to a specific developer, along with a specific bundle identifier. However, the designated requirement can be overridden at signing time, so what it actually requires is not guaranteed.
Unless you've examined the designated requirement — such as by using
codesign with the
‑‑requirements options — you can't be certain that
satisfies its Designated Requirement implies
a trusted certificate. You can, however, ask
codesign to check an additional, explicit requirement that includes trustworthiness. For example:
$ /usr/bin/codesign --verify --verbose=4 --test-requirement="=anchor trusted" /Applications/Some.app /Applications/Some.app: valid on disk /Applications/Some.app: satisfies its Designated Requirement /Applications/Some.app: explicit requirement satisfied
Here, the explicit requirement says that the signing certificate must ultimately
be issued by a certificate authority that is trusted by macOS. Often, this will be Apple's own certificate authority, but this requirement
is more general than that. (Use
anchor apple generic instead to require an Apple-issued certificate.)
Again, this is only interesting to understand what the
codesign tool is actually checking — and what it isn't.
It doesn't mean that macOS isn't checking the trust of code signing certificates in specific circumstances. For example,
Gatekeeper rules are themselves code-signing requirements, just like the above. You can see these rules via something like:
In fact, these are the same requirements that Apparency uses to determine the Gatekeeper status.
Why does Apparency say that a signature is valid when codesign(1) reports the “resource envelope is obsolete (custom omit rules)”?
As described above, part of verifying a code signature is verifying that the resources inside the app (such as strings or nib files) haven't been changed since the app was signed. For a standard macOS app, there are a handful of files that are allowed to change without being flagged as an error (for example, .DS_Store or PkgInfo files). In olden times, apps could add files to this list (i.e. “custom omit rules”), but since OS X 10.9 (Mavericks), such custom exclusions from the code signature have been prohibited. This is because macOS apps are not supposed to modify themselves — they should store customizations in standard places, usually inside a Library folder.
However, Apple grants itself some exceptions to this prohibition on custom exclusions, in at least 3 cases that we're aware of:
- The “Install macOS” app — delivered in a macOS Full Installer package — has an unique install process that adds the Contents/SharedSupport/SharedSupport.dmg file to the app bundle, which isn't part of the code signature. Apple adds custom omit rules here to exclude the dmg (and, indeed, all other resources) from the code signature. The disk image is verified independently of the code signature when the “Install macOS” app runs.
- On an Apple Silicon Mac, iOS apps delivered from the App Store are modified by the addition of SC_Info folders. These contain files related to the FairPlay DRM scheme, and are specific to the purchasing user. When Apple re-signs apps for delivery on the iOS App Store, it adds custom omit rules that exclude these SC_Info files. The App Store install process presumably has an exception for these custom omit rules when verifying these App Store-signed apps.
- On macOS 11 (Big Sur) or later, everything on the signed system volume has custom omit rules which exclude
all resources, presumably because it is redundant to the protections of the system volume itself. Normally,
codesign(1)won't complain about these, because it knows not to verify resources on the signed system volume — unless you run codesign to verify a component found on a secondary (non-booted) signed system volume, that is.
In each of these cases, if you run
codesign(1), you'll get something like:
$ /usr/bin/codesign --verify /Applications/Overcast.app /Applications/Overcast.app: resource envelope is obsolete (custom omit rules)
Prior to version 1.4, Apparency would show Can't verify signature for these components, for the same reason. This was accurate, but obscured the more useful information about the signature. It also preempted any verification of the non-omitted resources, because the code signing machinery balks at the custom omit rules before attempting to verify what is not omitted.
To present more useful information, Apparency now disables the “custom omit rule” error on components that are signed by Apple or by the App Store (but not those signed by any other certificate, not even one issued by Apple). This avoids presenting not-very-interesting signature errors, and allows any non-omitted resources to be verified. (Admittedly, the latter probably only applies to iOS apps, since the other two cases omit all resources.)
Why does Apparency sometimes say that it skipped verification of the signature?
In order to verify the signature of an app or component, every file that makes up that component has to be read in its entirety, creating a cryptographic digest (or hash) of the complete component. This digest is then compared against the one that was produced when the component was signed by the developer. Any changes that are made to any file of the component are thus detected, because it will result in a different digest and invalidate the signature. (This is a bit of an oversimplification, but conceptually accurate anyway.)
In most cases, creating the digest takes a few seconds at most, and the signature is verified quickly. However, for extremely large apps, verification can start to take real time. For example, Xcode — weighing in at over 10 GB in size — can take several minutes to verify. (Anyone who has ever installed Xcode has surely noticed the long delay upon first launch!) You might want Apparency to do the verification anyway, if the signature is the information that you're interested in inspecting, but otherwise, it is just an annoying use of your CPU.
Apparency tries to guess when an app or component is so large that the verification will take an unusually long time. Where it believes this to be the case, Apparency will not automatically verify that component. Instead, it shows the Signed By identity as Skipped verification due to size. When this occurs:
- If you don't care about the signature of the component, you can ignore this entirely.
- If you would like to proceed with verification of the signature for just this single component, select it, click the Signature toolbar button (or use Cmd-Shift-S), and then choose Verify Signature.
- If you would to proceed with verification of the signatures for all components in this app, choose File > Verify All Signatures.
- If you would like Apparency to always verify all signatures, even where it believes that this make take a long time, go to Apparency > Preferences > General and uncheck Skip automatic signature verification for large code.
What does “Conflicting signatures” mean? Why does Apparency show a processor architecture pop-up?
The macOS code signing mechanism stores most of the information about the signature inside the executable of the app or component. This information includes the entitlements and the code signing identity — along with a web of cryptographic digests (or hashes) that uniquely identifies the compiled code, the Info.plist and the other bundle resources (such as images or strings files).
If an app supports multiple processor architectures (e.g. Intel — 64-bit and Apple Silicon — 64-bit), there will actually be a separate code signature for each architecture. This is simply how the code signing mechanism is designed. (The app can even be “thinned” to remove architectures, without invalidating the signatures on the remaining architectures.) Typically, each of these distinct signatures is functionally identical: that is, signed by the same certificate, with the same entitlements, code signing identity, and so on. (They can't be exactly identical, however, because the compiled code is always going to differ.)
To ensure that a multiple-architecture component has functionally identical code signatures, Apparency will evaluate the signature for every architecture. If everything is indeed consistent, Apparency won't show anything special: the Signed By, Gatekeeper, Entitlements and other attributes will simply show the common values extracted from every architecture.
However, in the (unlikely!) event that Apparency does find a difference between signatures, it will instead show Conflicting signatures in the applicable field of the info pane. When you use Component > Show Code Signature or Component > Show Entitlements to get more details, you will be given a choice of architecture to examine. This allows you to see how the architectures differ. Obviously, the architecture that your Mac will use is the most interesting, but a difference in signatures is unusual enough that you may want to examine everything more closely.
Examining and Understanding Launch Information
What is a “code signing identity”? What is a “code signing requirement”?
A code signing identity allows macOS to recognize a specific app from an Apple-registered developer, without regard to a particular version of that app. This is used by macOS to grant access to sensitive resources — such as keychain items or App Sandbox “containers” — in a way that won't break every time the app is updated.
In its typical form, a code signing identity — also called a “designated requirement” (DR) — consists of two pieces:
- a bundle identifier, which is not trustworthy on its own, since any app can claim to be, say,
- an Apple-assigned developer identity, which can be claimed only by someone holding the private key of a matching Apple-issued certificate.
Any app that matches both of these attributes is assumed to be the “same” app, and is allowed to access the same resources. The developer can update the app with abandon, as long as the code signing identity doesn't change.
A code signing identity is an example of a code signing requirement, which is a more general mechanism for capturing requirements that a code signature must meet. In fact, Apple defines an entire domain-specific language for Code Signing Requirements. As an introduction to the language, here's an annotated version of a typical code signing identity (the one for Apparency itself):
anchor apple generic /* signing certificate was issued by Apple, trusted by Apple Root CA */ and identifier "com.mothersruin.Apparency" /* and app has this bundle identifier */ and ( certificate leaf[field.1.2.840.113622.214.171.124.9] /* and signing certificate is the Mac App Store one */ or ( certificate 1[field.1.2.840.1136126.96.36.199.6] /* ... OR was issued by the Developer ID intermediate CA */ and certificate leaf[field.1.2.840.1136188.8.131.52.13] /* ... for Developer ID Application use */ and certificate leaf[subject.OU] = "936EB786NH" ) ) /* ... for the developer with this team ID */
The team ID is an identifier that Apple assigns to each registered developer; every Developer ID certificate that Apple issues to that developer will have the same team ID, and so will meet the latter part of this requirement. (You'll see the same team ID in parentheses in the Signed By identity in Apparency's info pane.) This scheme allows the developer to renew their Developer ID certificate between app updates, without breaking any existing requirements.
The alternative check for the Mac App Store certificate allows the same resources to be shared between a Mac App Store version of the app, and the same app distributed outside the store; this is part of the default code signing identity, and doesn't necessarily mean that both versions actually exist.
A code signing requirement will usually specify an
identifier, which looks much like the bundle identifier. This is actually the code signing identifier, and 99% of the time, it is indeed the same as the bundle identifier. But depending on how the code was signed, it is possible for these to differ. The code signing identifier is stored separately within the code signature; the bundle identifier, if any, is stored in the Info Property List. Differences in the code signing identifier are most common with bare (unbundled) executables, since these often lack bundle identifiers (or embedded Info.plist files at all). But by default,
codesign(1)sets the code signing identifier from the bundle identifier.
Within Apparency, you'll find the code signing identity in the Launch Information inspector. Here, Apparency adds syntax highlighting and comments to improve the readability of code signing requirements. For example, as shown above, a requirement often checks for the presence of a specific extension in a certificate, using a multi-part, numeric object identifier (OID) value, such as 1.2.840.1136184.108.40.206.13. Where possible, Apparency adds a comment that gives the name for that OID, such as Apple Developer ID Application Signing.
How is the applicable Gatekeeper policy determined?
As described above, there are a number of possible Gatekeeper statuses, most of which refer to a policy name such as Notarized Developer ID.
When determining the Gatekeeper status, Apparency assumes that the default Gatekeeper policies are in effect. Gatekeeper policies are expressed using the code signing requirements language described above. For example, the Gatekeeper policy for Mac App Store looks like this:
anchor apple generic and certificate leaf[field.1.2.840.1136220.127.116.11.9] exists /* Apple Mac App Signing (Release) */
macOS has a default set of such policy rules, which you can see using this command:
/usr/sbin/spctl --list --type execute
When “assessing” code for execution, Gatekeeper evaluates each of these requirements in turn, and the first one that matches (if any) is the applicable policy. Most of the policies are allow types, meaning that matched code is allowed to run, but there are also deny types (such as Unnotarized Developer ID), which mean that matched code is prohibited from running.
If none of the policy requirements are matched, Apparency will simply show Rejected.
If you want to see the underlying code signing requirement that produced the Gatekeeper policy name, use the Launch Information inspector.
Apparency assumes the default set of Gatekeeper policies, rather than trying to query its policy
database. If you try the
spctl(8) command above, you'll notice it requires authorization, even though
it doesn't change anything. This — combined with the fact that customizing Gatekeeper policies is
uncommon — is why we don't go beyond the defaults.
Apparency doesn't report Gatekeeper status where it isn't actually used by macOS. The status is reported as Not evaluated for the following types of components:
- Traditional kernel extensions, which have special requirements that go beyond those for normal apps. For one, the Apple-issued Developer ID certificate must be explicitly approved for signing of kernel extensions, so an additional level of Apple review is involved. There's also an entire kernel extension user consent mode on
- iOS apps on Apple Silicon Macs, which can only be installed through the App Store. Although there is a Gatekeeper policy applicable to Mac App Store apps, there has never been one for iOS App Store apps, for whatever reason. (Apparency applies this exception only if the iOS app is validly signed by the App Store, however.)
- XCFrameworks, which are used by Xcode but are neither directly run nor dynamically linkable. The platform-specific frameworks inside are, of course, dynamically linkable, and Apparency will evaluate Gatekeeper status for those, unless they are ...
- Components for the watchOS, tvOS or visionOS platforms, since these do not run on macOS. This exception is mainly for an XCFramework, since you're unlikely to encounter these elsewhere. (iOS components are not generally excluded, since they might run on Apple Silicon Macs, but see the exception above.)
How are launchd jobs associated with a component?
In macOS, system daemons and agents are launched and managed by
launchd job definition property lists installed into /System/Library/LaunchDaemons or
/System/Library/LaunchAgents. The format of these plists is documented at
Historically, in order to activate a third-party daemon or agent, the job definition plist had to be installed into /Library/LaunchDaemons or /Library/LaunchAgents, often via a macOS Installer package. (For agents — but not daemons — there is also the Library/LaunchAgents subfolder of your home folder. This subfolder can be written without special permissions — at least by non-sandboxed apps — but apps need to keep it up-to-date for every user. Nevertheless, this subfolder is used and abused far and wide.)
Over the years, macOS has acquired other methods of activating third-party daemons and agents.
Way back in Mac OS X 10.6 (Snow Leopard), the SMJobBless() API added a way to for apps to install privileged helper applications. This is essentially a launchd daemon, but delivered in the app bundle at Contents/Library/LaunchServices. After administrator authentication, the executable is copied to /Library/PrivilegedHelperTools. The executable must have an embedded launchd job definition template, which macOS uses to create a plist in /Library/LaunchDaemons.
Fast forward to macOS 13 (Ventura), where the
introduces another way for apps to bundle and install launchd daemons or agents. In this case, the launchd
job definition plists are delivered in the app bundle at Contents/Library/LaunchDaemons or
Contents/Library/LaunchAgents, and point to a daemon or agent executable elsewhere in the bundle
(using a new
BundleProgram key). This new scheme appears to be “native” to launchd,
in that the job definition plist and executable are both used directly from their place in the app bundle,
and one can see the bundle information in
Apparency collects information about launchd job definition plists in all of these
locations, and associates each component with any launchd jobs that reference that component's executable (via the
BundleProgram keys). In other words,
these are the launchd jobs that could cause the component to be launched automatically by macOS.
To see this information for the selected component, use the Launch Information inspector.
Where apps use
SMAppService, Apparency gives the daemon or agent executable components a special Kind: “Privileged helper application” for the older scheme, or “Background app service daemon” or “Background app service agent” for the new one. These will also have a non-zero number of Launchd Jobs in the Provides section of the info pane.
What are launch constraints?
A launch constraint is a set of requirements, applied to a specific executable component, which must be met before macOS will allow that component to start.
There are two classes of launch constraint supported by macOS:
- An explicit launch constraint is defined with a “constraint dictionary,”
which is a property list file with an
Apple-defined structure. When the component is signed,
this property list is supplied to
codesign(1), using an option such as
‑‑launch‑constraint‑parent— or via an Xcode build setting, such as
LAUNCH_CONSTRAINT_PARENT. The constraint is embedded in the signed Mach-O binary, so that macOS can find it when the component is launched.
- An implicit launch constraint applies only to executables that are part of macOS itself (sometimes called “platform binaries” by Apple). macOS defines a number of “constraint categories,” each of which implies a specific set of requirements. Individual macOS executables are assigned to specific constraint categories through the trust cache. When the component is launched, macOS looks up the constraint category and finds the appropriate constraint from that.
The classification of launch constraints as explicit or implicit is our terminology, so it may not match what you read elsewhere. Apple documents only the explicit form.
Every launch constraint has an associated type. This type determines which process — relative to the component being launched — must meet the requirements of the constraint:
- The Parent Process constraint
must be met by the process that directly launches the component, i.e. using
execve(2). For a launchd daemon or agent, or an XPC service, this ought to be
- The Responsible Process constraint must be met by the process that triggered the component to launch, which might be different from the process doing the spawn. For an XPC service, the responsible process is the app that uses that service. Otherwise, it tends to be the same as the component itself; in particular, a launchd daemon or agent does not get a distinct responsible process, perhaps because a single launchd job can be shared by multiple clients.
- The Self constraint must be met by the component itself.
- The Loaded Libraries constraint
must be met by each library that is dynamically loaded by the component. This applies only if the
com.apple.security.cs.disable-library-validationentitlement is used to disable the library validation feature of the hardened runtime, and is a way of relaxing such validation selectively instead of entirely.
It is always the launching of the component that defines the constraint that is gated, even though some types of constraints are requirements that other processes must meet.
In addition to the above, there is a fifth type: like a Self constraint, the Launchd Job constraint
applies to the component itself, but instead of being stored with the code signature, this constraint
is stored in the launchd job definition plist that references the component (under the
SpawnConstraint key). This is meant to ensure that a launchd job, once installed and
allowed to run, won't be tricked into running an entirely unrelated executable.
To see the launch constraints in Apparency, use the Launch Information inspector. Apparency shows whatever types of constraints are defined for that component (including Launchd Job ones). If you open a component of macOS itself, Apparency will use the constraint category (from the trust cache) to find any implicit constraint, and show this in the same way.
Although Apparency gets the constraint category from the macOS trust cache, the mapping from that category to the actual constraint isn't readily available from the running version of macOS. Instead, Apparency carries its own copy of these constraint definitions, extracted from a recent version of macOS. Depending on which version of macOS you're running — and when Apparency was last updated — it's possible for these constraint details to differ. To see the entire set of constraints that Apparency is using — and which version of macOS they were extracted from — use Window > Implicit Launch Constraints.
As of macOS 14 (Sonoma), only explicit launch constraints are documented by Apple. For our understanding of implicit launch constraints — and especially how to extract the constraint categories and read the trust cache — we are particularly indebted to Csaba Fitzl, who did the hard work of reverse-engineering this stuff and documenting it.
Examining and Understanding Entitlements
What do the different Provisioned Entitlement statuses mean?
When viewing the entitlements with Component > Show Entitlements, you may see an additional column. This appears only if the selected app has a provisioning profile, and shows the provisioned status of each entitlement. The meaning of each status icon is as follows:
|Provisioned Entitlement Status
|The entitlement is one that requires provisioning, and is allowed by the provisioning profile. Such entitlements are usually for an Apple service — such as iCloud or push notifications — or for a macOS feature to which Apple has decided to restrict access, such as system extensions.
|The entitlement is one that requires provisioning, but it is not allowed by the provisioning profile. As a result, macOS will likely prevent the app from opening. (The macOS kernel will intentionally crash the app when it finds that it is using entitlements that are not permitted by the provisioning.)
|The entitlement is not allowed by the provisioning profile, but is one of a subset of entitlements that are only
quasi-restricted by Apple. These include
com.apple.security.application-groups (which allows
the app to share “group containers” with other apps from the same developer) and
com.apple.application-identifier (which is a meta-entitlement used to identify the app and developer).
Because provisioning of these is not strictly enforced, there will likely be no adverse impact on the launching
or operation of the app.
|The entitlement does not require provisioning at all. Generally, the entitlements related to App Sandbox and runtime hardening fall into this category.
What are “merged entitlements” and what do they have to do with PPPC Profiles?
Apparency allows you to select an app or other executable and show the entitlements that it requests. However, since an app might contain a large number of executable components, each which might have its own entitlements, Apparency also provides a way to see a high-level view of all entitlements requested by all components. For example, this can be useful when you are creating a Privacy Preferences Policy Control (PPPC) Profile, because it tells you (indirectly) what capabilities the app will request, and how you want macOS to prompt (or not) the end users.
If this sounds a bit hand-wavy, that's because we are not Mac admins, and have never built a PPPC Profile ourselves. We hope that this feature is useful, but if you have any feedback or suggestions in this area, please do contact us.
To get this merged view of entitlements, choose File > Show Entitlements for All Executables (Cmd-Control-A).
In this view, Apparency shows every entitlement key that is requested by at least one executable component. In some cases, the keys might be all that you need, in order to see what the software wants to do. But if you need more information, Apparency also merges together all of the distinct values for a given key. If all executables use the same value (such as a Boolean YES), that will also be the merged value. But if different executables use different values (such as string identifiers), Apparency will combine these into an array (or if it's already an array, it will combine the distinct values).
The icon in the rightmost column tells you how Apparency created the merged value: either the same value was found for all executables, or multiple values were merged together. If you move the pointer over the icon, a tooltip will show how many executables requested this particular entitlement.
If you're creating a PPPC Profile, you may want to allow a specific “service” for all of the
executables that request a related entitlement. For example, perhaps you want to allow the
AddressBook service for executables that request
If you select that entitlement, and choose Copy App Identities for PPPC Profile,
Apparency will construct the array-of-dictionaries structure that a PPPC Profile uses to establish
and put this on the clipboard. Then you can go to your property list editor and paste directly under the
Apparency doesn't understand the mapping between entitlements and PPPC services, so you will still need to infer that part. The intent here is only to reduce the tedium of gathering identifiers and code signing identities (requirements) from multiple components.