MacOS application "Operation Not Permitted" with ditto

Hey all,

I am working on my self updater and I am hitting an error replacing my binaries. Basically, when there is a new release my self updater checks and prompts the user to update. When the user accepts to update my applicaiton will download the .zip for the specified version and for the operating system, which is darwin in this case. Then, once the .zip has been download I use ditto to replace the existing binaries, but I am hitting the error "Operation not permitted" on darwin.

Here is my code for updating:

   // Download zip of latest version (Works)
	homeDir, _ := os.UserHomeDir()
	downloadPath := filepath.Join(homeDir, "Downloads", "tooler.zip")
	err := exec.Command("curl", "-L", "-H", "Accept: application/octet-stream", "-H", "Authorization: Bearer REMOVED_TOKEN", "-H", "X-GitHub-Api-Version: 2022-11-28", release.AssetURL, "-o", downloadPath).Run()
	if err != nil {
		return fmt.Errorf("binary update failed during curl: %v", err)
	}

   // get executable path for where we need to replace (Works)
	cmdPath, err := os.Executable()
	appPath := strings.TrimSuffix(cmdPath, "tooler.app/Contents/MacOS/tooler")
	if err != nil {
		appPath = "/Applications/"
	}

   // Cleanup zip after everything executes
	defer func() {
		err = exec.Command("rm", downloadPath).Run()
		if err != nil {
			// return fmt.Errorf("binary update failed during removal: %v", err)
		}
	}()

   // Replace .app contents, but hits "operation not permitted" (Does not work)
	cmd := exec.Command("ditto", "-xk", downloadPath, appPath)
	var out bytes.Buffer
	var stderr bytes.Buffer
	cmd.Stdout = &out
	cmd.Stderr = &stderr
	err = cmd.Run()
	if err != nil {
		return fmt.Errorf("binary update failed during ditto: %v \n Args: %v \n CmdPath: %v \n AppPath %v", stderr.String(), cmd.Args, cmdPath, appPath)
	}

	return nil

The first message before the line break is my application logging where the failure was and the rest of "ditto:" messages are the errors output by ditto.

Update failed: binary update failed during ditto:

ditto: /Applications//tooler.app/Contents/_CodeSignature/CodeResources: Operation not permitted
ditto: /Applications//tooler.app/Contents/MacOS/tooler: Operation not permitted
ditto: /Applications//tooler.app/Contents/Resources/icons.icns: Operation not permitted
ditto: /Applications//tooler.app/Contents/Info.plist: Operation not permitted

Here are my entitlements

<?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>com.apple.security.app-sandbox</key>
    <true/>
    <key>com.apple.security.network.client</key>
    <true/>
    <key>com.apple.security.network.server</key>
    <true/>
    <key>com.apple.security.files.user-selected.read-write</key>
    <true/>
    <key>com.apple.security.files.downloads.read-write</key>
    <true/>
</dict>
</plist>

Could this ditto error that the operation is not permitted be related to my entitlements? My .app is signed and notarized before distribution. I am able to download the zip, unzip and run the application without problems other than when I try to run the self update and it fails due to "operation not permitted".

Answered by DTS Engineer in 799092022

This is Gatekeeper app bundle protecting kicking in. See the WWDC 2022 session I link to in Trusted Execution Resources.

The best way to avoid problems like this is to not modify an app’s bundle after it’s been launched. So, your updater should create an entirely new copy of the app bundle and then replace the old copy with that new copy. If you want to minimise your download size by not downloading the app’s resources, copy those from your installed app and then install the newly downloaded binaries into that copy.

Share and Enjoy

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

This is Gatekeeper app bundle protecting kicking in. See the WWDC 2022 session I link to in Trusted Execution Resources.

The best way to avoid problems like this is to not modify an app’s bundle after it’s been launched. So, your updater should create an entirely new copy of the app bundle and then replace the old copy with that new copy. If you want to minimise your download size by not downloading the app’s resources, copy those from your installed app and then install the newly downloaded binaries into that copy.

Share and Enjoy

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

MacOS application "Operation Not Permitted" with ditto
 
 
Q