How to run the DropDMG command line tool from within another app?

Hi there,

First of all, thanks for making my life easier with DropDMG! :raised_hands:

I am trying to use DropDMG using NSTask/Process from within my Swift app, however I always get this error:

Error -1743/errAEEventNotPermitted. The app that is running “dropdmg” needs permission to control DropDMG. You can grant this from System Settings ‣ Privacy & Security ‣ Automation (DropDMG Manual: Granting Automation Access). If you are trying to control DropDMG from Xcode, please see this page for a workaround: DropDMG Manual: Command-line tool

I’ve not been able to trigger the “My App” wants access to control “Drop DMG” message:


Using SD Notary or the Terminal, everything works as expected.

I’ve tried the suggested “tell application "DropDMG" to get version” method:

 let appleScript = "tell application \"DropDMG\" to get version"
        var error: NSDictionary?
        
        if let scriptObject = NSAppleScript(source: appleScript) {
            let result = scriptObject.executeAndReturnError(&error)
            if let error = error {
                print("Error: \(error)")
            } else {
                print("Result: \(result.stringValue ?? "No result")")
            }
        }

It prints the correct version, but still no message pop up.

I also tried automationmodetool enable-automationmode-without-authentication As a response I get

Setting up machine to allow Automation Mode without requiring user authentication… succeeded.

But for some reason I am still being asked to grand permission. :thinking:

I’ve also added the NSAppleEventsUsageDescription key to my info.plist.

My app is not sandboxed (just like SD Notary, so that shouldn’t be a problem)

Here is the basic code I use to run DropDMG:

private static func runDropDMG(version: Version, appURL: URL) {
        let dropDMGPath = "/Applications/DropDMG.app/Contents/Frameworks/DropDMGFramework.framework/Versions/A/dropdmg" // Also tried /usr/local/bin/dropdmg
        
        let arguments = [
            "--config-name=\"Test-Config\"",
            "\"\(appURL.path)\""
        ]
                
        let process = Process()
        process.executableURL = URL(fileURLWithPath: dropDMGPath)
        process.arguments = arguments
        
        let outputPipe = Pipe()
        let errorPipe = Pipe()
        process.standardOutput = outputPipe
        process.standardError = errorPipe
        
        do {
            try process.run()
            
            process.waitUntilExit()
            
            let outputData = outputPipe.fileHandleForReading.readDataToEndOfFile()
            let output = String(data: outputData, encoding: .utf8) ?? "No output"
            print("Output: \(output)")
            
            let errorData = errorPipe.fileHandleForReading.readDataToEndOfFile()
            let errorOutput = String(data: errorData, encoding: .utf8) ?? "No errors"
            print("Errors: \(errorOutput)")
            if errorOutput.isEmpty == false {
                let alert = NSAlert()
                alert.messageText = "Error using DropDMG"
                alert.alertStyle = .warning
                alert.informativeText = errorOutput
                alert.addButton(withTitle: "OK")
                alert.runModal()
            }
                
        
        } catch {
            print("Failed to run the process: \(error)")
            let alert = NSAlert(error: error)
            alert.alertStyle = .warning
            alert.addButton(withTitle: "OK")
            alert.runModal()
        }
    }

What am I doing wrong? Any suggestions?

I’m a bit confused here. Are you saying that NSAppleScript from your app works even though you never got the prompt? Does your app show up in System Settings ‣ Privacy & Security ‣ Automation?

Maybe something is messed up in the TCC database and you could either reset it or temporarily change your app’s bundle ID.

Hi, I did reset it using tccutil reset AppleEvents

Yes NSAppleScript works, even if the app is sandboxed.

No, it never shows up in System Settings ‣ Privacy & Security ‣ Automation

I tried using a new bundle ID - same results

Now I’m also confused.

Could you maybe test if Apple Script works on your device?

Code: GitHub - Iomegan/Apple-Script-Test
Binary: http://witt-software.com/tmp/ast.zip

It even works in all my VMs. Never ever do I get the prompt…

Well, at least get version works. Other scripts like tell application "System Events" to beep don’t.

Anyway, there must be a way to trigger the prompt in order to use DropDMG.

Okay, two things:

  1. You have to use NSUserAppleScriptTask not NSAppleScript along with a .scpt file that you can install in your app’s bundle (non sandboxed apps) or manually in ~/Library/Application Scripts/ (sandboxed apps, the user has to move the script there by himself). Luckily my app doesn’t have to be sandboxed. EDIT: Or just use /usr/bin/osascript with NSTask or Process
  2. It looks like tell application "DropDMG" to get version is not enough to trigger the prompt, at least on macOS 15 Sequoia. ( got that script from here). tell application "DropDMG" to beep works. I’m not sure why, maybe some scripts that do not really control another don’t need the the authorization by the user anymore.

I think you are right the something changed with AppleScript so that get version now works without needing access.

Your sample app does not work for me with beep. It seems to be failing because the app is sandboxed and doesn’t have the right entitlements. I think you would need to add com.apple.security.temporary-exception.apple-events and com.apple.security.automation.apple-events.

NSUserAppleScriptTask runs the script outside the sandbox using a system process and so doesn’t need all the same entitlements.

I suppose as a last resort you could use NSUserUnixTask with a script that trampolines to dropdmg.

Well it does work now with Process, osascript and tell application "DropDMG" to *do something*. Thanks for your help!

1 Like