Gatekeeper rejects notarized app ("Unnotarized Developer ID") when using necessary entitlements

Hello everyone,

I'm hoping to get some guidance on a frustrating codesigning issue. I have a macOS application that successfully completes the entire notarization and stapling process, but it is still rejected by Gatekeeper during the final verification step. The rejection only happens when I apply the entitlements that I believe are necessary for my app's functionality.

The application is built with PyInstaller and has the following components:

  • A main executable written in Python.
  • A bundled Tcl/Tk instance for the GUI.
  • Embedded Playwright components, which include the Node.js runtime and a full Chromium browser instance. These are located deep inside the .app bundle.

The Problem

The core of my application relies on Playwright to perform some automated tasks, and its bundled Chromium browser requires specific entitlements to function under the Hardened Runtime. Specifically, it needs com.apple.security.cs.allow-jit and com.apple.security.cs.allow-unsigned-executable-memory.

My signing process is as follows:

  1. Prepare Entitlements: I use two separate .plist files:
  • main_app_entitlements.plist: This is for the main Python executable and only contains com.apple.security.cs.allow-jit.
  • jit_helper_entitlements.plist: This is for the node and Chromium Helper executables within the Playwright framework. It contains both com.apple.security.cs.allow-jit and com.apple.security.cs.allow-unsigned-executable-memory.
  1. Inside-Out Signing: I perform a deep signing process. I find all binaries, dylibs, and frameworks, sort them by path length (deepest first), and sign each one individually with the appropriate entitlements. The main .app bundle is signed last.
  2. Notarization: I zip the .app bundle and submit it using xcrun notarytool submit --wait. The tool reports a successful notarization every time.
  3. Stapling: I use xcrun stapler staple on the .app bundle, and it confirms that the ticket was successfully stapled.

The point of failure

The final step is to verify the result with spctl: spctl --assess --type execute --verbose --ignore-cache "MyApp.app" This is where it fails.

The output is: MyApp.app: rejected source=Unnotarized Developer ID This "Unnotarized Developer ID" message is confusing because xcrun notarytool and stapler both report complete success.

The crucial detail

If I run the entire process without any entitlements—just signing with the Hardened Runtime enabled—the final spctl assessment passes. However, the application then crashes at runtime as soon as it tries to use Playwright, which is expected since the browser helpers are missing their required JIT entitlements.

My question

Is there a known issue where using com.apple.security.cs.allow-jit or com.apple.security.cs.allow-unsigned-executable-memory on nested helper executables can invalidate an otherwise successful notarization?

Is my strategy of applying different, granular entitlements to different executables within the same app bundle correct?

Could the issue be related to how or when these entitlements are applied during an "inside-out" signing process? Is there a better way to structure the signing of these complex components?

I'm confident the notarization itself is working, but it seems Gatekeeper's local assessment is stricter and is being tripped up by my entitlement configuration.

Thank you in advance for any help or suggestions you can provide

Answered by DTS Engineer in 850267022

You listed various hardened runtime exception entitlements. That list didn’t include com.apple.security.cs.disable-library-validation, which is weird because none of those other entitlements affect Gatekeeper but the library validation one does. For the details, see Resolving Gatekeeper Problems Caused by Dangling Load Command Paths.

So, are you sure you haven’t included com.apple.security.cs.disable-library-validation in your entitlements.

The final step is to verify the result with spct

spctl is not a tool I put much faith in. Rather, I recommend the approach in Testing a Notarised Product.

Share and Enjoy

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

You listed various hardened runtime exception entitlements. That list didn’t include com.apple.security.cs.disable-library-validation, which is weird because none of those other entitlements affect Gatekeeper but the library validation one does. For the details, see Resolving Gatekeeper Problems Caused by Dangling Load Command Paths.

So, are you sure you haven’t included com.apple.security.cs.disable-library-validation in your entitlements.

The final step is to verify the result with spct

spctl is not a tool I put much faith in. Rather, I recommend the approach in Testing a Notarised Product.

Share and Enjoy

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

No, I haven't added that. Is it possible that maybe this library entitlement is added automatically during codesigning?

Actually after further testing, I realized that the culprit seem to be the entitlements I assign to the node and Chromium Helper executables within the Playwright framework ( com.apple.security.cs.allow-jit and com.apple.security.cs.allow-unsigned-executable-memory ).

The JIT entitlement applied to the main python executable does not affect gatekeeper.

Gatekeeper rejects notarized app ("Unnotarized Developer ID") when using necessary entitlements
 
 
Q