Unable to run embedded binary due to quarantine

Hi!

I've been scratching my brain for a few days now to no avail.

I have a Perl project that I need to embed within my app. Perl includes a pp command (https://metacpan.org/pod/pp) which takes the runtime binary and then slaps the Perl code at the end of the binary itself which in brings some woes in a sense that the binary then needs to be "fixed" (https://github.com/rschupp/PAR-Packer/tree/master/contrib/pp_osx_codesign_fix) by removing the linker-provided signature and fixing LINKEDIT and LC_SYMTAB header sections of the binary.

Nevertheless, I've successfully gotten the binary built, fixed up and codesigned it via codesign -s '$CS' mytool (where $CS is the codesigning identity). I can verify the signature as valid using codesign -v --display mytool:

Identifier=mytool
Format=Mach-O thin (arm64)
CodeDirectory v=20400 size=24396 flags=0x0(none) hashes=757+2 location=embedded
Signature size=4820
Signed Time=5. 1. 2026 at 8:54:53 PM
Info.plist=not bound
TeamIdentifier=XXXXXXX
Sealed Resources=none
Internal requirements count=1 size=188

It runs without any issues in Terminal, which is great.

As I need to incorporate this binary in my app which is sandboxed, given my experience with other binaries that I'm including in the app, I need to codesign the binary with entitlements com.apple.security.app-sandbox and com.apple.security.inherit. So, I run:

codesign -s '$CS' --force --entitlements ./MyTool.entitlements --identifier com.charliemonroe.mytool mytool

... where the entitlements file contains only the two entitlements mentioned above.

Now I add the binary to the Xcode project, add it to the copy resources phase and I can confirm that it's within the bundle and that it's codesigned:

codesign -vvvv --display MyApp.app/Contents/Resources/mytool

Identifier=com.xxx.xxx.xxx
Format=Mach-O thin (arm64)
CodeDirectory v=20500 size=24590 flags=0x10000(runtime) hashes=757+7 location=embedded
VersionPlatform=1
VersionMin=1703936
VersionSDK=1704448
Hash type=sha256 size=32
CandidateCDHash sha256=0a9f93af81e8e5cb286c3df6e638b2f78ab83a9e
CandidateCDHashFull sha256=0a9f93af81e8e5cb286c3df6e638b2f78ab83a9edf463ce45d1cd3f89a6a4a00
Hash choices=sha256
CMSDigest=0a9f93af81e8e5cb286c3df6e638b2f78ab83a9edf463ce45d1cd3f89a6a4a00
CMSDigestType=2
Executable Segment base=0
Executable Segment limit=32768
Executable Segment flags=0x1
Page size=16384
CDHash=0a9f93af81e8e5cb286c3df6e638b2f78ab83a9e
Signature size=4800
Authority=Apple Development: XXXXXX (XXXXXX)
Authority=Apple Worldwide Developer Relations Certification Authority
Authority=Apple Root CA
Signed Time=9. 1. 2026 at 5:12:22 PM
Info.plist=not bound
TeamIdentifier=XXXXX
Runtime Version=26.2.0
Sealed Resources=none
Internal requirements count=1 size=196

codesign --display --entitlements :- MyApp.app/Contents/Resources/mytool

<?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><key>com.apple.security.app-sandbox</key><true/><key>com.apple.security.inherit</key><true/></dict></plist>

All seems to be in order! But not to Gatekeeper... Attempting to run this using the following code:

let process = Process()
process.executableURL = Bundle.main.url(forResource: "mytool", withExtension: nil)!
process.arguments = arguments
try process.run()
process.waitUntilExit()

Results in failure:

  • process.terminationStatus == 255
  • Console shows the following issues:
default	17:12:40.686604+0100	secinitd	mytool[88240]: root path for bundle "<private>" of main executable "<private>"
default	17:12:40.691701+0100	secinitd	mytool[88240]: AppSandbox request successful
error	17:12:40.698116+0100	kernel	exec of /Users/charliemonroe/Library/Containers/com.charliemonroe.MyApp/Data/tmp/par-636861726c69656d6f6e726f65/cache-9c78515c29320789b5a543075f2fa0f8072735ae/mytool denied since it was quarantined by MyApp and created without user consent, qtn-flags was 0x00000086

Quarantine, hum? So I ran:

xattr -l MyApp.app/Contents/Resources/mytool

None listed.

It is a signed binary within a signed app. There are other binaries that are included within the app and run just fine exactly this way (most of them built externally using C/C++ and then codesigned exectly as per above), so I really don't think it's an issue with the app's sandbox setup...

Is there anyone who would be able to help with this? Thank you in advance!

Answered by DTS Engineer in 871864022

Let’s start with the trivial:

MyApp.app/Contents/Resources/mytool

Contents/Resources is not the right place for a helper tool. Rather, I recommend Contents/MacOS. See Placing content in a bundle.

Also check out Embedding a command-line tool in a sandboxed app for general background.

However, none of that is the cause of the really issue here. Rather, consider the error your got:

exec of /Users/charliemonroe/Library/Containers/com.charliemonroe.MyApp/Data/tmp/par-636861726c69656d6f6e726f65/cache-9c78515c29320789b5a543075f2fa0f8072735ae/mytool denied …`

Note the path. It’s not Contents/Resources/mytool but rather something in your app’s temporary directory.

I suspect that the wrapper applied by the pp tool is actually unpacking code into the temporary directory and then running that. This won’t work because sandboxed apps aren’t allow to create and execute code. Specifically, when a sandbox program creates a file, that file is always quarantined. If the file is an executable, running that quarantined executable triggers Gatekeeper which blocks its execution.

This general technique is simply not compatible with the App Sandbox. You’ll need to find an alternative path. I don’t have a solid recommendation because I’m not really hooked up to the Perl ecosystem, but ideally you’d want a mechanism that either compiles the Perl code to native or that embeds and runs the Perl interpreter and thus only ends up unpacked the .pl files.

Note While .pl files are code, you’d end up running in a way that doesn’t invoke Gatekeeper and thus the quarantine doesn’t matter.

Share and Enjoy

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

Accepted Answer

Let’s start with the trivial:

MyApp.app/Contents/Resources/mytool

Contents/Resources is not the right place for a helper tool. Rather, I recommend Contents/MacOS. See Placing content in a bundle.

Also check out Embedding a command-line tool in a sandboxed app for general background.

However, none of that is the cause of the really issue here. Rather, consider the error your got:

exec of /Users/charliemonroe/Library/Containers/com.charliemonroe.MyApp/Data/tmp/par-636861726c69656d6f6e726f65/cache-9c78515c29320789b5a543075f2fa0f8072735ae/mytool denied …`

Note the path. It’s not Contents/Resources/mytool but rather something in your app’s temporary directory.

I suspect that the wrapper applied by the pp tool is actually unpacking code into the temporary directory and then running that. This won’t work because sandboxed apps aren’t allow to create and execute code. Specifically, when a sandbox program creates a file, that file is always quarantined. If the file is an executable, running that quarantined executable triggers Gatekeeper which blocks its execution.

This general technique is simply not compatible with the App Sandbox. You’ll need to find an alternative path. I don’t have a solid recommendation because I’m not really hooked up to the Perl ecosystem, but ideally you’d want a mechanism that either compiles the Perl code to native or that embeds and runs the Perl interpreter and thus only ends up unpacked the .pl files.

Note While .pl files are code, you’d end up running in a way that doesn’t invoke Gatekeeper and thus the quarantine doesn’t matter.

Share and Enjoy

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

Thank you so much for answering!

Contents/Resources is not the right place for a helper tool.

Thanks, I'll try to move it. This is historically the place I've seen such tools to be placed (including Apple software in the past - but we're talking about 10-15 years ago) and I just kept putting the binaries there... But you are right, when I think about it, it is probably incorrect place.

I suspect that the wrapper applied by the pp tool is actually unpacking code into the temporary directory and then running that.

Huh! That did not occur to me! I suspected that this was due to App Translocation that the binary was launched from a temporary location as a result of the quarantine... So I did not even check the path!

I've used FSMonitor (app for displaying fsevents in real time) and indeed, this is what happens.

I fully understand this is not compatible with App Sandbox, so I will need to find another solution.

Embedding Perl interpreter was on my list of possible workarounds, so I'll need to look into that, though it seemed like a less "clean" solution than one single standalone binary...

One more small question:

Note While .pl files are code, you’d end up running in a way that doesn’t invoke Gatekeeper and thus the quarantine doesn’t matter.

Where would you recommend placing such files? Along with the Perl interpreter into Contents/MacOS or, as these are generally just basic text files, they are fine in Contents/Resources?

Unable to run embedded binary due to quarantine
 
 
Q