"because it is not SIP-protected" - macOS Mojave 中的 Apple 事件错误
"because it is not SIP-protected" - Apple event error in macOS Mojave
我在 macOS 中有一个适当的沙盒应用程序,Objective-C 通过 Apple Events(例如 Adobe InDesign)与第三方应用程序通信。
在 macOS Mojave 中,一切都中断了,因为 Apple 的新 SIP (https://developer.apple.com/library/archive/documentation/Security/Conceptual/System_Integrity_Protection_Guide/Introduction/Introduction.html) 不允许通信。
我还没有找到任何解决方案。任何帮助表示赞赏。
这是错误信息:
skipped scripting addition "/Library/ScriptingAdditions/Adobe Unit Types.osax" because it is not SIP-protected.
这是对问题的一个很好的总结:
https://www.felix-schwarz.org/blog/2018/06/apple-event-sandboxing-in-macos-mojave
Apple 仍需努力解决这个问题,它并不完美,对用户不友好,也没有很好的文档记录。但这是一个可行的解决方案。
从 OSX 10.14 (Mojave) 开始,如果用户允许您的应用程序与其他人通信,您必须询问 OSX 的系统完整性保护 (SIP)。
要使其正常工作,您需要在应用的 .plist 文件中添加一个条目:
key: NSAppleEventsUsageDescription
value: [Some description why you need to use AppleEvents]
注意:您不能为更多应用程序使用更多条目。它的一个条目。所以明智地选择你的描述。此描述将显示在 Apple 的对话框中,要求用户接受。
如果您像我一样有 XPC 服务,请将它放在您的主应用程序中,而不是服务中。
现在在您的应用程序中 - 在使用 Apple 事件之前 - 检查当前状态(是否允许 AppleEvents)。我写了这个方法:
- (BOOL)checkSIPforAppIdentifier:(NSString*)identifier {
// First available from 10.14 Mojave
if (@available(macOS 10.14, *)) {
OSStatus status;
NSAppleEventDescriptor *targetAppEventDescriptor;
targetAppEventDescriptor = [NSAppleEventDescriptor descriptorWithBundleIdentifier:identifier];
status = AEDeterminePermissionToAutomateTarget(targetAppEventDescriptor.aeDesc, typeWildCard, typeWildCard, true);
switch (status) {
case -600: //procNotFound
NSLog(@"Not running app with id '%@'",identifier);
break;
case 0: // noErr
NSLog(@"SIP check successfull for app with id '%@'",identifier);
break;
case -1744: // errAEEventWouldRequireUserConsent
// This only appears if you send false for askUserIfNeeded
NSLog(@"User consent required for app with id '%@'",identifier);
break;
case -1743: //errAEEventNotPermitted
NSLog(@"User didn't allow usage for app with id '%@'",identifier);
// Here you should present a dialog with a tutorial on how to activate it manually
// This can be something like
// Go to system preferences > security > privacy
// Choose automation and active [APPNAME] for [APPNAME]
return NO;
default:
break;
}
}
return YES;
}
这样称呼它:
[self checkSIPforAppIdentifier:@"com.apple.mail"];
您可以在 AppleEvents.h 中找到详细信息 - 这是所用方法的副本:
AEDeterminePermissionToAutomateTarget()
Discussion: Determines whether the current application is able to send
an AppleEvent with the given eventClass and eventID to the application
described as targetAddressDesc.
Mac OS 10.14 and later impose additional requirements on applications
when they send AppleEvents to other applications in order to insure
that users are aware of and consent to allowing such control or
information exchange. Generally this involves the user being prompted
in a secure fashion the first time an application attempts to send an
AppleEvent to another application.
If the user consents then this application can send events to the
target. If the user does not consent then any future attempts to send
AppleEvents will result in a failure with errAEEventNotPermitted being
returned. Certain AppleEvents are allowed to be sent without prompting
the user. Pass typeWildCard for the eventClass and eventID to
determine if every event is allowed to be sent from this application
to the target.
Applications can determine, without sending an AppleEvent to a target
application, whether they are allowed to send AppleEvents to the
target with this function. If askUserIfNeeded is true, and this
application does not yet have permission to send AppleEvents to the
target, then the user will be asked if permission can be granted; if
askUserIfNeeded is false and permission has not been granted, then
errAEEventWouldRequireUserConsent will be returned.
The target AEAddressDesc must refer to an already running application.
Results
If the current application is permitted to send the given AppleEvent
to the target, then noErr will be returned. If the current
application is not permitted to send the event, errAEEventNotPermitted
will be returned. If the target application is not running, then
procNotFound will be returned. If askUserIfNeeded is false, and this
application is not yet permitted to send AppleEvents to the target,
then errAEEventWouldRequireUserConsent will be returned.
Mac OS X threading:
Thread safe since version 10.14. Do not call this function on your
main thread because it may take arbitrarily long to return if the user
needs to be prompted for consent.
Parameters:
target:
A pointer to an address descriptor. Before calling
AEDeterminePermissionToAutomateTarget, you set the descriptor to
identify the target application for the Apple event. The target
address descriptor must refer to a running application. If the target
application is on another machine, then Remote AppleEvents must be
enabled on that machine for the user.
theAEEventClass: The event class of the Apple event to determine
permission for.
theAEEventID: The event ID of the Apple event to determine permission
for.
askUserIfNeeded: a Boolean; if true, and if this application does not
yet have permission to send events to the target application, then
prompt the user to obtain permission. If false, do not prompt the
user.
结论:
如前所述,它并不完美。
- 目标应用必须 运行 - 否则它将 return -600
- 一旦被拒绝,用户就只能手动激活了,感觉不太顺畅。
- 它是线程安全的,所以你不应该在主线程中调用它(手动激活的对话框除外)
让我们弄清楚 SIP(系统完整性保护)和 TCC(透明度、同意、控制:基本上,所有 "this app wants to access this service" 对话,在安全和隐私中管理)之间的区别。 Mojave 包括对两者的更改,它们对 Apple 事件和 AppleScript 的影响在 Mojave release notes.
中介绍
首先,SIP:有关 Adobe 单元类型未加载的日志消息确实是因为 SIP 发生了变化 — 强化运行时意味着不再允许全局安装的第三方脚本添加 — 但除非您使用的是 Adobe 的单位强制转换,不会影响您的应用。
接下来,TCC:如果您的应用程序在 10.13 中运行时发送 Apple 事件失败,那么您的问题很可能与 TCC 有关。要注意的错误是 errAEEventNotPermitted
, -1743:这意味着 TCC 阻止了一个事件。 (-600 和 -10004 表示沙箱授权有问题。)如果您没有首先看到 TCC 对话框,您很可能在 Info.plist 中缺少 NSAppleEventsUsageDescription
条目:在针对10.14 SDK.
一般情况下,您可以简单地正常发送事件并处理任何错误。但是,如果您希望提供额外的错误 UI——例如,用户拒绝 Apple 事件的预先警告——那么请使用 AEDeterminePermissionToAutomateTarget
.
我采纳了@Pat_Morita 的优秀答案并使其变得快速:
func checkSIPforAppIdentifier(_ sipIdentifier:String) -> Dictionary<String, Any> {
var dictSIPResponse = [String:Any]()
var targetAppEventDescriptor = NSAppleEventDescriptor(bundleIdentifier: sipIdentifier)
var status = AEDeterminePermissionToAutomateTarget(targetAppEventDescriptor.aeDesc, typeWildCard, typeWildCard, true);
switch (status) {
case -600: //procNotFound
dictSIPResponse["isSipEnabled"] = false
dictSIPResponse["sipMessage"] = "Not running app with id \(sipIdentifier)"
break;
case 0: // noErr
dictSIPResponse["isSipEnabled"] = true
dictSIPResponse["sipMessage"] = "SIP check successfull for app with id \(sipIdentifier)"
break;
case -1744: // errAEEventWouldRequireUserConsent
// This only appears if you send false for askUserIfNeeded
dictSIPResponse["isSipEnabled"] = false
dictSIPResponse["sipMessage"] = "User consent required for app with id \(sipIdentifier)"
break;
case -1743: //errAEEventNotPermitted
dictSIPResponse["isSipEnabled"] = false
dictSIPResponse["sipMessage"] = "User didn't allow usage for app with id \(sipIdentifier)"
// Here you should present a dialog with a tutorial on how to activate it manually
// This can be something like
// Go to system preferences > security > privacy
// Choose automation and active [APPNAME] for [APPNAME]
default:
break;
}
return dictSIPResponse
}
我在 macOS 中有一个适当的沙盒应用程序,Objective-C 通过 Apple Events(例如 Adobe InDesign)与第三方应用程序通信。
在 macOS Mojave 中,一切都中断了,因为 Apple 的新 SIP (https://developer.apple.com/library/archive/documentation/Security/Conceptual/System_Integrity_Protection_Guide/Introduction/Introduction.html) 不允许通信。
我还没有找到任何解决方案。任何帮助表示赞赏。
这是错误信息:
skipped scripting addition "/Library/ScriptingAdditions/Adobe Unit Types.osax" because it is not SIP-protected.
这是对问题的一个很好的总结: https://www.felix-schwarz.org/blog/2018/06/apple-event-sandboxing-in-macos-mojave
Apple 仍需努力解决这个问题,它并不完美,对用户不友好,也没有很好的文档记录。但这是一个可行的解决方案。
从 OSX 10.14 (Mojave) 开始,如果用户允许您的应用程序与其他人通信,您必须询问 OSX 的系统完整性保护 (SIP)。
要使其正常工作,您需要在应用的 .plist 文件中添加一个条目:
key: NSAppleEventsUsageDescription
value: [Some description why you need to use AppleEvents]
注意:您不能为更多应用程序使用更多条目。它的一个条目。所以明智地选择你的描述。此描述将显示在 Apple 的对话框中,要求用户接受。
如果您像我一样有 XPC 服务,请将它放在您的主应用程序中,而不是服务中。
现在在您的应用程序中 - 在使用 Apple 事件之前 - 检查当前状态(是否允许 AppleEvents)。我写了这个方法:
- (BOOL)checkSIPforAppIdentifier:(NSString*)identifier {
// First available from 10.14 Mojave
if (@available(macOS 10.14, *)) {
OSStatus status;
NSAppleEventDescriptor *targetAppEventDescriptor;
targetAppEventDescriptor = [NSAppleEventDescriptor descriptorWithBundleIdentifier:identifier];
status = AEDeterminePermissionToAutomateTarget(targetAppEventDescriptor.aeDesc, typeWildCard, typeWildCard, true);
switch (status) {
case -600: //procNotFound
NSLog(@"Not running app with id '%@'",identifier);
break;
case 0: // noErr
NSLog(@"SIP check successfull for app with id '%@'",identifier);
break;
case -1744: // errAEEventWouldRequireUserConsent
// This only appears if you send false for askUserIfNeeded
NSLog(@"User consent required for app with id '%@'",identifier);
break;
case -1743: //errAEEventNotPermitted
NSLog(@"User didn't allow usage for app with id '%@'",identifier);
// Here you should present a dialog with a tutorial on how to activate it manually
// This can be something like
// Go to system preferences > security > privacy
// Choose automation and active [APPNAME] for [APPNAME]
return NO;
default:
break;
}
}
return YES;
}
这样称呼它:
[self checkSIPforAppIdentifier:@"com.apple.mail"];
您可以在 AppleEvents.h 中找到详细信息 - 这是所用方法的副本:
AEDeterminePermissionToAutomateTarget()
Discussion: Determines whether the current application is able to send an AppleEvent with the given eventClass and eventID to the application described as targetAddressDesc.
Mac OS 10.14 and later impose additional requirements on applications when they send AppleEvents to other applications in order to insure that users are aware of and consent to allowing such control or information exchange. Generally this involves the user being prompted in a secure fashion the first time an application attempts to send an AppleEvent to another application.
If the user consents then this application can send events to the target. If the user does not consent then any future attempts to send AppleEvents will result in a failure with errAEEventNotPermitted being returned. Certain AppleEvents are allowed to be sent without prompting the user. Pass typeWildCard for the eventClass and eventID to determine if every event is allowed to be sent from this application to the target.
Applications can determine, without sending an AppleEvent to a target application, whether they are allowed to send AppleEvents to the target with this function. If askUserIfNeeded is true, and this application does not yet have permission to send AppleEvents to the target, then the user will be asked if permission can be granted; if askUserIfNeeded is false and permission has not been granted, then errAEEventWouldRequireUserConsent will be returned.
The target AEAddressDesc must refer to an already running application.
Results
If the current application is permitted to send the given AppleEvent to the target, then noErr will be returned. If the current application is not permitted to send the event, errAEEventNotPermitted will be returned. If the target application is not running, then procNotFound will be returned. If askUserIfNeeded is false, and this application is not yet permitted to send AppleEvents to the target, then errAEEventWouldRequireUserConsent will be returned.
Mac OS X threading:
Thread safe since version 10.14. Do not call this function on your main thread because it may take arbitrarily long to return if the user needs to be prompted for consent.
Parameters:
target:
A pointer to an address descriptor. Before calling AEDeterminePermissionToAutomateTarget, you set the descriptor to identify the target application for the Apple event. The target address descriptor must refer to a running application. If the target application is on another machine, then Remote AppleEvents must be enabled on that machine for the user.
theAEEventClass: The event class of the Apple event to determine permission for.
theAEEventID: The event ID of the Apple event to determine permission for.
askUserIfNeeded: a Boolean; if true, and if this application does not yet have permission to send events to the target application, then prompt the user to obtain permission. If false, do not prompt the user.
结论:
如前所述,它并不完美。
- 目标应用必须 运行 - 否则它将 return -600
- 一旦被拒绝,用户就只能手动激活了,感觉不太顺畅。
- 它是线程安全的,所以你不应该在主线程中调用它(手动激活的对话框除外)
让我们弄清楚 SIP(系统完整性保护)和 TCC(透明度、同意、控制:基本上,所有 "this app wants to access this service" 对话,在安全和隐私中管理)之间的区别。 Mojave 包括对两者的更改,它们对 Apple 事件和 AppleScript 的影响在 Mojave release notes.
中介绍首先,SIP:有关 Adobe 单元类型未加载的日志消息确实是因为 SIP 发生了变化 — 强化运行时意味着不再允许全局安装的第三方脚本添加 — 但除非您使用的是 Adobe 的单位强制转换,不会影响您的应用。
接下来,TCC:如果您的应用程序在 10.13 中运行时发送 Apple 事件失败,那么您的问题很可能与 TCC 有关。要注意的错误是 errAEEventNotPermitted
, -1743:这意味着 TCC 阻止了一个事件。 (-600 和 -10004 表示沙箱授权有问题。)如果您没有首先看到 TCC 对话框,您很可能在 Info.plist 中缺少 NSAppleEventsUsageDescription
条目:在针对10.14 SDK.
一般情况下,您可以简单地正常发送事件并处理任何错误。但是,如果您希望提供额外的错误 UI——例如,用户拒绝 Apple 事件的预先警告——那么请使用 AEDeterminePermissionToAutomateTarget
.
我采纳了@Pat_Morita 的优秀答案并使其变得快速:
func checkSIPforAppIdentifier(_ sipIdentifier:String) -> Dictionary<String, Any> {
var dictSIPResponse = [String:Any]()
var targetAppEventDescriptor = NSAppleEventDescriptor(bundleIdentifier: sipIdentifier)
var status = AEDeterminePermissionToAutomateTarget(targetAppEventDescriptor.aeDesc, typeWildCard, typeWildCard, true);
switch (status) {
case -600: //procNotFound
dictSIPResponse["isSipEnabled"] = false
dictSIPResponse["sipMessage"] = "Not running app with id \(sipIdentifier)"
break;
case 0: // noErr
dictSIPResponse["isSipEnabled"] = true
dictSIPResponse["sipMessage"] = "SIP check successfull for app with id \(sipIdentifier)"
break;
case -1744: // errAEEventWouldRequireUserConsent
// This only appears if you send false for askUserIfNeeded
dictSIPResponse["isSipEnabled"] = false
dictSIPResponse["sipMessage"] = "User consent required for app with id \(sipIdentifier)"
break;
case -1743: //errAEEventNotPermitted
dictSIPResponse["isSipEnabled"] = false
dictSIPResponse["sipMessage"] = "User didn't allow usage for app with id \(sipIdentifier)"
// Here you should present a dialog with a tutorial on how to activate it manually
// This can be something like
// Go to system preferences > security > privacy
// Choose automation and active [APPNAME] for [APPNAME]
default:
break;
}
return dictSIPResponse
}