Skip to content

Quarantine Events and Gatekeeper: Tracing Mac Downloads

Decode the com.apple.quarantine xattr, query QuarantineEventsV2, and use spctl, codesign and unified logs to trace downloads and Gatekeeper decisions.

Published on 6 min read

When an investigation involves a malicious installer, a phishing attachment or an unexpected application, the first questions are usually: where did this file come from, when did it arrive, and did the user open it? On macOS, the quarantine mechanism and Gatekeeper answer much of that. The com.apple.quarantine extended attribute tags downloaded files, the QuarantineEventsV2 database keeps a per-user history of downloads that often survives deletion of the file, and Gatekeeper's decisions are recorded in the unified log. This article explains how to read each of these and how to combine them.

How quarantine works

Applications that opt in to file quarantine (via the LSFileQuarantineEnabled Info.plist key, or because they are sandboxed) cause macOS to attach a com.apple.quarantine extended attribute to files they write. When the user first opens a quarantined executable, disk image or installer, Gatekeeper evaluates it: code signature, notarization status and XProtect signatures. If the user approves the launch, the attribute is updated to record that approval rather than removed. See the quarantine attribute and Gatekeeper glossary entries for a short summary.

The com.apple.quarantine attribute

Format

The value is a semicolon-separated string:

flags;timestamp;agent;UUID
0083;66f7c2a1;Safari;6E4C2B1A-3F4D-4E8B-9A10-7C2D5E6F8A90
FieldEncodingMeaning
flags4 hex digitsBit field describing the quarantine state
timestampHex, seconds since 1970-01-01 UTCWhen the file was quarantined
agentStringName of the application that wrote the file, e.g. Safari, Google Chrome, sharingd for AirDrop
UUIDUUID stringLinks to LSQuarantineEventIdentifier in QuarantineEventsV2; may be empty

Note that the timestamp in the attribute is Unix time in hexadecimal, while the database uses Mac Absolute Time. Convert carefully:

# Read the attribute
xattr -p com.apple.quarantine ~/Downloads/example-installer.dmg

