I sometimes test randomly downloaded code on macOS in a Sandbox that has limited and network access. I posted about this way back in Jan 2017, Creating a macOS Sandbox to run Kodi, and this is a short refresher for me...

The latest macOS man sandbox-exec page states that this command is DEPRECATED! However, I am not aware of any command line alternative... though someone replied on this topic at Stack Overflow, indicating that the sandbox is not going away (yet).

Sandbox Log

I use this command to view log output from the sandbox:

log stream --style compact --predicate 'sender=="Sandbox"'

And in this case, since I want to filter only for messages to do with python:

log stream --style compact --predicate 'sender=="Sandbox" and eventMessage contains "python"'

The Console.app may also help - hit the Start button and then search for sandbox:

Console.app filtered by "sandbox" logs

Sandbox Exec that Denies by Default

The command sandbox-exec runs a process in a sandbox with a “profile” applied. The -p option allows the profile to be specified in-line rather than being read from a file.

Understanding the profile format is a challenge, and you can refer to my prior post for a bit more explanation. Also, check out the in-built sandbox profiles at /System/Library/Sandbox/Profiles for more examples.

The profile below will deny everything by default. Often the process I am testing will totally fail to run, so I have to monitor the log output above and bit by bit, add additional allows. This is a long and tedious process!

In the case of Python:

  • Deny all networking... but to allow outgoing networking, I just add (allow network-outbound), or to allow local networking, I add (allow network* (local ip "localhost:*"))(allow network* (remote ip "localhost:*"))
  • Allow mandatory IO access for console and files
  • Allow reads for core system directory (root, /System, /Library, /private, /dev and /usr) and certain user directories (~/Library)
  • Allow read and write to the current working directory $PWD.
  • Allow specific processes to be run - in this case python3 is a symbolic link to executables in the Xcode Command Line Tools directory.
sandbox-exec -p '(version 1)(deny default)
(allow iokit-open)(allow mach* sysctl-read)
(allow file-ioctl)(allow file-read-metadata)
(allow file-read-data (literal "/") 
 (regex "^/System" "^/Library" "^/private" "^/dev" "^/usr" "~/Library"))
(allow file-read* file-write* (regex "^'$PWD'"))
(allow process-exec
 (regex "^/usr/bin/python3" "^/Library/Developer/CommandLineTools/"))' \
python3

Quick tests: try opening a port python3 -m http.server 8888 or connecting to Google: import urllib.request; print(urllib.request.urlopen("http://google.com").read())

Sandbox Exec that Allows by Default

Here is a profile that does the opposite - it allows everything except networking and certain user folders:

sandbox-exec -p '(version 1)(allow default)(deny network*)(deny file-read-data
 (regex "^/Users/'$USER'/(Documents|Desktop|Developer|Movies|Music|Pictures)"))' \
zsh

Quick tests: try curl google.com or ls ~/Documents.

You can replace zsh with other command line programs or run executables within Apps (e.g. /Applications/myApp.app/Contents/MacOS/myApp). Don’t use open /Applications/my.app. Also, apps that manipulate the sandbox or spawn sandboxed children will fail, e.g. with errors like deny forbidden-sandbox-reinit due to missing entitlements for com.apple.security.app-sandbox, as described here.

To quickly test something from the command like without networking and folder access, I use this alias so I can just sandbox zsh:

alias sandbox='sandbox-exec -p '\''(version 1)(allow default)(deny network*)(deny file-read-data (regex "^/Users/'$USER'/(Documents|Desktop|Developer|Movies|Music|Pictures)"))'\'''

That’s all for now!