We have an organization with multiple developers trying to develop apps. There are times where they want to find out if their app will pass notarization or not? We have a Developer ID Application certificate that we use to sign files right before production deployment and then for notarization approval. But this is not possible when developers are working in their sandboxes.
Providing each developer their own Developer ID Application certificate for distribution is both not feasible and perhaps not very secure.
Is there a way in which they can find out if their apps would pass the notarization tests without actually uploading to Apple?
Are there test methodologies that we can follow to write tests for problems presented by hardened runtime, missing entitlements and code/artifacts not in the right folder for the app?
Sure. I’ll come back to that below.
We have a Developer ID certificate in our CI CD right at the time of shipping. Perhaps we can move that up as well.
Right. That’d be the easiest option. Notarising every CI build isn’t a great idea, but notarising, say, your nightly builds is fine. That’ll head off any notarisation issues quickly, but it has another benefit: Once you have a notarised build, you can do a Gatekeeper evaluation using syspolicy_check
.
Remember that notarisation and Gatekeeper are different things, and it’s easy to create a build that passes notarisation but fails Gatekeeper.
I have a bunch of backstory to this in Resolving Trusted Execution Problems.
In terms of running checks on your code’s structure, all of the things you mentioned are possible. It’s really just a question of how much time you want to spend on this, and how much time that saves you when it stops a problem getting through. That’s the reasoning behind my earlier “what sort of problems?” question. If you understand what the common problems are, you can focus your tooling efforts on those.
And regarding the things you specifically called out…
write tests for problems presented by hardened runtime … ?
Testing that the hardened runtime is enabled is trivially easy. You can do that using codesign
or programmatically with the SecCode
API.
Testing that your code works with the hardened runtime is enabled is… well… just testing. You should absolutely have the hardened runtime enabled on all your builds — development and distribution — and thus all your day-to-day testing should be testing the hardened runtime case.
write tests for problems presented by … missing entitlements … ?
Again, there are two aspects to this. It’s feasible to do a static test to confirm that:
-
You’re only claiming entitlements on executables, not libraries.
-
All of the entitlements you claim are either unrestricted or authorised by a provisioning profile.
Again, codesign
and SecCode
work for this, along with using security
to work out the entitlements in your profile. And you’ll probably want to read TN3125 Inside Code Signing: Provisioning Profiles.
Testing for the runtime behaviour of this is, again, just testing.
The added challenge is that provisioning profiles are different for development and distribution, so you’ll want to run this test again in your CI. Or do the syspolicy_check
thing, because it should catch such issues.
write tests for problems presented by … code/artifacts not in the right folder for the app?
That is trickier. It’s relatively easy to programmatically enforce the rules in Placing Content in a Bundle, but most large apps bend those rules in one way or another. So it kinda depends on the current state of your product. If it’s already well structured, building a tool to enforce that structure should be pretty easy. If not, well it depends on how bad things are (-:
Regardless, there are some high-level things you can test:
-
Check that your built product has no extended attributes. Extended attributes, especially code-signing extended attributes [1], are a common indicator of structural problems.
-
Copy the built product to both case-sensitive and case-insensitive volumes, and then run this command:
% codesign --verify --deep --strict /path/to/your.app
If that fails, it’s likely that notarisation will fail.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
[1] See TN3126 Inside Code Signing: Hashes for an example of code-signing extended attributes.