# Convert the hex timestamp to UTC
date -u -r $((16#66f7c2a1))

Interpreting flags

Apple does not publicly document the flag bits. Commonly reported values from reverse engineering include 0x0001 (downloaded), 0x0002 (created by a sandboxed app) and 0x0040 (user approved the launch through Gatekeeper). In practice, a value such as 0083 changing to 00c3 on the same file is widely interpreted as the user having opened and approved it. Treat flag interpretation as supporting evidence, confirm on a test system of the same macOS version, and avoid resting a conclusion on flags alone.

Propagation

Quarantine follows files in useful ways. Archive Utility applies the attribute to files extracted from a quarantined archive, and mounting a quarantined disk image marks its contents as quarantined. Third-party unarchivers may or may not propagate it. Copies within APFS and HFS+ keep the attribute; on file systems without native xattr support, macOS stores it in AppleDouble ._ files, which are easily lost.

The QuarantineEventsV2 database

Location and schema

Each user has a SQLite database at:

~/Library/Preferences/com.apple.LaunchServices.QuarantineEventsV2

It contains one table, LSQuarantineEvent, with these columns:

ColumnContent
LSQuarantineEventIdentifierUUID matching the last field of the xattr
LSQuarantineTimeStampMac Absolute Time (seconds since 2001-01-01 UTC)
LSQuarantineAgentBundleIdentifierBundle ID of the downloading app, e.g. com.apple.Safari
LSQuarantineAgentNameDisplay name of the agent
LSQuarantineDataURLStringDirect URL of the downloaded data
LSQuarantineOriginURLStringPage the download was initiated from, when known
LSQuarantineOriginTitleTitle of the origin, when recorded
LSQuarantineSenderName / LSQuarantineSenderAddressSender details, populated by some agents such as Mail
LSQuarantineTypeNumberNumeric type of the quarantine event
LSQuarantineOriginAliasAlias data, rarely populated

The database does not record the local file path. The UUID is the join key between a file on disk and its record.

Queries

-- Full download timeline
SELECT
  datetime(LSQuarantineTimeStamp + 978307200, 'unixepoch') AS event_utc,
  LSQuarantineAgentBundleIdentifier AS agent,
  LSQuarantineDataURLString AS data_url,
  LSQuarantineOriginURLString AS origin_url,
  LSQuarantineEventIdentifier AS uuid
FROM LSQuarantineEvent
ORDER BY LSQuarantineTimeStamp;

-- Record for a specific file (UUID from its xattr)
SELECT *
FROM LSQuarantineEvent
WHERE LSQuarantineEventIdentifier = '6E4C2B1A-3F4D-4E8B-9A10-7C2D5E6F8A90';

Because records outlive the downloaded file, the database often proves that a payload was downloaded even after the attacker or user deleted it. Correlate record times with FSEvents created and removed events in ~/Downloads, with kMDItemWhereFroms in Spotlight metadata, and with browser history from Safari browser forensics.

Quarantine Parser reads the database with its -wal, carves deleted rows and matches com.apple.quarantine xattr listings to their records in the browser, without uploading anything.

Gatekeeper, notarization and XProtect

Three layers interact when a quarantined file is opened:

  • Gatekeeper (implemented by syspolicyd) checks that the code is signed by an identified developer and, for software distributed outside the Mac App Store, notarized by Apple.
  • Notarization is Apple's automated scan of developer-submitted software. The resulting ticket can be stapled to the app or fetched online.
  • XProtect provides signature-based detection of known malware, checked on first launch and, on recent macOS releases, periodically by XProtect Remediator.

When an app from an unusual location is launched while quarantined, macOS may run it from a randomized read-only mount (App Translocation), which explains paths under /private/var/folders/.../AppTranslocation/ in logs and process listings.

Assessing a sample

These commands are read-only and safe to run on a copy of the suspect file on an analysis Mac:

# Gatekeeper assessment
spctl --assess --type execute -vv /Volumes/evidence/Applications/Example.app

# Signature details: identifier, Team ID, authority chain, flags
codesign -dv --verbose=4 /Volumes/evidence/Applications/Example.app

# Verify signature integrity
codesign --verify --deep --strict -v /Volumes/evidence/Applications/Example.app

# Gatekeeper global status on the live system
spctl --status

An ad-hoc signature, a revoked certificate, a missing or mismatched Team ID, or an app that fails codesign --verify after being modified are all strong leads. Compare the Team ID with the vendor the user believed they were installing.

Unified log evidence

Gatekeeper, XProtect and quarantine decisions are logged. Useful starting predicates (see Unified Logs forensics for collection and syntax):

# Gatekeeper and system policy decisions
log show --info --last 3d --predicate 'process == "syspolicyd"'

# Subsystem-based variant
log show --info --last 3d --predicate 'subsystem BEGINSWITH "com.apple.syspolicy"'

# XProtect scanning activity
log show --info --last 3d --predicate 'process CONTAINS[c] "xprotect"'

# From a collected archive
log show /cases/MBP-IR-01/system.logarchive --info \
  --predicate 'process == "syspolicyd" AND eventMessage CONTAINS[c] "Example.app"'

Message formats change between releases and many fields are redacted as <private>, so search for the bundle name, path fragments or Team ID rather than exact strings. Retention is limited, so collect logs early.

Recent macOS versions also keep system policy state in databases under /private/var/db/SystemPolicyConfiguration/, which can record assessments and provenance of launched code. Their schemas are undocumented and change between releases; parse them only with tooling validated against your target version.

Pitfalls and caveats

  • Not all downloads are quarantined. curl, wget, scp, rsync, git and many third-party apps do not set the attribute. Attackers who already have a shell commonly fetch second stages this way specifically to avoid Gatekeeper prompts, so a missing attribute on a suspicious binary is itself informative.
  • Attributes are removable. xattr -d com.apple.quarantine or xattr -c strips the attribute without touching the database. A file without quarantine plus a matching database record suggests deliberate removal.
  • The database is editable. Users can clear QuarantineEventsV2 with sqlite3. Check for gaps in the timeline, WAL contents and free pages if clearing is suspected.
  • Epoch mismatch. The xattr uses hexadecimal Unix seconds; the database uses Mac Absolute Time. Mixing them produces errors of 31 years.
  • Per-user scope. Each user has their own database. Collect it for every account, including service or admin accounts an attacker may have used.
  • Undocumented flags. Quarantine flag meanings come from research, not Apple documentation. Hedge accordingly in reports.

Frequently asked questions

Is every downloaded file quarantined on macOS?

No. Only apps that opt in to file quarantine, such as Safari, Chrome, Mail, Messages and AirDrop, set com.apple.quarantine. Command-line tools like curl, wget and scp do not, so files fetched that way have no quarantine attribute and usually no QuarantineEventsV2 record.

The last field of the com.apple.quarantine value is a UUID. Search for it in the LSQuarantineEventIdentifier column of the LSQuarantineEvent table in ~/Library/Preferences/com.apple.LaunchServices.QuarantineEventsV2 to recover the download URL, origin page and agent.

Do QuarantineEventsV2 records disappear when the file is deleted?

Generally not. Records persist after the downloaded file is removed, which makes the database valuable for proving a download happened. They can, however, be removed by a user or attacker editing the database, so verify integrity and corroborate with other sources.

Related guides

05 · Execution & Persistence

Investigating launchd Persistence on macOS

Find and analyze macOS persistence: LaunchAgents, LaunchDaemons, launchctl, Background Task Management (sfltool dumpbtm), cron, periodic and profiles.

Read guide