LSOF Shell command not running in macOS Sandbox

When I run the following Shell code in a normal macOS Desktop app I get a list of my login’s running processes:

Var tempShell As New Shell
Var Result As String

tempShell.ExecuteMode = Shell.ExecuteModes.Asynchronous
tempShell.Execute("lsof -u `whoami` ")

Do
  tempShell.Poll
Loop Until Not tempShell.IsRunning

ListBox1.RemoveAllRows
If tempShell.Result <> "" Then
  Var tempArray() As String = tempShell.Result.ToArray(EndOfLine)
  
  For tempInt As Integer = 0 To tempArray.LastIndex
    Listbox1.AddRow(tempArray(tempInt))
  Next tempInt
End If

But when I sandbox it using AppWrapper I get the following tempShell.Result:

lsof: can’t get PID byte count: Operation not permitted

How can I run a sandboxed LSOF Shell command? Is there a way to not return the PID or PID byte count, as I don’t need them?

If your app is sandboxed, you shouldn’t be able to use lsof (except perhaps for your own process, but I’m not even sure), as that’s what the sandbox is here for.
Have you tried the entitlements in AppWrapper? I seem to recall there is one for using the shell…

1 Like

Just use the “id” command instead of lsof if you need more than just your user name or use “whoami” directly if that’s all that you need.

I only want to access the open files of the current user (not every user) — that is why I am using -u whoami in the command.

I am not sure how the ‘id’ command would help me since I need to return my open files, not get info on my id.

I have not seen any Shell entitlements in AppWrapper — where are they? Or is there a plist entry for Shell access? Or is there a way to get LSOF to not return the PID values?

Yeah, that would defeat the purpose of sandboxing. Off the top of my head, I don’t know of an exemption.

Sorry - I thought you were trying to pull the ID from the output.

As Thom mentions, Sandboxing prohibits lsof.

There’s no entitlements for shell since you’d need entitlements for the command line app that you’re executing IN the shell. Battled that one for years with the BRU command line tool. Ended up having to play sudoers games and the backup tools can’t be sandboxed (that would limit their usefulness in that case).

Note in shell you always need the full path to the command/excutable.

/usr/bin/lsof (could be different)

Is this correct?:

Sandboxing doesn’t prohibit the use of the Shell command, but does prohibit certain commands within a Shell eg LSOF.

As far as i know it doesn’t. You just need to make sure you set the context, as shell works as a blank terminal without a profile and without settings.

I have found a solution that works using NSUserUnixTaskMBS. It will run any Script shell (MBS have a similar class for running AppleScripts too). It runs these outside of the App Sandbox, meaning you can break the normal rules.

Note that: your Shell Script must have the extension .sh, must be set with executable bit on via chmod +x, must have the #!/bin/bash shebang prefix and linefeeds for EndOfLine.

You have to manually place your Script file (myScript.sh) into the special read-only (to your app) Application Scripts (user drag or Open Folder). Set the output to a text file (normally not on Desktop!) — I had to send the output to a text file as I was unable to return the text Results in the executeFinished Event. Then Execute the Script (the cog icon will spin in the menu bar). I have created a ClassNSUserUnixTaskMBS that sets a Boolean isFinished to True when Event executeFinished runs (you could put your extra action code here instead).

Here is the code:

If Not NSUserScriptTaskMBS.Available Then
  Return
End If

'Var f As FolderItem = NSUserUnixTaskMBS.ScriptFolder 'this should work, but always returns Nil
Var f As FolderItem = SpecialFolder.UserLibrary.Child("Application Scripts").Child(NSBundleMBS.mainBundle.bundleIdentifier).Child("myScript.sh") 'it needs executable bit on ie chmod +x /pathtofile/
If f = Nil Or Not f.Exists Then
  Return
End If

Var tempNSErrorMBS As NSErrorMBS
Var tempNSUserUnixTaskMBS As ClassNSUserUnixTaskMBS

Try
  tempNSUserUnixTaskMBS = New ClassNSUserUnixTaskMBS(f, tempNSErrorMBS)
  
Catch Error
  Var ErrorMessage As String = Error.Message
  Return
End Try

f = SpecialFolder.Desktop.Child("myResults.txt") 'the output file must exist
f.Remove
If f <> Nil And Not f.Exists Then
  Var tempTextOutputStream As TextOutputStream = TextOutputStream.Create(f)
  
  If tempTextOutputStream <> Nil Then
    tempTextOutputStream.Close
  End If
End If
Var tempNSFileHandleMBS As NSFileHandleMBS = NSFileHandleMBS.fileHandleForWritingToFile(f, tempNSErrorMBS)

If tempNSErrorMBS <> Nil Then
  Var ErrorMessage As String = tempNSErrorMBS.Description
  Return
End If

tempNSUserUnixTaskMBS.standardOutput = tempNSFileHandleMBS
tempNSUserUnixTaskMBS.execute

Do
  DelayMBS 3.0
  Thread.YieldToNext
Loop Until tempNSUserUnixTaskMBS.isFinished

I would expect that if Apple catches you doing this, they’ll reject your app and/or plug that security hole in the next OS update.

Actually, this usage does seem officially supported according to https://developer.apple.com/documentation/foundation/nsuserunixtask:

If the application is sandboxed, then the script must be in the FileManager.SearchPathDirectory.applicationScriptsDirectory folder.

Edit: I’m not sure how it works with the output stream going to the user’s desktop… or why you’d put temp files there in the first place.

1 Like

In the context of this thread, I only meant that being allowed to run an LSOF Shell command was breaking the normal sandbox rules, but is in no way malware. Users can view my two line Shell Script as it is in plain text.

You are right, as this would add a new list of sandbox problems! I used the Desktop only for my testing and this example, but I use different code in my app. This was to allow others to test the code as easily as possible, and why I added “(normally not on Desktop!)”.