“a sealed resource is missing or invalid” on an installed, notarized application

We have a native ARM64 application. The application is a development environment and native compiler for the language Common Lisp. CL has a foreign function interface, which allows loading of .dylib files into CL and calling functions in them from CL. For this reason, we add certain entitlements. See below.

It is notarized and installed on macOS 14.7. When I run spctl on it I get this:

$ spctl --assess -v /Applications/AllegroCL64.app
/Applications/AllegroCL64.app: rejected (the code is valid but does not seem to be an app)

That’s before I run it. Which is odd because the app is notarized. When I run the app, it asks for a license file and installs it into /Applications/AllegroCL64.app/Contents/Resources/ and after that, the spctl shows this:

$ spctl --assess -v /Applications/AllegroCL64.app /Applications/AllegroCL64.app: a sealed resource is missing or invalid

I assume the mere act of copying the license (a file called devel.lic which is a small text file) is causing this. Why does it say it “does not seem to be an app”?

This self-modification of the files in the Contents/Resources directory is a huge feature. We allow downloading of patches, which add features and fix bugs in the product. Is this going to be a problem, going forward? I don’t remember seeing this result from spctl before and I have a feeling it’s a new , due to tightening of security policies, etc.

All of this is quite worrying to us.

More details of the app:

$ codesign -vvvv mlisp
mlisp: valid on disk
mlisp: satisfies its Designated Requirement
$ codesign -d --entitlements - /Applications/AllegroCL64.app
Executable=/Applications/AllegroCL64.app/Contents/MacOS/AllegroCL64
[Dict]
    [Key] com.apple.security.cs.allow-dyld-environment-variables
    [Value]
        [Bool] true
    [Key] com.apple.security.cs.allow-jit
    [Value]
        [Bool] true
    [Key] com.apple.security.cs.disable-library-validation
    [Value]
        [Bool] true
    [Key] com.apple.security.get-task-allow
    [Value]
        [Bool] true
$ 

Other details:

  • The app was built with the Command Line tools version 2395 on macOS 12.x.
Answered by DTS Engineer in 809688022

There are two cases to consider here:

  • Self-modifying apps

  • Software updates

I’ll tackle each in turn.


Modifying your own app is not a supported technique. This is not a change in policy. It’s been this way since 1984. There are plenty of historical docs that are clear about this, and we also state it clearly in the Separate read-only and read/write content section of Embedding nonstandard code structures in a bundle.

The nature of unsupported things is that sometimes they work and sometimes they don’t. So the fact that something might have worked in some specific situations isn’t helpful. And even if you get something working today, you may run into problems in the future.

My goal is to help you move to a supported approach that should work now and in the future. I regularly help folks in your situation. For example, you might find this post and this post enlightening.

Regarding your licence setup, storing the licence outside of the app is the correct path forward. Exactly how you do that depends on a variety of tradeoffs. Personally I like this approach:

  • Store the licence in Application Support directory (.applicationSupportDirectory) in the user domain (.userDomainMask).

  • Look for the licence in the same directory in all domains (.allDomainsMask).

That way the site admin can move the file from their home directory to /Library/Application Support, or the much more obscure /Network/Library/Application Support, if they want the app to work for other users.

Note I’m referencing the FileManager.urls(for:in:) method here.

Regarding stuff that’s not your licence, I’m going to defer that until after our discussion of software updates.


And on the subject software updates, you wrote::

How do applications patch themselves and still not run afoul of Gatekeeper, then? Are they just complete reinstalls?

Not necessarily.

Software updaters generally don’t patch a live app. Doing so is risky because, if something goes wrong, the app is left in an inconsistent state.

IMPORTANT Because software updaters don’t do that, Gatekeeper now works hard to prevent it. See the link to the WWDC talk about app bundle protection in Trusted Execution Resources.

Rather, an incremental updater will usually copy the app and apply the deltas to it. Once the new app is all done, it replaces the old app with the new. If you wanna be fancy, you can use renamex_np to do this atomically.

The key point here is that the updater doesn’t:

  • Modify an app that’s been run.

  • Or run an app until it’s fully formed, meaning that it’ll pass Gatekeeper.

