SkillAgentSearch skills...

MacOSSandboxBuild

Profiles to build software in a sandboxed environment on macOS

Install / Use

/learn @BrianSwift/MacOSSandboxBuild
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

MacOS Sandbox Build

What is this?

MacOS sandbox profiles and instructions for command line (Terminal) building of software in a sandboxed environment.

Audience

People who build software from the command line on macOS and have security related concerns about what could be occurring in a complex build process.

How do I use it?

After some setup, software build commands are executed in a restricted environment defined by the confined.sb profile using macOS command sandbox-exec. Three parameters passed to confined.sb specify directories accessible to the build command that are additions to a limited set of standard system directories.

  • _RX1 : contents are readable and executable, and metadata can be read from its path-ancestors
  • _RW1: contents are readable and writable
  • _TMPDIR: contents are readable and writable

The following example steps through building cmake.

Download Profiles

mkdir -p "$HOME/Development/github"
cd "$HOME/Development/github"
git clone https://github.com/BrianSwift/macOSSandboxBuild.git

Setup TMPDIR

export TMPDIR="$HOME/Dev Space/sandtmp"
mkdir -p "$TMPDIR"

Load xcrun_db cache

Identify tool names in /usr/bin that might bounce to an XCode tool (this is intersection of tools in Xcode.app and tools in /usr/bin.) This annoying setup process only needs to be done once.

(ls /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin ; ls /Applications/Xcode.app/Contents/Developer/usr/bin ) | sort -u >/tmp/tool_list_xcode.txt
ls /usr/bin | sort >/tmp/tool_list_usr_bin.txt
comm -12 /tmp/tool_list_usr_bin.txt /tmp/tool_list_xcode.txt >/tmp/tool_list_to_cache.txt

Execute xcrun -find for each tool name to load xcrun_db. Execution in sandbox using nomach.sb profile causes xcrun to fallback to using $TMPDIR for location of xcrun_db, rather than _CS_DARWIN_USER_TEMP_DIR returned by confstr(3). nomach.sb only denies mach-lookup operations. It is not more restrictive because only Apple provided code is executed.

First pass takes a few seconds per tool, and may get error messages when looking up DeRez and swift. Second command produces lots of errors, but that is expected because some apps don't recognize --version. Final command should only take a fraction of second per tool, and produce no errors.

</tmp/tool_list_to_cache.txt xargs -n 1 -I % /usr/bin/time sandbox-exec -f $HOME/Development/github/macOSSandboxBuild/nomach.sb xcrun -find %
: Some tools like "make" are not cached correctly with "xcrun -find" so also make a pass attempting to run each with "--version". For some tools this is produces an error, but that is OK since we are only trying to get the cache initialized. "true" at end keeps xargs from terminating early when tools exit with error.
</tmp/tool_list_to_cache.txt xargs -n 1 -I % /usr/bin/time sandbox-exec -f $HOME/Development/github/macOSSandboxBuild/nomach.sb sh -c "% --version ; true"
: This should not produce errors and each command should only take fraction of a second
</tmp/tool_list_to_cache.txt xargs -n 1 -I % /usr/bin/time sandbox-exec -f $HOME/Development/github/macOSSandboxBuild/nomach.sb xcrun -find %

Verify xcrun_db has been created in $TMPDIR.

ls -l "$TMPDIR/xcrun_db"
: total 64
: -rw-------  1 sand  notstaff  31950 Sep  9 21:46 xcrun_db

