Conventional wisdom is that a ‘quarantine flag’ is attached to files which are downloaded from the Internet, using most but not all apps, and is used to determine whether an app or other executable code needs to undergo a full first run check by Gatekeeper.
Although there’s nothing inherently wrong with that, there’s a great deal more to quarantine and its extended attribute than that. For a start, the majority of items on your Mac which carry a quarantine flag aren’t apps at all, but non-executable documents. And in most cases, macOS doesn’t even know why they are there.
Quarantine and the com.apple.quarantine extended attribute (xattr) originated in macOS 10.5 in 2007, although Gatekeeper didn’t appear until 10.7 in 2011-12, at around the same time that sandboxing was introduced. Apple’s original and updated accounts for users only refer to quarantine of apps for Gatekeeper’s checks.
In this first of two articles, I look at how quarantine works for apps and other executables, including both code and scripts.
All files which are downloaded from the Internet, using HTTPS or HTTP, in email messages, and by other means, can have a quarantine flag attached to them by the app which performs the downloading. Commonly-used apps such as Safari, third party browsers, and most mail clients respect this, but apps giving access to torrents and the command tool
curl don’t. If you download an archive or installer from a website using your browser, a flag will normally be attached. But custom app download-installers and most updaters either don’t set the flag at all, or, when one is set, remove it (for example, Sparkle-based updaters).
It’s essential to remember that the quarantine flag is an opt-in system, and not one imposed by macOS itself. Any developer, including malware authors, can download files from the Internet without setting the flag on them, and any app on your Mac can change or strip the quarantine flag on any item to which it has write permission. The use of these flags in security is very much a gentleman’s agreement, which is easily broken when software doesn’t behave like a gentleman.
The quarantine flag is among the stickiest of all xattrs. When you unZip an archive which has been flagged, the xattr is normally propagated to all items which are saved from that, a behaviour which ensures that compressed apps retain their flag when uncompressed, for example. This isn’t, though, imposed by macOS, and some tools and utilities which can decompress archives may not follow this behaviour; the bundled Archive Utility does, though. [Thanks to Al Varnell for reminding me of this – it’s yet another example of how this is an opt-in system.]
In macOS Mojave, a typical quarantine xattr consists of a Unicode string of the form
with the components:
- the quarantine value in hexadecimal,
- the time at which the xattr was attached, in hexadecimal,
- the app or agent responsible for creating the xattr (normally the downloading app too),
- a UUID referring to the entry for this quarantine flag in the QuarantineEvents database
separated by semi-colons.
The QuarantineEvents database is an SQLite database at ~/Library/Preferences/com.apple.LaunchServices.QuarantineEventsV2. There appears to be no system-level equivalent, so each user is only able to access details of their own quarantine events, not those of other users. At no time does macOS appear to perform any maintenance or checks on that database. If a quarantine flag bearing a UUID reference is removed, that entry should also be removed from the database, but macOS doesn’t appear to check that it is, nor does it scan through database entries to check whether their quarantine flags still exist.
When an app or other executable is run, its quarantine xattr is checked before opening. If the executable code hasn’t been cleared previously by a full Gatekeeper check, that is deemed to be its first run, and a full check is performed. If that is successful, the quarantine value on all checked executable code is changed from (for example)
00000000 10000011 = 83
00000000 11000011 = C3
Subsequent attempts to run that code are then no longer blocked for the first run Gatekeeper check to be performed. Successful completion of that first run check doesn’t alter the quarantine xattrs attached to non-executable files within an app bundle, though.
Earlier versions of macOS have used other bits in the quarantine value too. For example, in Sierra and earlier an app which has passed first run and been successfully opened could end up with a value of
00000000 11100011 = E3
the lower-order bit signifying that the app had also been run. These appear to have fallen into disuse in Mojave.
The QuarantineEvents database contains and retains additional information for these flags, which includes the reason for their attachment in the LSQuarantineType value, which includes:
- LSQuarantineTypeSandboxed, which is only attached to documents (see the next article)
each of which is self-explanatory.
In normal circumstances, quarantine xattrs which are attached to apps and other executables remain in place until that app is removed.
Developers who ship apps which are signed or notarized need to check that those will successfully pass through Gatekeeper first run checks before distributing those apps. I have discussed ways of doing this here.
Executable scripts with quarantine flags may not undergo Gatekeeper checks, because most are unsigned. The presence of a quarantine flag on a script therefore normally results in macOS blocking it from running until that flag has been removed. This is an issue to which I will return in the next and concluding article, looking at quarantine for documents and other non-executable files.
Thanks to Jonathan Levin for pointing out that the Gatekeeper mechanism is only triggered by launches from the GUI, not the command line. This is because it’s the framework which checks xattrs at app startup. Gatekeeper checks are normally followed by an XProtect scan looking for known malware signatures. When 10.15’s changes become clearer, I will look again in detail at the steps involved in app startup.
Updated with addition 2240 29 April 2019.