Preparing the deltas can be a bit tricky. The basic idea is:

  1. Build, sign, notarise, and stapler the old.

  2. Build, sign, notarise, and stapler the new.

  3. Generate the delta from 1 to 2.

Finally, this incremental update mechanism could take twice the disk space but that’s not always true. If the volume supports cloning, the copy is actually a clone and thus only takes extra space as you modify things.

Share and Enjoy

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

I assume the mere act of copying the license (a file called devel.lic which is a small text file) is causing this.

Yes. Any change to the application bundle after signing could trigger this. I think there are ways to validate a signature while accepting a certain level of irregularity. But you don't have any control over how the OS on an end user's system is going to perform the validation. And those validation strategies will likely change as the OS evolves. The best course of action is to always assume the bundle never changes.

There are many alternative places to store licensing information. I recommend keeping it in the user's home directory so you don't have to ask the user for admin privileges to create it.

Why does it say it “does not seem to be an app”?

Most likely the bundle structure diverges too far from what is expected.

Remember that the purpose of all of this is to detect and block malware, which often uses novel ways to structure bundles and executables. The idea is to exploit edge cases that Apple never anticipated in order to confuse the system and gain more privileges. The more unusual your app is structured, the more likely you are to be impacted. This can happen during development or at an unexpected time later on.

This self-modification of the files in the Contents/Resources directory is a huge feature. We allow downloading of patches, which add features and fix bugs in the product. Is this going to be a problem, going forward?

Yes.

In theory, it's possible to modify parts of the application bundle and then supply an updated signature that matches the new bundle. But I'm not sure if anyone has ever managed to do that successfully. It's a high-risk, high-effort solution when storage and bandwidth are cheap.

Furthermore, you shouldn't be putting patches or executable code in the Resources directory. That is a special location for non-executable resource data. Using it for any kind of executable code is an excellent way to give yourself signing and/or notarization problems.

@Etresoft thanks for the reply.

How do applications patch themselves and still not run afoul of Gatekeeper, then? Are they just complete reinstalls?

Allegro Common Lisp is 35+ years old. We have a customers with a huge amount of history. We have been using our patching system for decades. Sounds like Apple is saying we must throw that away, because the patches do include binary executables (shared libraries), though they are code signed.

Moving everything that changes outside of Contents/Resources will mean either it's user specific, or we'll need to use /Library/Application Support/, which will require admin privs. Also, it's quite a lot of redesign for us to move everything out.

As for the "not an app" ... I mean, Apple notarized our app and didn't complain then. Seems like I should have gotten errors or warnings for that.

How do applications patch themselves and still not run afoul of Gatekeeper, then? Are they just complete reinstalls?

I can't speak for all applications. For me personally, life is complicated enough as it is.

