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!
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"