Skip to content

Debugging Sandbox Denials

Use /usr/bin/log (not shell-shadowed log) for denial analysis.

Live Stream Denials

bash
/usr/bin/log stream --style compact --predicate 'eventMessage CONTAINS "Sandbox:" AND eventMessage CONTAINS "deny("'

Filter by Specific Agent/PID Pattern

bash
/usr/bin/log stream --style compact --predicate 'eventMessage CONTAINS "Sandbox: 2.1.34(" AND eventMessage CONTAINS "deny("'

Kernel-Level Denials

bash
/usr/bin/log stream --style compact --info --debug --predicate '(processID == 0) AND (senderImagePath CONTAINS "/Sandbox")'

Recent History

bash
/usr/bin/log show --last 2m --style compact --predicate 'process == "sandboxd"'

Filter Common Noise

bash
/usr/bin/log stream --style compact \
  --predicate 'eventMessage CONTAINS "Sandbox:" AND eventMessage CONTAINS "deny(" AND NOT eventMessage CONTAINS "duplicate report" AND NOT eventMessage CONTAINS "/dev/dtracehelper" AND NOT eventMessage CONTAINS "apple.shm.notification_center" AND NOT eventMessage CONTAINS "com.apple.diagnosticd" AND NOT eventMessage CONTAINS "com.apple.analyticsd"'

Suppress dtracehelper source noise:

bash
DYLD_USE_DTRACE=0 sandbox-exec ...

Correlate denials with filesystem activity:

bash
sudo fs_usage -w -f filesystem <pid> | grep -iE "open|create|write|rename"

Converting Deny Logs to Allow Rules

Deny line format:

deny(<pid>) <operation> <path-or-name>

Deny typeAllow rule pattern
file ops(allow <operation> (literal "<path>"))
sysctl(allow sysctl-read (sysctl-name "<name>"))
mach(allow mach-lookup (global-name "<name>"))
network(allow network-<op> (local ip "localhost:*"))

Building a Profile from Scratch

  1. Start with (version 1) and (deny default).
  2. Run with log stream active in another terminal.
  3. Map each deny(...) event to the minimum allow rule.
  4. Repeat until required startup/workflows pass.

Exercise full toolchain workflows (git, npm, cargo, etc.) since child processes inherit sandbox policy.

Open source under the Apache 2.0 License.