You can definitely patch your application bundle, but one of the files you replace should be the signature. And that signature will have to be valid for the application bundle as it is on (the user's) disk.

Apple is saying we must throw that away, because the patches do include binary executables (shared libraries), though they are code signed.

I'm not making any statements about Apple. My position is that it's better to maintain a valid, signed, notarized bundle on disk. I'm confident that this will result in an easier, more trouble free experience for both the user and the developer. I've definitely seen developers use different methods. I just can't recommend those other approaches.

Patching the app bundle is not any kind of violation or anything. It's just tricky, prone to failure, and creates the potential for a significant problem later on, when you are least prepared to deal with it.

From what I've learned with my own app, I can tell you that Apple has, so far, been very conservative with respect to signature validation. Lots of developers find it challenging to get past Gatekeeper. But once they get installed, they can pretty much do whatever they want. It might not always be that way.

Moving everything that changes outside of Contents/Resources will mean either it's user specific, or we'll need to use /Library/Application Support/, which will require admin privs. Also, it's quite a lot of redesign for us to move everything out.

That's not what I said. I said that the Resources directory should be for non-executable data - images, text files, translations, data files, whatever. But it's not the place for executable binaries or libraries.

Ideally, nothing should be changing. But if you do want to make changes, you can change anything. It's just that the signature should match.

There is a version of "Library/Application Support" available in the user's home directory. If you need a place for dynamic source code, plug-ins, libraries, etc., that would be the place for it. I'm not familiar with your code. And I'm not familiar with apps that scatter components around. There might be better locations than "Application Support". There is also a "Frameworks" directory, for example.

Apple notarized our app and didn't complain then. Seems like I should have gotten errors or warnings for that.

Notarization is used for more than just apps. One mistake I see a lot in this forum is assuming that a successful result from the command-line notarization tools means that notarization was successful. It's not successful until it runs in a pristine environment like a VM.

There are two cases to consider here:

  • Self-modifying apps

  • Software updates

I’ll tackle each in turn.


Modifying your own app is not a supported technique. This is not a change in policy. It’s been this way since 1984. There are plenty of historical docs that are clear about this, and we also state it clearly in the Separate read-only and read/write content section of Embedding nonstandard code structures in a bundle.

The nature of unsupported things is that sometimes they work and sometimes they don’t. So the fact that something might have worked in some specific situations isn’t helpful. And even if you get something working today, you may run into problems in the future.

My goal is to help you move to a supported approach that should work now and in the future. I regularly help folks in your situation. For example, you might find this post and this post enlightening.

Regarding your licence setup, storing the licence outside of the app is the correct path forward. Exactly how you do that depends on a variety of tradeoffs. Personally I like this approach:

  • Store the licence in Application Support directory (.applicationSupportDirectory) in the user domain (.userDomainMask).

  • Look for the licence in the same directory in all domains (.allDomainsMask).

That way the site admin can move the file from their home directory to /Library/Application Support, or the much more obscure /Network/Library/Application Support, if they want the app to work for other users.

Note I’m referencing the FileManager.urls(for:in:) method here.

Regarding stuff that’s not your licence, I’m going to defer that until after our discussion of software updates.


And on the subject software updates, you wrote::

How do applications patch themselves and still not run afoul of Gatekeeper, then? Are they just complete reinstalls?

Not necessarily.

Software updaters generally don’t patch a live app. Doing so is risky because, if something goes wrong, the app is left in an inconsistent state.

IMPORTANT Because software updaters don’t do that, Gatekeeper now works hard to prevent it. See the link to the WWDC talk about app bundle protection in Trusted Execution Resources.

Rather, an incremental updater will usually copy the app and apply the deltas to it. Once the new app is all done, it replaces the old app with the new. If you wanna be fancy, you can use renamex_np to do this atomically.

The key point here is that the updater doesn’t:

  • Modify an app that’s been run.

  • Or run an app until it’s fully formed, meaning that it’ll pass Gatekeeper.

Preparing the deltas can be a bit tricky. The basic idea is:

  1. Build, sign, notarise, and stapler the old.

  2. Build, sign, notarise, and stapler the new.

  3. Generate the delta from 1 to 2.

Finally, this incremental update mechanism could take twice the disk space but that’s not always true. If the volume supports cloning, the copy is actually a clone and thus only takes extra space as you modify things.

Share and Enjoy

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

In theory, it's possible to modify parts of the application bundle and then supply an updated signature that matches the new bundle. But I'm not sure if anyone has ever managed to do that successfully. It's a high-risk, high-effort solution when storage and bandwidth are cheap.

Can you explain more about this? It seems we might have to go that route, otherwise, it's the redesign of a 40 year old application.

Can you explain more about this? It seems we might have to go that route, otherwise, it's the redesign of a 40 year old application.

I was talking about the same process that Quinn described. I've never done it. Perhaps you should use a DTS ticket for more details.

Can you explain more about this?

That’s exactly what I outlined above. It’s standard operational procedure for apps that implement their own software update, but it’s not the same as patching a ‘live’ app.

IMPORTANT If you go down this path, make sure you read Updating Mac Software and follow its guidance. Failing to do so can yield some really mysterious errors O-:

Perhaps you should use a DTS ticket for more details.

These days DTS does most of its business here on the forums, so it’s unlikely we’d accept such a support request. And practically speaking, the person who would take it is me, so in this case the new policy has no downsides.

Share and Enjoy

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

“a sealed resource is missing or invalid” on an installed, notarized application
 
 
Q