Skip to content

How to Analyze TCC.db in Your Browser

Step by step: load macOS TCC.db files and their WAL into a free in-browser parser, read the findings, review permissions and history, and export.

Published on 7 min read

TL;DR. Collect the com.apple.TCC folders (system and every user, with the WAL), drop them on TCC Parser, and work through the tabs: Findings, Permissions, Apps, Timeline, History, Sources. Set a time range to focus every view on the window you care about, then export CSV, Timesketch CSV or JSON. Parsing runs in WebAssembly in a Web Worker inside your browser: the files are never uploaded.

This is the hands-on companion to the TCC.db forensics guide. The examples use the built-in sample, which is synthetic and fictional: a finance laptop, FIN-MBP-03, user dana.whitlock, where an attacker using a stolen session worked between about 10:05 and 10:50 UTC on 2026-09-14. It belongs to the same fictional intrusion as the Windows host FIN-WKS-07 used on our sister tools.

Apple and macOS are trademarks of Apple Inc. TCC Parser is independent and not affiliated with Apple.

Before you start

You needWhy
The system com.apple.TCC folderFull Disk Access, Accessibility, Screen Recording and MDM grants
Each user's com.apple.TCC folderAutomation, folders, camera, microphone
The -wal filesRecent changes and earlier versions of rows
The user folder in the pathAttributes each database to an account
A current desktop browserThe parser runs as WebAssembly

Step 1: Collect the com.apple.TCC folders

On a live Mac, grant Full Disk Access to Terminal (or your agent) first, then copy the folders:

sudo ditto "/Library/Application Support/com.apple.TCC" ./case/tcc_system
for u in /Users/*; do
  [ -d "$u/Library/Application Support/com.apple.TCC" ] && \
  sudo ditto "$u/Library/Application Support/com.apple.TCC" "./case/tcc_$(basename "$u")"
done

The same commands, plus UAC, Aftermath, Velociraptor and dead-box options, are in the How to get your data guide under the drop zone and in TCC.db location and acquisition. If your collector copied only TCC.db without TCC.db-wal, the parser still works, but the History tab will have less to show.

Step 2: Load the files

Open the home page. On the drop zone you can:

  • drop files, a folder, or a collection archive: a ZIP (Velociraptor, Aftermath tcc_root / tcc_<user> copies, or a zipped folder) or a UAC .tar.gz, which you do not need to extract first;
  • click Choose files or Choose a folder;
  • click Try a sample to load FIN-MBP-03.

The parser recognizes TCC.db files and pairs each with its -wal, reads MDMOverrides.plist, and loads REG.db and any other SQLite file in the folder as raw tables. The workspace opens full screen; press Esc to leave full screen.

Step 3: Read the Findings tab

Findings are prompts to look, not verdicts. In the sample you will see, among others:

FindingSample row
High-impact grant to a terminalFull Disk Access to com.apple.Terminal, 10:11:37 UTC
Path-based client in a hidden folder, ad-hoc code requirement/Users/dana.whitlock/Library/Caches/.sync/sync-helper
Grant removed (only visible in the WAL)Accessibility for that helper, granted 10:19:05, removed later
Denied request from an unusual clientScreen Recording for the helper, denied at 10:24:40
Automation of System Events and FinderTerminal, 10:14:02 and 10:16:48
Burst of permission changesChanges from 10:11:37 to 10:24:40, then a 14-minute gap before 10:38:55
MDM-granted row to reviewFull Disk Access for com.example.edr.agent from a PPPC profile

The MDM row is expected on a managed Mac; it is listed so you confirm it, not because it is suspicious. Other finding types include clients in /tmp, /private/var/tmp, /Users/Shared or Downloads, and timestamps in the future.

Step 4: Review the Permissions and Apps tabs

Permissions lists every row from every database, decoded, in these columns:

ColumnContent
Last modifiedlast_modified in UTC
DecisionAllowed, denied, unknown or limited
PermissionHuman name and raw identifier, for example Full Disk Access and kTCCServiceSystemPolicyAllFiles
App/programThe client, plus the Automation target when there is one
Reasonauth_reason, decoded
DatabaseSystem or the user's database, so a per-user row stays attributed to its account
Review flagsThe findings that apply to the row

Click a row to open its detail panel: client_type, the raw auth_value, the csreq decoded to requirement text, auth_version, flags, the policy, the storage (a TCC.db page or a WAL copy), and buttons to set the time range around this decision.

Apps pivots by client. Open com.apple.Terminal in the sample and you see its whole footprint in one place: Full Disk Access in the system database; Automation of System Events and Finder, Documents and Desktop access, and removable-volume access (10:38:55, the USB volume "EXFIL" in the story) in dana.whitlock's database. That picture is the one you want in the report: one app, many capabilities, all within 30 minutes.

Step 5: Scope a time range on the Timeline

The Timeline tab orders every timestamp. To focus it, and everything else, use the time range bar that sits above all the tabs:

  • From and To, to the second;
  • a time-field select: Last modified, Last reminded, Expired at or Any of these times;
  • presets computed from the data;
  • an Around… select;
  • a density strip that shows where changes cluster, which you can drag to select a window.

From a row's detail panel you can also set the range around that decision. The range scopes every view, count, finding and export, and it is kept in the URL, so you can bookmark or share the exact view with a colleague who loads the same files. In the sample, a range of 10:05:00 to 10:50:00 UTC isolates the intrusion from the older, legitimate grants (Zoom, Teams camera and microphone, and so on); with the sample loaded, the Use the sample's incident window button on the Findings tab sets it for you.

Step 6: Check History and Sources

History shows rows recovered from WAL frames (older commits, uncommitted frames and frames from an older WAL generation) and from freed pages, compared with the current state. In the sample, it shows the Accessibility grant for the sync-helper that no longer exists in the current database. How and why that works is in recovering removed TCC grants from the WAL.

Sources lists what was loaded: each database, whether a WAL was found and replayed, MDMOverrides.plist, and the raw tables of other SQLite files. Check it before you trust an absence: if a user's database is missing, the answer is a collection gap, not a clean account.

Step 7: Export the evidence

Export menu entryUse
Permissions (CSV, incl. history)The decoded rows, including recovered history rows, with a state column (current, expired, removed or earlier)
Timeline (CSV)The timeline, for your case timeline
Timeline for Timesketch (CSV)Import into Timesketch next to other sources
Everything (JSON)Everything, including decoded requirements and recovered history

When a time range is set, it is part of the file names, so an export always says which window it covers. Record the hashes of the source files you loaded alongside the exports.

What to do next

The sample does not end in TCC.db: in the same story there is a quarantined download tools.zip from https://files.example/…, a fake LaunchAgent com.example.updater.plist, files staged in ~/Library/Caches/.sync/ and a visit to transfer.example. Pivoting from TCC rows to those artifacts is covered in investigating TCC permission abuse.

Related articles

What the macOS TCC database records, what it proves and what it does not, the services that matter, and a repeatable workflow for reviewing TCC.db in a case.
Where the system and per-user TCC.db files live, why Full Disk Access and SIP matter, and how to collect them with the WAL using UAC, Aftermath or Velociraptor.
How attackers abuse macOS TCC permissions, what each technique leaves in TCC.db, and how to pivot to unified logs, launchd, quarantine and shell history.