Quarantined: more about the quarantine extended attribute

In yesterday’s article about extended attributes, xattrs, I wrote a little about the quarantine xattr, which determines whether an app requires a full Gatekeeper check. And I’m afraid that I got some of the information wrong, which I have now corrected. This article looks in more detail at the quarantine xattr and the processes which it is involved in, and hopefully will prove completely accurate.

Look at the com.apple.quarantine xattr for a downloaded Zip archive, and it might read
0083;5991b778;Safari.app;9CE7DFC3-9FC3-45E7-A922-72CD4C95A1AB

Recall that these represent, in order, the Gatekeeper score, the system time of download, the downloading app or agent, and a UUID for the event. When you unzip that archive, the com.apple.quarantine xattr of the app might then read
0183;5991b778;Safari.app;9CE7DFC3-9FC3-45E7-A922-72CD4C95A1AB

All the contents of the original xattr are inherited by the unzipped app, although in this case a single bit has been set in the high-order byte of the Gatekeeper score. In binary, that is
00000001 10000011

When you then run the app, Gatekeeper will perform its full check; assuming that it passes and is authorised for use on your Mac and then runs, the xattr is changed to
01e3;5991b778;Safari.app;9CE7DFC3-9FC3-45E7-A922-72CD4C95A1AB
with all the elements the same, except for the Gatekeeper score, which has become
00000001 11100011
with two bits set: the higher-order bit indicates that it has passed the full Gatekeeper check, and the other that the app has now been run as well.

xattred2

Another Zipped archive arrived with the following xattr:
0083;5991876c;Safari.app;BC4DFC58-0D26-460D-9688-81D119298642

The app inside it inherited exactly the same xattr this time, giving it a binary Gatekeeper score of
00000000 10000011

Clearing that through quarantine gave it a score of 00C3, or in binary
00000000 11000011
and once that ran it became 00E3, or
00000000 11100011

If you develop and sign an app, it is very important to check that the signature clears Gatekeeper’s checks. I am currently particularly aware of this, because some readers have downloaded my signed apps from this blog only to find that Gatekeeper claims that they are not signed by a recognised developer.

Apple recommends two separate checks before posting a download. The first is a local check which merely ensures that the signature is correct for the app:
codesign --verify --verbose appname.app
which should return
appname.app: valid on disk
appname.app: satisfies its Designated Requirement

The snag is that does not put the app through the full Gatekeeper check. Indeed, when Xcode builds an app, it does not put any quarantine xattr on the app, which is very convenient for development purposes. The normal routine then is to email a Zip archive of the app to yourself (if you are confident that your mail client adds a quarantine xattr), or to upload it to your website and download it from there using a browser such as Safari, which will set the quarantine xattr.

This is awkward, and for very large apps wastes time and effort. But you can work around this by adding your own quarantine xattr to your app, to tell Gatekeeper to perform a complete check, without the app ever leaving your Mac. The most important information required to do this is an appropriate Gatekeeper score: as shown above, 0083 should be ideal. So you may find adding an xattr using the Terminal command
/usr/bin/xattr -w com.apple.quarantine "0083;5991b778;Safari.app;" appname.app
is sufficient to trigger the full check. If not, you will need to obtain a UUID using the Terminal command
/usr/bin/uuidgen
which should be suitable for pasting at the end, making the command something like
/usr/bin/xattr -w com.apple.quarantine "0083;5991b778;Safari.app;BC4DFC58-0D26-460D-9688-81D119298642" appname.app

If that doesn’t trigger a full check, then you will have to insert the UUID into the QuarantineEvents database using a command of the form
/usr/bin/sqlite3 ~/Library/Preferences/com.apple.LaunchServices.QuarantineEventsV2 "INSERT INTO \"LSQuarantineEvent\" VALUES('BC4DFC58-0D26-460D-9688-81D119298642',5991b778,NULL,'Safari.app','http://dummy.com/file.zip',NULL,NULL,0,NULL,'/Users/nyname/appname.app',NULL);"

You can check that record for the UUID using the command
/usr/bin/sqlite3 ~/Library/Preferences/com.apple.LaunchServices.QuarantineEventsV2 "SELECT * FROM LSQuarantineEvent WHERE LSQuarantineEventIdentifier == 'BC4DFC58-0D26-460D-9688-81D119298642'"

There may well be another way of achieving this by changing security policy rules using the complex command spctl. If you want to check that your app runs correctly without uploading and downloading it, setting the quarantine xattr looks far more straightforward.

xattred3

None of this, though, solves my own problem. I can download a copy of Consolation from here and it sails through Gatekeeper’s full checks without fault. But some readers find that it, or yesterday’s download of xattred, are reported as being from an unidentified developer. I do not understand why this anomaly occurs. The saving grace is that, for the time being at least, users can ignore the full Gatekeeper check using the Finder’s Open command.

Given this complexity and its quirks, perhaps that is just as well.

This is partly based on excellent information provided by klanomath on StackExchange’s AskDifferent.