launchApplicationAtURL - Launched Application "Verifying" since Catalina

We have a test tool our engineers use to launch various versions of our application during development and verification. Each daily build of our application is stored on a server. As well, each push of a change generates a new build of our application that is stored on a server. These are added to a database and the developer application accesses the server via REST to find the desired version to run, retrieves a server path and launches the application. This tool is valuable in finding pushes that introduced regressions.

The developer application (Runner) is using the launchApplicationAtURL:options:configuration:error: (deprecated I know) to launch the app. Prior to Catalina, this was working great. However, as of Catalina, this process is taking a VERY long time due to the app needing to be "verified". the app seems to need to be copied to the users machine and verified. It only occurs the first launch, but as most of the time the users are running new push or daily builds, it has made the app useless. With the new remote work environment it is even worse as VPN copy can take forever.

I have switched to using NSTask with a shell script to open the executable in the bundle. If I add the developer tool (Runner) to the Developer Tools in Privacy this seems to launch the application without the need for verification. However this just seems wrong. It also provides little feedback to know when the application is up and running, which makes my user experience poor. As well many of the systems we use this tool on for verification do not have Developer Tools installed. They are VMs.

Is there a way for me to use the launchApplicationAtURL:options:configuration:error: (or the new openApplicationAtURL:configuration:completionHandler:) to launch these versions of the application without the need for the lengthy verification process? Adding our application to the Developer Tools did not seem to help.
Answered by JMP Humphrey in 617317022
After the 1-1 Meeting with Apple Engineers, there appears to be a solution to this issue. I am going to post what I did in my application in hopes that if someone finds this it will help you too. I was a year trying to find a solution.

First, the application needs to be in the Developer Tools section of the Security and Privacy preferences (Privacy tab). If you have Xcode installed, that category will already be there and will most likely have Terminal included. You can either 1) ask your users to drag your app into that section or 2) use an API to add it there automatically and direct your users to simply add the access.


Apple Header and Framework Needed

There is a header you need to add to the code you are going to use for the rest of this post.

#import <ExecutionPolicy/ExecutionPolicy.h>

You will also have to link against the ExecutionPolicy Framework.

In that header, you will find another import for the EPDeveloperTool.h which will contain one of the important classes for this solution.

EPDeveloperTool - This class will allow you to add your application to the Developer Tools Privacy list.
EPExecutionPolicy - This class will allow you to add an exception for an application or task you want to spawn.

Adding Programatically

The key is in the - (void)requestDeveloperToolAccessWithCompletionHandler:(void (^)(BOOL granted))handler method on EPDeveloperTool. When you call this, your application will be added to the Developer Tool section. It will not have access granted, but it will be listed and it will avoid your users having to search for your application and drag it there. the completion handler receives a "granted" flag which will never be set (it seems) the first time. I had hoped this would present a OS level authorization dialog and let me know if the system granted the permission, but it does not seem it does. So it will return granted if your application was in the list and checked.

But you do not have to call this all the time. You can use the authorizationStatus property on EPDeveloperTool. This will tell you if your application has been granted permission. There are several options, but I chose to simply call the method to add it if the status is not EPDeveloperToolStatusAuthorized.

Once that is done, you need to alert your users and request they check the box. That will shut down your application and allow the to restart with the new permissions.

Here is a snippet of the code (ObjC):
Code Block ObjC
if (@available(macOS 10.15, *)) {
EPDeveloperTool *tool = [[EPDeveloperTool alloc] init];
if (tool.authorizationStatus != EPDeveloperToolStatusAuthorized) {
[tool requestDeveloperToolAccessWithCompletionHandler:^(BOOL granted) {
if (!granted) {
NSLog(@"APP MUST be granted developer access on 10.15+");
/* Present the dialog before exiting */
NSStoryboard *story = [NSStoryboard storyboardWithName:@"Main" bundle:nil];
DeveloperToolAlert *alert = [story instantiateControllerWithIdentifier:@"developerToolController"];
/* This controller will present Help, Open System Prefs or exit the application */
[[[NSApp mainWindow] contentViewController] presentViewControllerAsModalWindow:alert];
}
}];
}
}

Exceptions for the Launched Application

If you are launching another process via NSTask, then you do not need to do anything else. It seems that will work fine. So running a shell script will not trigger the verification issue once you have added your app to Developer Tools.

However, my application launches applications that it built via the launchApplicationAtURL method. This requires a little more work from another class in the EPExecutionPolicy header.

In there you will see another interface EPExecutionPolicy. that will allow you to set an exception for the application you are launching (assuming you are in the Developer Tools and have access).

Here you simply call the addPolicyExceptionForURL method to add the URL you are launching to the exceptions. If it works, it will return true. If not, you will get an error (if you provided one). That error might be helpful, or it might not. At first when my application was not in Developer Tools I got a weird POSIX error. So check that first.

Once the URL is added, you should not see the Verifying from the system.

