Entitlement Question for a macOS Text Processing Service

Folks;

I have a sandboxed macOS app that provides text handling via a service. This app has been for sale in the Mac App Store for several years. The basic mechanics of the service work just fine…

To set the scene:

  • user opens a file in some fashion
  • user makes a text selection within this open file
  • user invokes my app’s service

In the course of development of this service, I now have established an NSURL (file) for the user’s document. However, when I later attempt to open this url I get a sandbox error: client lacks entitlements? for path: …. NOTE: It does not matter where the file is located! My app already has this entitlement: com.apple.security.files.user-selected.read-write

My question: Is there an entitlement that will permit me to programmatically open this fileURL?

Replies

In the course of development of this service, I now have established an NSURL (file) for the user’s document.

Can you elaborate on this? Exactly how did you establish this URL? Was it passed to your code by the system? If so, using which API? Or did you construct it internally to your app?

This matters because, when the system passes you a file URL, it’s supposed to dynamically extend your app’s sandbox so that it can access that URL. This is what happens, for example, if you open a file using the standard open panel, or drag a file in using drag’n’drop. If that’s not happening, I’d like to better understand those circumstances.

OTOH, if you’ve just built this URL yourself, the App Sandbox is correct in blocking that access. In that case I’d like to better understand how you built the URL so I can suggest a way forward.

For more info on how dynamic sandbox extensions work, see On File System Permissions and the resources it links to.

Share and Enjoy

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

Quinn;

Thanks for this response. Also thank you for your long standing service to the developer community!! I wish there were a dozen of you.

The URL is established by simply asking the active app for its current document. First recall that I have the app level entitlement: com.apple.security.files.user-selected.read-write On launch my app becomes an observer of 'externalApplicationDidActivate' et al - in this fashion my app is aware of the active app.
When the service is invoked my app may fire a compiled AppleScript. These AppleScripts targets (~25) are each approved with an entitlement (com.apple.security.temporary-exception.apple-events) as well as each of these targets being approved by the user using the 'AEDeterminePermissionToAutomateTarget' mechanism These scripts are about as simple as can be; an example is shown below.

I suppose that this fits the 'constructed internally' paradigm you reference above but I submit that it complies fully with the 'System' - I don't see how I can make it more fully comply with the 'System'. If you are aware of a better mechanism - I am all ears!

So in essence the user has expressly asked my app to process a text string from a given app that my app has ensured that the system understands that my app will interact with.. Why under these circumstances are the results of a valid AppleScript from an approved app not considered as supplied by the 'System'?

Any thoughts you might have will be fully considered!

Thanks Again for your time and your patience over many years! Steve

tell application "TextEdit"

	try
		set documentName to name of document 1
		set documentPath to (path of document 1 as string)
		if ((documentPath is missing value) or (length of documentPath is 0)) then
			set documentPath to "unsaved document"
		end if
		set theResult to {|url|:documentPath, title:documentName}

	on error errMsg number errNum
		set theResult to {title:("Error" & errNum as text), |url|:errMsg}
	end try

end tell

return (theResult as record)

The problem here is that your script to get the current document path returns a string. Consider this snippet:

tell application "TextEdit"
    set p to path of document 1
end tell
{p, class of p}
-- Result: {"/Users/quinn/Desktop/tmp.rtf", text}

The Apple Event subsystem doesn’t know that this string represents a path and thus doesn’t extend your sandbox to grant you access to it. For that to work the value coming back must be a file-like thing: a file, file URL, alias, and so on.

Looking at the TextEdit scripting dictionary, there doesn’t seem to be any way to get back a file-like thing for an open document. It has the path property and that’s it.

Not all apps works this way. For example, in Numbers the document class has a file property that returns a file object. Getting that may well extend your sandbox appropriately.

Share and Enjoy

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

Quinn;

Thanks for your reply. Yeah Numbers works somewhat better but there are issues…. First I don’t have any need for the actual file to be returned via AppleScript.   I cannot predict how large a given file might be… So using Numbers file property of the document object, if I return only the POSIX path of the file object - I do get different behavior than with the returned path string from TextEdit.. My app takes the AppleScript result and constructs a custom attributedString that embeds the fileURL… When the user clicks on this attributedString (from Numbers) in the app’s main textView, I don’t get the error:

[sandbox] Sandbox extension creation failed: client lacks entitlements? for path:

Instead a Finder view opens to the enclosing folder without any complaining in the console - better yes, but not what I expect! I expected the file to actually open…

But this ‘enclosing folder’ behavior is something I observe with a number of different ‘click on link’ actions - it seems that the ’System’ is forcing the user to perform the specific action to open a file rather that performing that open when the user only implies ‘open that file’…

The user can build a collection of these text selection actions - the main attributes are stored in a custom CoreData repo. The use of CoreData is another reason why returning the file is not the correct result - I don’t want to store randomly large files in CoreData when all I want is a fileURL….

When I quit and relaunch the app, clicking on the attributedString results in the same Finder behavior but now the error mention above is shown in the Console. So the sandbox permission is very short lived - it only lasts for the current session…. From my perspective this is a limitation of the sandbox - If the user correctly asked me to access a file - why does that permission not persist if none of the security constraints have changed? Sure I do understand that there are issues -  but I am trying to articulate the limitations I experience in app development…

So the whole POSIX thing above let to experimentation with the AppleScript in TextEdit. On the whole I don’t find a better solution than simply returning the path string and using it in the attributedString mentioned above. In this case, the click action will throw this error to the console but will also open the enclosing folder as above. The user’s experience is identical to what happens with the file object from the Numbers script….

Instead a Finder view opens to the enclosing folder without any complaining in the console

Right. Revealing an item in the Finder is something that’s specifically allowed by App Sandbox. You can access this yourself using -[NSWorkspace activateFileViewerSelectingURLs:].

From my perspective this is a limitation of the sandbox - If the user correctly asked me to access a file - why does that permission not persist if none of the security constraints have changed?

App Sandbox supports persistent access via security-scoped bookmarks. See Security-Scoped Bookmarks and Persistent Resource Access in the App Sandbox Design Guide.

Share and Enjoy

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