App first run, quarantine and translocation

A simple account of what happens when you first run an app you have just downloaded runs something like:

  • As the app has its quarantine flag set, it undergoes full first run checks by Gatekeeper.
  • The app undergoes a full signature check, XProtect scan, and its notarization is checked.
  • If they’re OK and it’s notarized correctly, you’re prompted to decide whether to run the app.
  • If you say yes, the quarantine flag is cleared, and the app doesn’t have to go through first run checks again.

As I described earlier this week, that omits app translocation, should the app meet its criteria. Not only that, it treats the quarantine flag, actually an extended attribute or xattr, as having binary state, which is incorrect. The reality is rather more complex in important respects, and has also changed in the last couple of years.

A typical quarantine xattr, com.apple.quarantine, consists of a Unicode string of the form
0083;5991b778;Safari.app;BC4DFC58-0D26-460D-9688-81D119298642
with the components:

  1. 0083, the quarantine value in hexadecimal text,
  2. 5991b778, the time at which the xattr was attached, in hexadecimal text,
  3. Safari.app, the app or agent responsible for creating the xattr (normally the downloading app too),
  4. BC4DFC58-0D26-460D-9688-81D119298642, a UUID referring to the entry for this quarantine flag in the QuarantineEvents database

separated by semi-colons.

When you download an app, perhaps compressed into an archive, that normally has a quarantine value of 0083 attached to it in the xattr. That’s not built into macOS, and some methods of downloading such as curl can obtain files without attaching any quarantine xattr to them. Transfer a file using AirDrop, and the quarantine value is normally 0081 instead.

Quarantine xattrs are sticky due to the way they’re normally respected. When Archive Utility unpacks and decompresses a file with a quarantine xattr, it propagates that xattr to all the resulting files. So an app decompressed from a Zip archive will bear the same quarantine value, on each of its files and folders. Not all decompression utilities follow that, but all the more popular ones do.

Once decompressed from its download, if you follow my advice and move that app to a different folder, such as one of the standard Applications folders, when you run it the normal first run sequence takes place, much as described at the top. The quarantine value is then set to 01c3, and no further first run checks are performed on that app.

If you ignore my advice and run the app in the same folder, it’s translocated and undergoes its first run in a random directory buried deep in hidden folders. That translocated app still undergoes all the normal first run checks, but there are two important differences:

  • The quarantine value on the original app (not its translocated copy) is changed not to 01c3, but only to 00c3, sufficient to dodge the dialog asking you whether you want to run the app.
  • Next time the app is run from that same folder, it’s run in translocation again, and can only escape future translocation if it’s moved out of its original folder and run again.

Over the years, macOS has used the quarantine value in subtly different ways. It now appears that, to escape perpetual translocation, the high order bit must be set, turning the first byte given from 00 to 01. That won’t be done so long as the app hasn’t been moved from its original folder, under the translocation rules.

One popular workaround used by developers to get out of app translocation is to strip the quarantine xattr completely. Provided that’s done after first run checks have been completed, that seems fine, but it’s bizarre that it should be necessary to undo one security mechanism to cope better with another. However, as the only other way to escape translocation jail is to move the app to a different location, there’s no really good solution. As a process, app translocation doesn’t offer any way ahead.

Another important point to note here is that what stops translocation isn’t a change in the full path to the original app, merely the folder in which the app is located. For example, if you download and decompress XProCheck, you’ll see it comes in a folder named XProCheck10. If you move that folder to another folder, even /Applications, and run XProCheck within it, then it will still be translocated. The moment you move the app out of that into a different folder, translocation will cease, and its quarantine value will be set to the magic 01c3. It will finally have completed all its first run checks.

There’s one last thing too. In the past, when quarantine flags were cleared, every file and folder inside the app had its quarantine flag cleared. That doesn’t appear to be the case in Monterey, as only the com.apple.quarantine xattr on the .app bundle folder is cleared now, while those internal to the app are left at 0083.

When Apple introduced app translocation six years ago, apps were very different. Many weren’t even signed, and there was no such thing as notarization. It seems strange that, despite having hardened runtimes, having been scanned for malware by Apple, and been notarized, those same rules should still apply to modern apps distributed outside the App Store.