skip to content

On macOS, an app downloaded through a web browser is blocked on first launch, but the identical file fetched with curl opens immediately. What mechanism explains the difference?

level: juniorimportance: must knowfreq 66%

answer

  1. it depends on who wrote the file
  2. an extended attribute, not a scan
  3. curl sets no metadata
  4. first-launch assessment only
  5. xattr -l shows the tag

basics

~20 s

The browser tags its download with the com.apple.quarantine extended attribute; curl does not. Gatekeeper only evaluates quarantined files, so the tagged copy is checked for a valid signature and notarization on first launch while the untagged copy simply runs.

solid answer

~40 s

Quarantine is an opt-in flag, not a property of the file's contents. Applications that download content — browsers, mail clients, chat apps — attach the `com.apple.quarantine` extended attribute to what they write. On first launch, Gatekeeper sees that attribute and performs an assessment: is the code signed, is the signature intact, and has the developer had the app notarized by Apple? If any of that fails the user gets the "cannot be opened because the developer cannot be verified" or "is damaged" dialog. `curl` is a plain HTTP client with no quarantine support, so the file it writes carries no attribute and Gatekeeper never assesses it. You can inspect the attribute with `xattr -l` and clear it with `xattr -d com.apple.quarantine`, and check what Gatekeeper thinks with `spctl --assess --verbose`.

code

bash · 12 lines
bash
# Same bytes, different provenance metadata
curl -sLO https://example.com/Example.zip
xattr -l Example.zip            # prints nothing

# A browser-downloaded copy carries the flag:
#   com.apple.quarantine: 0083;65f0a1b2;Safari;

# Ask Gatekeeper what it thinks of an app bundle
spctl --assess --verbose=4 /Applications/Example.app

# Clear the flag (only for code whose origin you trust)
xattr -d com.apple.quarantine /Applications/Example.app

go deeper

for a junior

Recall that browser downloads are tagged as quarantined and get checked on first open, that curl-fetched files are not tagged, and that the tag lives in an extended attribute you can list with xattr.

for a middle

Explain the assessment itself: signature integrity, Developer ID identity, and a notarization ticket that is either stapled into the bundle or looked up online — and that clearing the attribute skips the whole thing.

for a senior

Separate the three layers cleanly (signing, notarization, App Store review), diagnose "is damaged" as a broken seal after a post-signing edit, and recognise App Translocation as the cause of an app that cannot find its neighbouring files.

for a principal

Own distribution strategy: which builds get Developer ID plus notarization, how the notary step lands in CI without leaking credentials, and where the organisation accepts unnotarized internal tooling versus requiring the full chain.

