Post

Replies

Boosts

Views

Activity

App cannot open embedded command-line tool
This is very similar to the (unresolved) issue here: Signed Command Line Tool Rejected by spctl I'm attempting to distribute an internal command-line tool called bootstrap-mercury to engineers at my organization. The command-line tool sets up a development environment; it installs Nix, the Xcode command-line tools, sets up a Postgres instance, edits configuration files, and so on. I would like users to be able to download the file and double-click it to run it. I know that Gatekeeper blocks command-line tools, so I've embedded the command-line tool in an app. The app is a simple Swift executable with this source code: import Foundation import AppKit import OSLog import SwiftUI @main struct BootstrapMercuryApp: App { @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate var body: some Scene { WindowGroup { ContentView() } } } struct ContentView: View { var body: some View { Text("bootstrap-mercury") .padding() } } class AppDelegate: NSObject, NSApplicationDelegate { var popover = NSPopover.init() func applicationDidFinishLaunching(_ notification: Notification) { if let window = NSApplication.shared.windows.first { window.close() } let logger = Logger() guard let executableUrl = Bundle.main.url(forAuxiliaryExecutable: "bootstrap-mercury-cli") else { fatalError("Couldn't find embedded bootstrap-mercury executable") } logger.log("Embedded bootstrap-mercury executable URL: \(executableUrl.absoluteString, privacy: .public)") guard let terminalUrl = NSWorkspace.shared.urlForApplication(withBundleIdentifier: "com.apple.Terminal") else { fatalError("Couldn't find Terminal.app") } logger.log("Terminal.app URL: \(terminalUrl, privacy: .public)") let configuration = NSWorkspace.OpenConfiguration() configuration.activates = true configuration.allowsRunningApplicationSubstitution = false configuration.createsNewApplicationInstance = true configuration.hides = false NSWorkspace.shared.open( [executableUrl], withApplicationAt: terminalUrl, configuration: configuration, completionHandler: { (maybeApp: NSRunningApplication?, maybeError: Error?) -> Void in logger.log("Terminal.app completionHandler fired") if let app = maybeApp { logger.log("Terminal.app started succesfully: \(app, privacy: .public)") } if let error = maybeError { logger.log("Terminal.app failed to start: \(error as NSError, privacy: .public)") } exit(0) } ) logger.log("Launched Terminal.app") } } The key here is it attempts to open the embedded command-line tool with Terminal.app, so that the user can see log messages printed to STDOUT and reply to prompts on STDIN. I assemble the app like this: bootstrap-mercury.app └── Contents ├── Info.plist ├── MacOS │ ├── bootstrap-mercury │ └── bootstrap-mercury-cli └── Resources └── bootstrap-mercury.icns Then, I sign and notarize/staple the app. (It later gets stuffed into a .dmg, which is in turn notarized/stapled, but I don't think that's related.) spctl verifies it: $ spctl -a -v --raw bootstrap-mercury.app bootstrap-mercury.app: accepted <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>assessment:authority</key> <dict> <key>assessment:authority:flags</key> <integer>2</integer> <key>assessment:authority:row</key> <integer>11</integer> <key>assessment:authority:source</key> <string>Notarized Developer ID</string> </dict> <key>assessment:remote</key> <true/> <key>assessment:verdict</key> <true/> </dict> </plist> codesign verifies it: $ codesign -vvvv -verify bootstrap-mercury.app bootstrap-mercury.app: edited signature app bundle with Mach-O universal (x86_64 arm64) [bootstrap-mercury-app-arm64] $ codesign -vvvv -R="notarized" --check-notarization bootstrap-mercury.app --prepared:/Users/wiggles/bootstrap-mercury/target/release/bootstrap-mercury.app/Contents/MacOS/bootstrap-mercury-cli --validated:/Users/wiggles/bootstrap-mercury/target/release/bootstrap-mercury.app/Contents/MacOS/bootstrap-mercury-cli bootstrap-mercury.app: valid on disk bootstrap-mercury.app: satisfies its Designated Requirement bootstrap-mercury.app: explicit requirement satisfied The list of entitlements is empty: $ codesign -d -vvv --entitlements :- bootstrap-mercury.app/Contents/MacOS/bootstrap-mercury-cli ... Warning: Specifying ':' in the path is deprecated and will not work in a future release <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "https://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><dict></dict></plist> (Note: I've tried setting com.apple.security.app-sandbox for the Swift wrapper executable bootstrap-mercury.app/Contents/MacOS/bootstrap-mercury and com.apple.security.app-sandbox as well as com.apple.security.inherit for the bootstrap-mercury.app/Contents/MacOS/bootstrap-mercury-cli, but the bootstrap-mercury-cli gets immediately SIGKILLed on launch and I'm not sure why; it's a relatively simple Rust command-line tool that doesn't do anything too complex.) When launching the app on my machine, it works correctly. On a coworker's machine, it shows several errors: "'bootstrap-mercury' is an app downloaded from the Internet. Are you sure you want to open it?" "'bootstrap-mercury' would like to access files in your Downloads folder." "'bootstrap-mercury-cli' can't be opened because the identity of the developer cannot be confirmed." "The application 'Terminal' can't be opened. -128" The first two are expected. The last two are quite strange. bootstrap-mercury-cli is signed with my Developer ID Application. Why can't macOS identify that?
3
1
1k
Sep ’22