Here is a code snippet for that. The url here is the application you are launching.
Code Block ObjC
if (@available(macOS 10.15, *)) {
NSError *err;
EPExecutionPolicy *policy = [[EPExecutionPolicy alloc] init];
bool added = [policy addPolicyExceptionForURL:url error:&err];
if (!added || err) {
NSException* myException = [NSException exceptionWithName:@"DeveloperToolPermissionException"
  reason:@"Security Exception: Unable to add the URL to the exception policy."
userInfo:@{ @"executionUrl": url }];
@throw myException;
}
}


Misc Notes

If you are building and running your app in Xcode, you will need to add it to the Developer Tools over and over. Anytime the app "changes" the system will not accept the previous permission. Your users will not see this unless they get a new version of your app.

I was able to call the interface to add the app to the Developer Tools on machines that did not have Xcode installed.
I had a 1-1 with an engineer today. He suggested this question is best handled by the Security Team. Specifically Trusted Execution.

[Security]
Accepted Answer
After the 1-1 Meeting with Apple Engineers, there appears to be a solution to this issue. I am going to post what I did in my application in hopes that if someone finds this it will help you too. I was a year trying to find a solution.

First, the application needs to be in the Developer Tools section of the Security and Privacy preferences (Privacy tab). If you have Xcode installed, that category will already be there and will most likely have Terminal included. You can either 1) ask your users to drag your app into that section or 2) use an API to add it there automatically and direct your users to simply add the access.


Apple Header and Framework Needed

There is a header you need to add to the code you are going to use for the rest of this post.

#import <ExecutionPolicy/ExecutionPolicy.h>

You will also have to link against the ExecutionPolicy Framework.

In that header, you will find another import for the EPDeveloperTool.h which will contain one of the important classes for this solution.

EPDeveloperTool - This class will allow you to add your application to the Developer Tools Privacy list.
EPExecutionPolicy - This class will allow you to add an exception for an application or task you want to spawn.

Adding Programatically

The key is in the - (void)requestDeveloperToolAccessWithCompletionHandler:(void (^)(BOOL granted))handler method on EPDeveloperTool. When you call this, your application will be added to the Developer Tool section. It will not have access granted, but it will be listed and it will avoid your users having to search for your application and drag it there. the completion handler receives a "granted" flag which will never be set (it seems) the first time. I had hoped this would present a OS level authorization dialog and let me know if the system granted the permission, but it does not seem it does. So it will return granted if your application was in the list and checked.

But you do not have to call this all the time. You can use the authorizationStatus property on EPDeveloperTool. This will tell you if your application has been granted permission. There are several options, but I chose to simply call the method to add it if the status is not EPDeveloperToolStatusAuthorized.

Once that is done, you need to alert your users and request they check the box. That will shut down your application and allow the to restart with the new permissions.

Here is a snippet of the code (ObjC):
Code Block ObjC
if (@available(macOS 10.15, *)) {
EPDeveloperTool *tool = [[EPDeveloperTool alloc] init];
if (tool.authorizationStatus != EPDeveloperToolStatusAuthorized) {
[tool requestDeveloperToolAccessWithCompletionHandler:^(BOOL granted) {
if (!granted) {
NSLog(@"APP MUST be granted developer access on 10.15+");
/* Present the dialog before exiting */
NSStoryboard *story = [NSStoryboard storyboardWithName:@"Main" bundle:nil];
DeveloperToolAlert *alert = [story instantiateControllerWithIdentifier:@"developerToolController"];
/* This controller will present Help, Open System Prefs or exit the application */
[[[NSApp mainWindow] contentViewController] presentViewControllerAsModalWindow:alert];
}
}];
}
}

Exceptions for the Launched Application

If you are launching another process via NSTask, then you do not need to do anything else. It seems that will work fine. So running a shell script will not trigger the verification issue once you have added your app to Developer Tools.

However, my application launches applications that it built via the launchApplicationAtURL method. This requires a little more work from another class in the EPExecutionPolicy header.

In there you will see another interface EPExecutionPolicy. that will allow you to set an exception for the application you are launching (assuming you are in the Developer Tools and have access).

Here you simply call the addPolicyExceptionForURL method to add the URL you are launching to the exceptions. If it works, it will return true. If not, you will get an error (if you provided one). That error might be helpful, or it might not. At first when my application was not in Developer Tools I got a weird POSIX error. So check that first.

Once the URL is added, you should not see the Verifying from the system.

Here is a code snippet for that. The url here is the application you are launching.
Code Block ObjC
if (@available(macOS 10.15, *)) {
NSError *err;
EPExecutionPolicy *policy = [[EPExecutionPolicy alloc] init];
bool added = [policy addPolicyExceptionForURL:url error:&err];
if (!added || err) {
NSException* myException = [NSException exceptionWithName:@"DeveloperToolPermissionException"
  reason:@"Security Exception: Unable to add the URL to the exception policy."
userInfo:@{ @"executionUrl": url }];
@throw myException;
}
}


Misc Notes

If you are building and running your app in Xcode, you will need to add it to the Developer Tools over and over. Anytime the app "changes" the system will not accept the previous permission. Your users will not see this unless they get a new version of your app.

I was able to call the interface to add the app to the Developer Tools on machines that did not have Xcode installed.
launchApplicationAtURL - Launched Application "Verifying" since Catalina
 
 
Q