Running Developer Tools from a Sandboxed App

This thread has been locked by a moderator.

I’ve talked about this a bunch of times here on DevForums but, reviewing those posts today, I realised that they’re quite fragmented. This post is my attempt to create a single post that collects together all the bits.

If you have questions or comments, please put them in a new thread. Tag it with App Sandbox so that I see it.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"


Running Developer Tools from a Sandboxed App

If you attempt to run a developer tool, like otool, from a sandboxed app, it fails with an error like this:

xcrun: error: cannot be used within an App Sandbox.

In this case I was trying to run /usr/bin/otool directly, so how did xcrun come into it?

Well, the developer tools that come pre-installed on macOS, like otool, are actually trampolines that use xcrun to bounce to the the real tools within Xcode. Specifically, xcrun defaults to the tools within the currently selected Xcode or Command Line Tools package. So, if you have Xcode installed in the usual place and are using it for your currently selected tools, the actual sequence is /usr/bin/otool, which runs xcrun, which runs /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/otool.

The user can change the currently selected tools with xcode-select.

You can get around this problem by running otool from within Xcode. This skips the first two steps, allowing the tool to run. However, there are some serious problems here. The first is that there’s no guarantee that the user has Xcode installed, or that they want to use that specific Xcode. They might have the Command Line Tools package installed. Or they might prefer to store Xcode somewhere outside of the Applications directory.

You can get around this by running xcode-select with the --print-path argument:

% xcode-select --print-path
/Applications/Xcode.app/Contents/Developer

However, that results in two more problems:

  • xcode-select prints the root of the Developer directory. The location of, say, otool within that directory isn’t considered API.

  • As a sandboxed app, you might not have access to the path returned.

That second point deserves a deeper explanation. To understand this, you’ll need to understand the difference between your static and dynamic sandbox. I talk about this in On File System Permissions.

Running otool from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/otool works because /Applications is in the sandbox’s built-in allowlist. This is part of your static sandbox, so you can run executables from there.

But what happens if the user’s selected Xcode is in a different directory? (Personally, I keep numerous copies of Xcode in ~/XcodeZone.) That might not be part of your static sandbox so, by default, you won’t be able to run tools from it.

For normal files you can dynamically extend your sandbox to allow this, for example, by presenting a standard open panel. However, this doesn’t work for executable access. There is currently no way to get a dynamic sandbox extension that grants executable access. On File System Permissions has a link to a post that explains this in detail.

Finally, there’s a big picture concern: Does the tool actually work when run in a sandbox? Remember, when a sandboxed app runs a command-line tool like this, the tool inherits the app’s sandbox. For more about the mechanics of that, see the documentation linked to by On File System Permissions. For a simple tool, like otool, you can reasonably assume that the tool will work in a sandbox. Well, you have to make sure that any path arguments you pass in point to locations that the sandbox allows access to, but that’ll usually do the trick. OTOH, a complex tool, like say the Swift compiler, might do things that don’t work in the sandbox. Moreover, it’s possible that this behaviour might change over time. The tool might work in a sandbox today but, sometime in the future, an updated tool might not.

So what should you do? The only approach I’m prepared to actively recommend is to not sandbox your app. That avoids all of the issues discussed above.

If you must sandbox your app then I see two paths forward. The first is to just live with the limitations discussed above. Specifically:

  • You can only use a tool that’s within your static sandbox.

  • For complex tools, you run the risk of the tool not working in the future.

The alternative is to embed the tool within your app. This is only feasible if the tool is open source with a licence that’s compatible with your plans. That way you can build your own copy of the tool from the source. Of course this has its own drawbacks:

  • It increases the size of your app.

  • You can only run that version of the tool, which might not be the version that the user wants.

Up vote post of eskimo
244 views