Gatekeeper and XProtect Evidence on macOS
Gatekeeper, XProtect and XProtect Remediator leave log entries and databases that show which code was assessed, approved, flagged or removed.
- Location
- /var/db/SystemPolicyConfiguration/ExecPolicy
- Proves
- Whether macOS assessed, allowed, blocked or remediated a given program, and which behaviors it flagged
- Timestamps
- Unified log: shown in the time zone recorded with each entry unless --timezone is set; XPdb dt: text date-time; ExecPolicy: Unix seconds
- Access
- root; Full Disk Access; XPdb is a Data Vault on 26.2+
- Retention
- Unified log: days to weeks; databases: until rebuilt (varies)
- Collection
- Aftermath, mac_apt, UAC, log collect, sysdiagnose
Tools
Compare all tools- Unified Log ParserIn browser
- Quarantine ParserIn browser
- logCLI · built into macOS
- mac_aptCLI · open source
- sqlite3CLI · built into macOS
What it is
Gatekeeper is the macOS policy that checks quarantined code on first launch: signature, Developer ID, notarization and XProtect signatures. It is implemented by syspolicyd. XProtect is Apple's signature-based anti-malware (YARA rules and related data in XProtect.bundle). XProtect Remediator is a set of scanners in XProtect.app that run periodically and can remove known malware. From macOS 13 Ventura, the XProtect Behavior Service (Bastion rules) records, but does not block, processes that touch sensitive locations.
None of these keep a single tidy history. Evidence is spread across the unified log, several SQLite databases and version plists.
Where it lives
| Artifact | Path | macOS | Protection |
|---|---|---|---|
| Gatekeeper and exec policy DB | /var/db/SystemPolicyConfiguration/ExecPolicy | Documented since 10.14; provenance_tracking table from 13 | root |
| Notarization tickets DB | /var/db/SystemPolicyConfiguration/Tickets | Documented since 10.14 | root |
| Gatekeeper policy DB | /var/db/SystemPolicy (authority table); /var/db/SystemPolicyConfiguration/SystemPolicy on recent releases | Long-standing, location varies by release | root |
| Last Gatekeeper rejection | /private/var/db/SystemPolicyConfiguration/.LastGKReject; older releases: /private/var/db/.LastGKReject | Current path in Apple's Security source from macOS 15; legacy path before | root |
| Gatekeeper data | /private/var/db/gkopaque.bundle; gke.bundle in /private/var/db or /var/db/SystemPolicyConfiguration depending on release | Current releases | root |
| XProtect data (legacy) | /Library/Apple/System/Library/CoreServices/XProtect.bundle | 10.15-14; fallback on 15+ | SIP |
| XProtect data (primary) | /var/protected/xprotect/XProtect.bundle | 15 Sequoia+ | SIP |
| XProtect Remediator | /Library/Apple/System/Library/CoreServices/XProtect.app | 12.3+ | SIP |
| Behavior Service DB | /var/protected/xprotect/XPdb | 13 Ventura+ | root |
| Behavior Service DB (moved) | /var/protected/xprotect/db/XPdb | XProtectPayloads 156+ | Data Vault from 26.2 |
| Logs | Unified log (/private/var/db/diagnostics) | All | root to read |
From macOS 26.2 the db folder is a Data Vault that even root cannot open on the running system; researchers report that it is readable from a mounted Data volume (Recovery or a local snapshot). The com.apple.provenance xattr (11 bytes) on app bundles holds a key into provenance_tracking.
What it proves
- An app was assessed by Gatekeeper, and whether it was accepted or rejected (
syspolicydlog entries,.LastGKReject). - A user explicitly overrode Gatekeeper (log entries; Endpoint Security
ES_EVENT_TYPE_NOTIFY_GATEKEEPER_USER_OVERRIDEfrom macOS 15). - An app was first allowed to run after download and its cdhash at that time (
provenance_tracking, Ventura+). - XProtect Remediator detected or remediated a named threat (log category
XPEvent.structured; ES eventsES_EVENT_TYPE_NOTIFY_XP_MALWARE_DETECTED/..._REMEDIATEDfrom macOS 13). - A process triggered a Behavior Service rule, with executable and responsible process hashes and team IDs (
XPdb). - Which XProtect and Remediator versions were installed (bundle
Info.plist).
A clean Gatekeeper or XProtect verdict does not prove code is benign; signatures cover known families only.
Key fields
XPdb events table (Ventura+): violated_rule, exec_path, exec_cdhash, exec_signing_id, exec_team_id, exec_sha256, exec_is_notarized, responsible_path, responsible_cdhash, responsible_signing_id, responsible_team_id, responsible_sha256, responsible_is_notarized, reported, profile_hash, dt.
ExecPolicy: provenance_tracking (Ventura+) stores per-app provenance with cdhash; older tables include legacy_exec_history_v4, scan_targets_v2 and executable_measurements_v2. Schemas are undocumented, so inspect with .tables and .schema.
Version plists: CFBundleShortVersionString in each bundle's Contents/Info.plist.
Timestamps
- Unified log:
log showdoes not followTZ; without--timezoneit prints each entry in the time zone recorded when it was written. Pass--timezone UTCfor timelines (see Unified Logs). - XPdb
dt: text date-time (for example2023-06-30 06:34:28); verify the timezone on a test system. - ExecPolicy integer times are Unix seconds; APOLLO's ExecPolicy modules convert them with
UNIXEPOCH.
SELECT dt, violated_rule, exec_path, exec_team_id, responsible_path
FROM events ORDER BY dt DESC;
Retention
Unified log entries age out with the log store (typically days to a few weeks, depending on volume). XPdb records each unique rule fingerprint once per process per boot and is uploaded to Apple; do not expect a complete history. ExecPolicy and SystemPolicy rows persist until macOS rebuilds them. XProtect bundles are replaced on each update.
Collection
# Logs first: they age out
sudo log collect --last 7d --output ./case/system.logarchive
# Databases (root, Full Disk Access)
sudo ditto /var/db/SystemPolicyConfiguration ./case/spc
# Older releases keep SystemPolicy and .LastGKReject in /var/db (the ditto above copies the current .LastGKReject)
sudo cp -p /var/db/SystemPolicy /private/var/db/.LastGKReject ./case/ 2>/dev/null
# db/ is a Data Vault on 26.2+: copy it from a snapshot or from Recovery
sudo cp -p /var/protected/xprotect/XPdb* /var/protected/xprotect/db/XPdb* ./case/ 2>/dev/null
spctl --status
- Aftermath collects XProtect Behavior Service data (from the legacy
/var/protected/xprotect/XPdbpath, notdb/XPdb), XProtect versions and Gatekeeper status. - UAC
files/system/xprotect.yamlcollects the XProtect, XProtect Remediator and MRTInfo.plistfiles. - sysdiagnose includes a log archive.
Parsing
- log on a live system or
.logarchive:
log show ./case/system.logarchive --info \
--predicate 'subsystem == "com.apple.XProtectFramework.PluginAPI" AND category == "XPEvent.structured"'
log show ./case/system.logarchive --info --predicate 'subsystem == "com.apple.syspolicy.exec"'
log show ./case/system.logarchive --info --predicate 'process == "syspolicyd"'
- mac_apt
XPROTECTplugin (XProtect diagnostic files and Behavior Service DB) andQUARANTINEplugin (.LastGKReject). - sqlite3 for XPdb and ExecPolicy, on copies.
- Assess a sample on an analysis Mac:
spctl --assess --type execute -vv <app>,codesign -dv --verbose=4 <app>.
Unified Log Parser opens a .logarchive, the diagnostics and uuidtext folders or a log show export in the browser, applies DFIR triage rules and exports CSV or Timesketch; nothing is uploaded.
Quarantine Parser reads .LastGKReject together with the quarantine database, so a rejection can be tied back to its download.
Investigator tips
- Build the chain: quarantine event, then Gatekeeper assessment, then
provenance_trackingrow, then launchd persistence. - From macOS 15 the Control-click bypass is gone; overrides go through System Settings, so a user override is a deliberate act worth documenting.
- XPdb
responsible_pathoften names the parent app (Terminal, a browser, an installer), which helps attribute activity. /var/protected/xprotect/XPdbon 26.2+ may be old or stale: check thedbsubfolder.- Record XProtect and Remediator versions at collection time: a detection that appears after an update tells you when coverage arrived, not when infection happened.
- Paths under
AppTranslocationin logs mean a quarantined app ran from a randomized read-only mount. - Correlate installer packages with install history and privacy grants with TCC.db.