xcrun_db is pre-loaded because xcodebuild (which is invoked by shim apps in /usr/bin does not like trying to determine the developer tools configuration while running in a confined sandbox. After producing xcrun_db, xcodebuild will be able to lookup paths to real tools whenever a shim is invoked rather than performing a search.

This setup probably needs to be repeated anytime the default developer tools are changed with xcode-select, but that hasn't been tested.

This may be avoidable if environment variables are set to specify the SDK and path to XCode tools, but this has not been tested.

Build Something (cmake) in Confined Sandbox (with Activity Logging)

Setup Build and Install tree

mkdir -p "$HOME/Dev Space/Net/cmake/"{Src,Build,Dist,Inst}

Download cmake source distribution (using "download.sb" sandbox)

cd "$HOME/Dev Space/Net/cmake/Dist"
sandbox-exec -D_RW1="$PWD" -f "$HOME/Development/github/macOSSandboxBuild/download.sb" curl -OL https://github.com/Kitware/CMake/releases/download/v3.21.2/cmake-3.21.2.tar.gz

Unpack source within confined sandbox

cd "$HOME/Dev Space/Net/cmake/Src"
sandbox-exec -D_RX1="$HOME/Dev Space/Net/cmake/Dist" -D_RW1="$PWD" -D_TMPDIR="$TMPDIR" -f "$HOME/Development/github/macOSSandboxBuild/confined.sb" tar xf "$HOME/Dev Space/Net/cmake/Dist/cmake-3.21.2.tar.gz"

Prepare build directory by copying /usr/bin/false to ps to keep cmake build from spewing "Failure calling sysctl: Operation not permitted" messages. ps is a suid program, and Apple's sandbox environment (understandably) denies execution of suid programs.

cd "$HOME/Dev Space/Net/cmake/Build"
cp /usr/bin/false ps

Start logging (optional)

As a user with administrator privileges, in a separate window start recording sandbox log messages for review after build ends.

log stream --style compact --info --debug  --predicate '(((processID == 0) AND (senderImagePath CONTAINS "/Sandbox")) OR (subsystem == "com.apple.sandbox.reporting"))' >/tmp/sblog-cmake-01.txt

bootstrap cmake within confined sandbox

cd "$HOME/Dev Space/Net/cmake/Build"
PATH="`pwd`:$PATH" /usr/bin/time sandbox-exec -D_RX1="$HOME/Dev Space/Net" -D_RW1="$PWD" -D_TMPDIR="$TMPDIR" -f "$HOME/Development/github/macOSSandboxBuild/confined.sb" "$HOME/Dev Space/Net/cmake/Src/cmake-3.21.2/bootstrap"  --prefix="$HOME/Dev Space/Net/cmake/Inst"
: Expected final output
: CMake has bootstrapped.  Now run make.
:      463.29 real       376.10 user        79.49 sys

build and install cmake within confined sandbox

PATH="`pwd`:$PATH" /usr/bin/time sandbox-exec -D_RX1="$HOME/Dev Space/Net" -D_RW1="`dirname $PWD`" -D_TMPDIR="$TMPDIR" -f "$HOME/Development/github/macOSSandboxBuild/confined.sb" make -j 4 install
: Expected final output
: -- Installing: /Users/sand/Dev Space/Net/cmake/Inst/share/bash-completion/completions/ctest
:      501.18 real      1820.55 user       124.39 sys

Stop logging

Stop (^C) the log stream... running in another window.

A tally of sandbox allow/deny log messages can be produced with this command.

tr '0123456789' '##########' </tmp/sblog-cmake-01.txt | sed 's/[^ ]*  *[^ ]*  *[^ ]* *[^ ]*  *[^ ]*//'  | grep -v duplicate | sort | uniq -c | sort -rn | grep -e allow -e deny | less 
443  Sandbox: cmake(#####) deny(#) process-exec* /usr/sbin/sysctl
443  Sandbox: cmake(#####) deny(#) file-read-metadata /usr/sbin/sysctl
 43  Sandbox: cmake(#####) deny(#) file-read-metadata /usr/X##R#
 25  Sandbox: xcodebuild(#####) deny(#) mach-lookup com.apple.distributed_notifications@Uv#
 25  Sandbox: xcodebuild(#####) deny(#) mach-lookup com.apple.FSEvents
 25  Sandbox: xcodebuild(#####) deny(#) file-read-data /
 20  Sandbox: cmake(#####) deny(#) file-read-metadata /usr/sbin
 19  Sandbox: cmake(#####) deny(#) file-read-metadata /opt
 19  Sandbox: cmake(#####) deny(#) file-read-metadata /Library/Frameworks
 13  Sandbox: routined(###) deny(#) mach-lookup com.apple.Maps.MapsSync.store
 10  Sandbox: cmake(#####) deny(#) file-read-metadata /usr/standalone
 10  Sandbox: cmake(#####) deny(#) file-read-metadata /usr/sbin/sendmail
 10  Sandbox: cmake(#####) deny(#) file-read-metadata /usr/libexec
 10  Sandbox: cmake(#####) deny(#) file-read-metadata /usr/X##
 10  Sandbox: cmake(#####) deny(#) file-read-metadata /sbin
 10  Sandbox: cmake(#####) deny(#) file-read-metadata /Library/Apple/usr/bin
 10  Sandbox: cmake(#####) deny(#) file-read-metadata /Library/Apple/System/Library/CoreServices/SafariSupport.bundle/Contents/MacOS/safaridriver
 10  Sandbox: cmake(#####) deny(#) file-read-data /usr/sbin
 10  Sandbox: cmake(#####) deny(#) file-read-data /sbin
 10  Sandbox: cmake(#####) deny(#) file-read-data /opt
...

Note: the log messages come from all sandbox activity on the system, not just the build process, so you may see messages unrelated to the build.

Note: not all denied operations are being logged. Some denied operations that occur frequently (with every process launch) are configured to not log messages to keep down pollution/noise.

Note: Raw (un-tallied) log also contains many messages related to Symbolicator for cmake[90573] is NULL , and stack traces. I haven't figured out how to control these yet.

Build More Stuff

See exampleBuilds.md for walkthroughs of building gflags, glog,eigen, ceres, llvm clang

Tell me more

What is the macOS Sandbox?

"App Sandbox is an access control technology provided in macOS, enforced at the kernel level." (From https://developer.apple.com/library/archive/documentation/Security/Conceptual/AppSandboxDesignGuide/AboutAppSandbox/AboutAppSandbox.html)

The macOS sandbox enables creation of an environment that restricts access to a configurable subset of system resources and capabilities. These include what files can be read/write/stat/exec, network access, mach messaging and other interprocess communication, and system information via sysctl.

The specification of what is and is not accessible from the sandboxed environment is provide by a sandbox profile.

For our purposes, a sandboxed environment is created by running sandbox-exec with the -f parameters supplying the path to the profile configuring the restricted environment and also the command to be executed within the restricted environment.

Sandbox (also referred to as Seatbelt) was introduced in 2007 in OS X 10.5 Leopard. However, the profile definition language remains undocumented by Apple and is considered an "Apple System Private Interface". Also, while the sandbox-exec man page has indicated it is DEPRECATED since at least 2017, it remain

Related Skills

View on GitHub
GitHub Stars40
CategoryDevelopment
Updated13d ago
Forks2

Security Score

90/100

Audited on Mar 26, 2026

No findings