## Quarantine is metadata, not a scan The first thing to understand is that macOS is not inspecting the bytes of the file to decide it is "from the internet". It is reading a label that the downloading application chose to attach. That label is an extended attribute named `com.apple.quarantine`, and applications opt into setting it — historically via the `LSFileQuarantineEnabled` key in their Info.plist, and automatically for sandboxed apps. ```sh $ xattr -l ~/Downloads/Example.app com.apple.quarantine: 0083;65f0a1b2;Safari;... $ xattr -l /tmp/example-from-curl.app # nothing — no attribute at all ``` That is the whole difference. Safari, Chrome, Mail and Messages set it. `curl`, `wget`, `scp`, `git clone` and a tarball extracted with `tar` do not. It is a provenance hint, deliberately cheap, and it is why "download it with curl instead" is a well-known workaround — and equally why it is a bad security habit. ## What Gatekeeper does when it sees the flag Gatekeeper is the policy engine that runs on **first launch of a quarantined executable**. Its assessment asks: 1. **Is the code signed at all, and is the signature intact?** A signature covers every byte of the bundle. If someone edited a file inside the app after signing, the seal is broken. That is what the confusing "the application is damaged and can't be opened" dialog usually means — not disk corruption, but a broken or missing signature. 2. **Who signed it?** A Developer ID certificate issued by Apple to an identified developer, an App Store signature, or nothing recognisable. 3. **Is it notarized?** Notarization is a separate step in which the developer uploads the signed build to Apple's notary service, Apple runs an automated malware scan, and returns a ticket. The developer normally *staples* that ticket into the bundle with `xcrun stapler staple`; if it is not stapled, macOS looks it up online. Only if all of that passes does the app launch quietly. Otherwise the user gets a blocking dialog. Once the user approves an app, the decision is remembered and the quarantine flag is cleared, which is why the prompt appears exactly once. ## Signing, notarization and the App Store are three different things Candidates routinely merge these: - **Code signing** proves integrity and identity. It says "this is the bundle developer X produced, unmodified". Anyone with a Developer ID certificate can sign, and signing happens on the developer's machine. - **Notarization** is Apple's automated check of an already-signed build. It is not review, no human looks at it, and it does not judge quality — it scans for malware and for signing and hardening mistakes. It requires the Hardened Runtime. - **App Store review** is a human process with its own rules, and distributes through a different channel entirely. An app distributed outside the App Store on modern macOS needs the first two. ## App Translocation, the surprising side effect When a quarantined app is launched from a location like `~/Downloads`, macOS may run it from a randomised, read-only path rather than where it sits — a feature usually called App Translocation or Gatekeeper path randomisation, added in macOS 10.12. It exists to stop an attacker shipping a legitimate app in a disk image next to a malicious library it will load relatively. The visible symptom is an app that cannot find files it expects beside itself, or that appears not to save its settings, until the user drags it to `/Applications` — which clears quarantine and stops the translocation. ## Clearing quarantine, and why not to make it a habit ```sh $ spctl --assess --verbose=4 /Applications/Example.app /Applications/Example.app: rejected source=Unnotarized Developer ID $ xattr -d com.apple.quarantine /Applications/Example.app ``` Removing the attribute makes the file look local and Gatekeeper stops assessing it. That is a reasonable move for a binary you built yourself or one whose provenance you verified another way. Doing it reflexively for anything downloaded discards the one check that stands between the user and a trojaned installer. The user-facing approval path has tightened over releases: the old "Control-click, Open" shortcut was the standard way to run an unnotarized app, and in macOS 15 Sequoia that route was removed in favour of an explicit approval in System Settings under Privacy & Security. Expect the exact clicks to keep moving; the underlying quarantine-plus-assessment model has been stable for years.

  • A user reports an app "is damaged and can't be opened". What is your first hypothesis?
    Not disk corruption — a signature problem. The bundle is quarantined and its code signature is missing, broken, or was invalidated by an edit after signing, such as a script that touched a file inside the .app. Verify with `codesign --verify --deep --strict` and `spctl --assess --verbose`, then fix the build so the app is signed and notarized rather than telling users to strip the attribute.
  • What exactly does notarization add that a Developer ID signature does not?
    The signature proves who built it and that it is unmodified; it says nothing about what the code does. Notarization submits that signed build to Apple's automated malware and hardening scan and returns a ticket, so macOS can distinguish "signed by someone" from "signed and screened by Apple". It also gives Apple a revocation lever if the developer's build later turns out malicious.
  • Why is stapling the notarization ticket worth doing if macOS can check online?
    Stapling embeds the ticket in the bundle or disk image with `xcrun stapler staple`, so first launch succeeds with no network round trip. Without it, a machine that is offline or behind a restrictive proxy may fail the lookup and block the app. Stapling makes the result deterministic and removes an Apple service from your users' launch path.

saying these in an interview costs you the question

  • Believing macOS scans the file's contents for malware
  • Saying notarization is the same as App Store review
  • Thinking the check runs on every launch, not the first
  • Assuming curl downloads are somehow trusted or verified
  • Treating "is damaged" as disk corruption rather than a bad signature

context