Problem summary
I have a macOS helper app that is launched from a sandboxed main app. The helper:
- has
com.apple.security.app-sandbox = true
andcom.apple.security.inherit = true
in its entitlements, - is signed and embedded inside the main app bundle (placed next to the main executable in
Contents/MacOS
), - reports
entitlement_check = 1
(code signature contains sandbox entitlement, implemented via SecStaticCode… check), sandbox_check(getpid(), NULL, 0)
returns1
(runtime sandbox enforcement present),APP_SANDBOX_CONTAINER_ID
environment variable is not present (0).
Despite that, Cocoa APIs return non-container home paths:
NSHomeDirectory()
returns/Users/<me>/
(the real home).[[NSFileManager defaultManager] URLsForDirectory:inDomains:]
andURLForDirectory:inDomain:appropriateForURL:create:error:
return paths rooted at/Users/<me>/
(not under~/Library/Containers/<app_id>/Data/...
) — i.e. they look like non-sandboxed locations.
However, one important exception: URLForDirectory:...
for NSItemReplacementDirectory
(temporary/replacement items) does return a path under the helper's container (example: ~/Library/Containers/<app_id>/Data/tmp/TemporaryItems/NSIRD_<helper_name>_hfc1bZ
).
This proves the sandbox is active for some FileManager APIs, yet standard directory lookups (Application Support, Documents, Caches, and NSHomeDirectory()
) are not being redirected to the container.
What I expect
The helper (which inherits the sandbox and is clearly sandboxed) should get container-scoped paths from Cocoa’s FileManager APIs (Application Support, Documents, Caches), i.e. paths under the helper’s container: /Users/<me>/Library/Containers/<app_id>/Data/...
.
What I tried / diagnostics already gathered
Entitlements & code signature
codesign -d --entitlements :- /path/to/Helper.app/Contents/MacOS/Helper
# shows com.apple.security.app-sandbox = true and com.apple.security.inherit = true
Runtime checks (Objective-C++ inside helper):
extern "C" int sandbox_check(pid_t pid, const char *op, int flags);
NSLog(@"entitlement_check = %d", entitlement_check()); // SecStaticCode check
NSLog(@"env_variable_check = %d", (getenv("APP_SANDBOX_CONTAINER_ID") != NULL));
NSLog(@"runtime_sandbox_check = %d", sandbox_check(getpid(), nullptr, 0));
NSLog(@"NSHomeDirectory = %s", NSHomeDirectory());
NSArray *urls = [[NSFileManager defaultManager]
URLsForDirectory:NSApplicationSupportDirectory
inDomains:NSUserDomainMask];
NSLog(@"URLsForDirectory: %@", urls);
Observed output:
entitlement_check = 1
env_variable_check = 0
runtime_sandbox_check = 1
NSHomeDirectory = /Users/<me>
URLsForDirectory: ( "file:///Users/<me>/Library/Application%20Support/..." )
Temporary/replacement directory (evidence sandbox active for some APIs):
NSURL *tmpReplacement = [[NSFileManager defaultManager]
URLForDirectory:NSItemReplacementDirectory
inDomain:NSUserDomainMask
appropriateForURL:nil
create:YES
error:&err];
NSLog(@"NSItemReplacementDirectory: %@", tmpReplacement.path);
Observed output (example):
/Users/<me>/Library/Containers/<app_id>/Data/tmp/TemporaryItems/NSIRD_<helper_name>_hfc1bZ
Other facts
- Calls to
NSHomeDirectory()
andURLsForDirectory:
are made aftermain()
to avoid "too early" initialization problems. - Helper is placed in
Contents/MacOS
(notContents/Library/LoginItems
). - Helper is a non-GUI helper binary launched by the main app (not an XPC service).
- macOS version: Sequoia 15.6
Questions
-
Why do
NSHomeDirectory()
andURLsForDirectory:
return the real/Users/<me>/...
paths in a helper process that is clearly sandboxed (entitlement + runtime enforcement), whileNSItemReplacementDirectory
returns a container-scoped temporary path? -
Is this behavior related to how the helper is packaged or launched (e.g., placement in
Contents/MacOS
vsContents/Library/LoginItems
, or whether it is launched withposix_spawn
/fork+exec
vs other APIs)? -
Are there additional entitlements or packaging rules required for a helper that inherits sandbox to have Cocoa directory APIs redirected to the container (for Application Support, Documents, Caches)?
*Thanks in advance — I can add any requested logs