Can't run NSTask inside sandboxed app

I'm creating a small Swift editor mac app. I want this app to be able to run entered code and then show output. For this reason, I used NSTask and it looks like this:

let task = NSTask()

task.standardOutput = outPipe

task.standardInput = inPipe

task.standardError = errPipe

task.executableURL = URL(fileURLWithPath: "/usr/bin/swift")

task.arguments = [pathToFile] //To run the code I create a temporary Swift file

do {
     try task.run()
} catch {
    //Error handling
}

//Rest of the code for reading output

And this code works... without sandbox. When I turn App Sandbox on, there is an error:

xcrun: error: cannot be used within an App Sandbox

I made a research and found many people that had the same problem, however often the solution was just not turning App Sandbox, but I need it on because I want to release the app to Mac App Store. I know that this could be achieved using XPC helper, but I'd rather choose at very last.

Could someone tell me how this could be achieved?

Replies

/usr/bin/swift is a trampoline that bounces to the user’s preferred swift by invoke xcrun. xcrun gets grumpy when you try to use it from within a sandbox. You can work around this immediate problem by invoking swift directly rather than through this trampoline.

However, that just gets you past one of a number of potential pitfalls here. The swift command-line tool was not designed to run in a sandbox and you’ll likely encounter all sorts of other problems. You wrote:

I'm creating a small Swift editor mac app.

Can you explain more about your overall goal here? Specifically, is it an absolute requirement that the you invoke the user’s preferred swift tool? Or could you just bundle the Swift compiler within your app and always use that built-in one? This is, for example, what Swift Playgrounds does.

Also, when you invoke Swift are you just asking it to run code? Or to generate an executable that you can then run? The latter presents additional complexities from a sandboxed environment.

Share and Enjoy

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

  • My goal is to create an editor which will be able to run code in many languages (ones, that can be run using single CLI command), but currently, implementing Swift is my main goal. This is why I would rather not bundle compilers because the size of the app could be too big. However, depending on how complicated the bundling of the compiler is, I could just make the user choose languages and download them.

    I just want Swift to run the code and show the output, without compiling it.

Add a Comment

My goal is to create an editor which will be able to run code in many languages

This isn’t really a feasible goal in a sandboxed app. To explain why I’m going to reference a bunch of terminology from my On File System Permissions post. I recommend that you read this before going on.

Also, the following discussion is about simply executing the tool in question. Whether the tool will work in a sandboxed environment is an entirely different kettle of fish. I can’t speak for non-Apple languages, but I’m certain that the swift tool was not written with this is mind.


The sticking point here relates to sandbox extensions. Imagine that you want to run Brainf*** code [1]. Needless to say, that’s not installed by default, so the user has installed in themselves in a directory of their choosing. Your sandboxed app cannot access that directory; in order to do anything in that directory the user must first grant you access using, say, the open panel. However, the sandbox extension issued by the open panel is either a read extension (com.apple.security.files.user-selected.read-only) or a read/write extension (com.apple.security.files.user-selected.read-write). Neither of these will let you execute code from that directory. For that you’d need an execute extension, and the OS simply has no support for this.

There are two ways around this, but neither is particularly great:

  • The first is to require the user to place the language in a directory that’s on the sandbox’s built-in allowlist. This list isn’t well documented and it’s quite restricted. It does not, for example, include the locations used by the most common package management systems on macOS.

  • The second is to statically extend your sandbox using file access temporary exceptions but those tend to cause App Review entanglements (I don’t work for App Review and thus can’t give you definitive answer about their policies).

You’re not the first developer to hit this problem and we have a bug on file requesting a better solution for yet (r. 77725959), but I can’t promise that it’ll be fixed any time soon )-:

Now, you should be able to get this working for Swift because:

  • The swift tool typically ships as part of Xcode.

  • Xcode is typically installed within /Applications.

  • /Applications is on the App Sandbox allow list.

However, if any of these assumptions fails then you won’t be able to run the swift tool. For example, I install my (many) copies of Xcode in ~/XcodeZone, and you can’t get access to those.

Share and Enjoy

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

[1] Hey, for once the stars are warranted!

Hi. I can assume that this would require that the user has installed Xcode. I'd rather don't require that. Looks like bundling compiler within the app is the best choice. I will just make the user able to select which launguages they want. I tried looking for binary at swift.org, but I couldn't find a toolchain for macOS. How can I do that?

I can assume that this would require that the user has installed Xcode.

Yes.

I tried looking for binary at swift.org, but I couldn't find a toolchain for macOS.

There are definitely toolchains for macOS there. The Download Swift page has a link to swift-5.4.1-RELEASE-osx.pkg, which is the toolchain equivalent of the version in Xcode 12.5.

Having said that, this is not going to be easy. The above-mentioned Swift toolchain is intended to be run by Xcode, which isn’t sandboxed. It probably won’t cope well with being run in a sandboxed environment. And it’s very likely that you’ll need to build the code from source to make it work in the sandbox, and that’s not something I can help you with.

Share and Enjoy

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

Ok. I will try building Swift from source code.

Thank you for help.