As a feature update, when we released Sandvox 2.7 back in November, it had to be sandboxed. Recently, version 2.7.3 became available on the app store, with a tweak to one sandboxing aspect. So it's about time I discussed quite what was involved in getting Sandvox sandboxed. (and yes, that little tongue-twister is a bit annoying!)
At first glance, Sandvox seems a pretty good fit for the sandbox. It's document-based and doesn't have too deep a hook into the OS's configuration. But as ever in programming, we did run into a few of those pesky edge cases. Let's approach this by taking a tour through Sandvox's entitlements, roughly by usage:
Nice and straight-forward; as a document-based app, write access is needed to any documents the user chooses to open. Read access is also used for media files dragged, pasted or otherwise inserted into a site.
We use the iMedia framework to provide an in-app media browser panel. A fair number of tweaks were required to keep it working in the sandbox:
- Read-only entitlement to the Pictures, Music and Movies folders
- com.apple.security.files.bookmarks.app-scope to store a bookmark to any custom folders
- com.apple.security.temporary-exception.shared-preference.read-only to locate media libraries using the preference files of:
- Temporary entitlement for read-only access to /Library/Audio/Apple Loops/Apple
- Temporary entitlement for read-only access to the bookmark files of:
For anyone using iMedia in their sandboxed app, search the headers for "SANDBOXING:" to see many useful comments.
When media is brought into a document, Sandvox offers the option to link to the file rather than copy it. This is mostly useful for large pieces of media to save disk space, or for files that need to be updated on an ongoing basis. Fortunately 10.7.3 resolved a major missing piece of the sandbox APIs by adding security-scoped bookmarks. For each media file, Sandvox attempts to save a document-scoped bookmark to it.
When the API first became available, it looked like there might be only a week or two left until sandboxing became mandatory. So we quickly shipped an update to Sandvox that began quietly generating security-scoped bookmarks from existing alias data where possible. This was possible while the app was still outside of the sandbox, and minimises the pain of migrating into it.
There is a nasty bug with security-scoped bookmarks though. Generating one requires write access to the target file (rdar://problem/11929296). For files dragged from the media browser, for example, Sandvox only has read access, and so creating the bookmark fails. Sandvox only needs to generate such a bookmark when a file is explicitly marked not to be copied into the document. So our solution is to wait till that happens and pop up an open panel for full, write access to the file. We play slightly dumb and claim that it's to "locate" the file, as trying to explain the real reason would be rather complicated. Besides, this same workflow is handy for when the original file really can't be located.
Note that the name of this entitlement changed in 10.7.4; we've stuck with the old one for maximum compatibility.
Sandvox directly uses the network when publishing a site, and or a handful of web services (e.g. Amazon). Arguably these functions could be moved out to an XPC process, but there's also a built-in WebView for previewing of pages, which may load content from the internet itself. Since this leaves us needing the network entitlement, there doesn't seem to be any security gain by moving only some of that functionality out to another process.
Active FTP would require an entitlement to act as a server on the network (the FTP server connects to the client for data transfers). We have been slowly deprecating active FTP support anyway; it's simply not supported in Sandvox 2.7+.
On the subject of publishing, when connecting to an SFTP server, it's advisable to check that the host's fingerprint matches that which you're recorded from before. Traditionally this has been done by keeping a list in ~/.ssh/known_hosts
Of course, that file falls outside of the sandbox. Sandvox moves to maintaining its own known_hosts file, inside of the sandbox. But what of the existing record? It seems a waste — and potentially a security flaw — to disregard any existing known hosts. Fortunately Apple agreed with us, allowing a temporary read-only entitlement to that file. Thus, our publishing system can look in both files for a matching key, and write new/updated information only to its own file. Sadly, Apple didn't feel that a write entitlement would be appropriate.
Public Key Authentication
When publishing to an SFTP server, Sandvox supports public key authentication, in addition to password-based. Previously, this had been accomplished by communicating with SSH-Agent (the agent itself sometimes needed some prodding to recognise passphrase-encrypted keys).
Use of SSH-Agent by apps is forbidden from the sandbox — and indeed, discouraged elsewhere. I see the reasons behind this and have to agree. Private keys are sensitive property of the user; they deserve control and knowledge over apps using them.
Our solution is to have customers select precisely which private key file they to use, via an NSOpenPanel. Access is then persisted as part of the document, by using an application-scoped bookmark. I reasoned that should another app be working with a Sandvox document, it would be preferable that it doesn't automatically gain access to the key. For passphrase-protected keys, Sandvox consults the keychain and then prompts for the passphrase, as needed.
Finally, when publishing locally, write access is needed to the destination folder. Previously, Sandvox offered a choice between ~/Sites, or the Mac's web server documents folder. Of course, from within the sandbox, there is no access to either of these by default (the latter is considered a system folder, and so forbidden, and may well be outside of the user's permissions too!).
To cope, we redesigned the system, so that the customer chooses the folder to publish to using an open panel. Of course, we then persist that as an application-scoped bookmark. Experience-wise, it's slightly more work up-front, but with far greater flexibility, which can benefit quite a few of our customers.
Like many apps, one nicety Sandvox offers is to automatically fill in your email address or other details into certain fields for convenience (e.g. when setting up a contact form on a site, or sending Karelia feedback). This is done simply by checking to see if the Address Book's Me card holds any suitable information.
If granted the above entitlement, sandboxing poses no problem. Instead, it is Mountain Lion which prompts the first time any app tries to access the address book. We do a few things to make that more pleasant for people; maybe there'll be a blog post on the subject in future.
Another convenience offered by Sandvox is to automatically guess the URL to be used (e.g. when creating a link) by having a look at open web browsers. Since we still support OS X v10.7, this is achieved with com.apple.security.temporary-exception.apple-events and a list of all supported browsers.